mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-25 19:09:52 +03:00
1708998a11
In addition to saving 4 bytes for each instantiation, this change makes IP-related operations faster and clearer. https://tools.ietf.org/html/rfc3493.html#section-3.7
5905 lines
158 KiB
C
5905 lines
158 KiB
C
// SoftEther VPN Source Code - Developer Edition Master Branch
|
|
// Cedar Communication Module
|
|
|
|
|
|
// Proto_IKE.c
|
|
// IKE (ISAKMP) and ESP protocol stack
|
|
|
|
#include "Proto_IKE.h"
|
|
|
|
#include "Cedar.h"
|
|
#include "Connection.h"
|
|
#include "Logging.h"
|
|
#include "Proto_EtherIP.h"
|
|
#include "Proto_IPsec.h"
|
|
#include "Proto_L2TP.h"
|
|
#include "Server.h"
|
|
|
|
#include "Mayaqua/Memory.h"
|
|
#include "Mayaqua/Object.h"
|
|
#include "Mayaqua/Str.h"
|
|
#include "Mayaqua/Table.h"
|
|
#include "Mayaqua/TcpIp.h"
|
|
#include "Mayaqua/Tick64.h"
|
|
|
|
//#define RAW_DEBUG
|
|
|
|
// Processing of IKE received packet
|
|
void ProcIKEPacketRecv(IKE_SERVER *ike, UDPPACKET *p)
|
|
{
|
|
// Validate arguments
|
|
if (ike == NULL || p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p->Type == IKE_UDP_TYPE_ISAKMP)
|
|
{
|
|
// ISAKMP (IKE) packet
|
|
IKE_PACKET *header;
|
|
|
|
header = ParseIKEPacketHeader(p);
|
|
if (header == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Debug("InitiatorCookie: %I64u, ResponderCookie: %I64u\n", header->InitiatorCookie, header->ResponderCookie);
|
|
|
|
switch (header->ExchangeType)
|
|
{
|
|
case IKE_EXCHANGE_TYPE_MAIN: // Main mode
|
|
ProcIkeMainModePacketRecv(ike, p, header);
|
|
break;
|
|
|
|
case IKE_EXCHANGE_TYPE_AGGRESSIVE: // Aggressive mode
|
|
if (ike->Cedar->Server->DisableIPsecAggressiveMode == false)
|
|
{
|
|
ProcIkeAggressiveModePacketRecv(ike, p, header);
|
|
}
|
|
break;
|
|
|
|
case IKE_EXCHANGE_TYPE_QUICK: // Quick mode
|
|
ProcIkeQuickModePacketRecv(ike, p, header);
|
|
break;
|
|
|
|
case IKE_EXCHANGE_TYPE_INFORMATION: // Information exchange
|
|
ProcIkeInformationalExchangePacketRecv(ike, p, header);
|
|
break;
|
|
}
|
|
|
|
IkeFree(header);
|
|
}
|
|
else if (p->Type == IKE_UDP_TYPE_ESP)
|
|
{
|
|
// ESP packet
|
|
ProcIPsecEspPacketRecv(ike, p);
|
|
}
|
|
}
|
|
|
|
// Send a packet via IPsec
|
|
void IPsecSendPacketByIPsecSa(IKE_SERVER *ike, IPSECSA *sa, UCHAR *data, UINT data_size, UCHAR protocol_id)
|
|
{
|
|
bool is_tunnel_mode;
|
|
IKE_CLIENT *c;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
is_tunnel_mode = IsIPsecSaTunnelMode(sa);
|
|
|
|
c = sa->IkeClient;
|
|
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (is_tunnel_mode)
|
|
{
|
|
// Add an IPv4 / IPv6 header in the case of tunnel mode
|
|
if (IsZeroIP(&c->TunnelModeClientIP) == false || IsZeroIP(&c->TunnelModeServerIP) == false)
|
|
{
|
|
BUF *b;
|
|
UCHAR esp_proto_id;
|
|
|
|
b = NewBuf();
|
|
|
|
if (IsIP4(&c->TunnelModeClientIP))
|
|
{
|
|
// IPv4 header
|
|
IPV4_HEADER h;
|
|
|
|
h.VersionAndHeaderLength = 0;
|
|
h.TypeOfService = 0;
|
|
IPV4_SET_VERSION(&h, 4);
|
|
IPV4_SET_HEADER_LEN(&h, sizeof(IPV4_HEADER) / 4);
|
|
h.TotalLength = Endian16((USHORT)(data_size + sizeof(IPV4_HEADER)));
|
|
h.Identification = Endian16(c->TunnelSendIpId++);
|
|
h.FlagsAndFragmentOffset[0] = h.FlagsAndFragmentOffset[1] = 0;
|
|
h.TimeToLive = DEFAULT_IP_TTL;
|
|
h.Protocol = protocol_id;
|
|
h.SrcIP = IPToUINT(&c->TunnelModeServerIP);
|
|
h.DstIP = IPToUINT(&c->TunnelModeClientIP);
|
|
h.Checksum = 0;
|
|
h.Checksum = IpChecksum(&h, sizeof(IPV4_HEADER));
|
|
|
|
WriteBuf(b, &h, sizeof(IPV4_HEADER));
|
|
|
|
esp_proto_id = IKE_PROTOCOL_ID_IPV4;
|
|
}
|
|
else
|
|
{
|
|
// IPv6 header
|
|
IPV6_HEADER h;
|
|
|
|
Zero(&h, sizeof(h));
|
|
h.VersionAndTrafficClass1 = 0;
|
|
IPV6_SET_VERSION(&h, 6);
|
|
h.TrafficClass2AndFlowLabel1 = 0;
|
|
h.FlowLabel2 = h.FlowLabel3 = 0;
|
|
h.PayloadLength = Endian16(data_size);
|
|
h.NextHeader = protocol_id;
|
|
h.HopLimit = 64;
|
|
Copy(h.SrcAddress.Value, c->TunnelModeServerIP.address, sizeof(h.SrcAddress.Value));
|
|
Copy(h.DestAddress.Value, c->TunnelModeClientIP.address, sizeof(h.DestAddress.Value));
|
|
|
|
WriteBuf(b, &h, sizeof(IPV6_HEADER));
|
|
|
|
esp_proto_id = IKE_PROTOCOL_ID_IPV6;
|
|
}
|
|
|
|
WriteBuf(b, data, data_size);
|
|
|
|
IPsecSendPacketByIPsecSaInner(ike, sa, b->Buf, b->Size, esp_proto_id);
|
|
|
|
FreeBuf(b);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Send as it is in the case of transport mode
|
|
IPsecSendPacketByIPsecSaInner(ike, sa, data, data_size, protocol_id);
|
|
}
|
|
}
|
|
void IPsecSendPacketByIPsecSaInner(IKE_SERVER *ike, IPSECSA *sa, UCHAR *data, UINT data_size, UCHAR protocol_id)
|
|
{
|
|
UINT esp_size;
|
|
UINT encrypted_payload_size;
|
|
UCHAR *esp;
|
|
UINT i;
|
|
UINT size_of_padding;
|
|
IKE_CRYPTO_PARAM cp;
|
|
BUF *enc;
|
|
IKE_CLIENT *c;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c = sa->IkeClient;
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Calculate the payload size after encryption
|
|
encrypted_payload_size = data_size + 2;
|
|
if ((encrypted_payload_size % sa->TransformSetting.Crypto->BlockSize) != 0)
|
|
{
|
|
encrypted_payload_size = ((encrypted_payload_size / sa->TransformSetting.Crypto->BlockSize) + 1) * sa->TransformSetting.Crypto->BlockSize;
|
|
}
|
|
size_of_padding = encrypted_payload_size - data_size - 2;
|
|
|
|
// Calculate the size of the ESP packet
|
|
esp_size = sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + encrypted_payload_size + IKE_ESP_HASH_SIZE;
|
|
|
|
// Build the ESP packet
|
|
esp = Malloc(esp_size + IKE_MAX_HASH_SIZE);
|
|
|
|
// SPI
|
|
WRITE_UINT(esp, sa->Spi);
|
|
|
|
// Sequence number
|
|
sa->CurrentSeqNo++;
|
|
WRITE_UINT(esp + sizeof(UINT), sa->CurrentSeqNo);
|
|
|
|
// IV
|
|
Copy(esp + sizeof(UINT) * 2, sa->EspIv, sa->TransformSetting.Crypto->BlockSize);
|
|
|
|
// Payload data
|
|
Copy(esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize, data, data_size);
|
|
|
|
// Padding
|
|
for (i = 0;i < size_of_padding;i++)
|
|
{
|
|
esp[sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + data_size + i] = (UCHAR)(i + 1);
|
|
}
|
|
|
|
// Padding length
|
|
esp[sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + data_size + size_of_padding] = (UCHAR)size_of_padding;
|
|
|
|
// Next header number
|
|
esp[sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + data_size + size_of_padding + 1] = protocol_id;
|
|
|
|
// Encryption
|
|
Copy(cp.Iv, sa->EspIv, sa->TransformSetting.Crypto->BlockSize);
|
|
cp.Key = sa->CryptoKey;
|
|
|
|
enc = IkeEncrypt(esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize, encrypted_payload_size, &cp);
|
|
if (enc != NULL)
|
|
{
|
|
bool start_qm = false;
|
|
UINT server_port = c->ServerPort;
|
|
UINT client_port = c->ClientPort;
|
|
|
|
// Overwrite the encrypted result
|
|
Copy(esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize, enc->Buf, encrypted_payload_size);
|
|
|
|
FreeBuf(enc);
|
|
|
|
// Calculate the HMAC
|
|
IkeHMac(sa->TransformSetting.Hash,
|
|
esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + encrypted_payload_size,
|
|
sa->HashKey,
|
|
sa->TransformSetting.Hash->HashSize,
|
|
esp,
|
|
sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + encrypted_payload_size);
|
|
|
|
//*(UCHAR *)(esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + encrypted_payload_size) = 0xff;
|
|
|
|
if (sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TRANSPORT ||
|
|
sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TUNNEL)
|
|
{
|
|
server_port = client_port = IPSEC_PORT_IPSEC_ESP_RAW;
|
|
}
|
|
|
|
// Add the completed packet to the transmission list
|
|
IkeSendUdpPacket(ike, IKE_UDP_TYPE_ESP, &c->ServerIP, server_port, &c->ClientIP, client_port,
|
|
esp, esp_size);
|
|
|
|
// Feedback the IV
|
|
Copy(sa->EspIv, cp.NextIv, sa->TransformSetting.Crypto->BlockSize);
|
|
|
|
sa->TotalSize += esp_size;
|
|
|
|
if (sa->CurrentSeqNo >= 0xf0000000)
|
|
{
|
|
start_qm = true;
|
|
}
|
|
|
|
if (sa->TransformSetting.LifeKilobytes != 0)
|
|
{
|
|
UINT64 hard_size = (UINT64)sa->TransformSetting.LifeKilobytes * (UINT64)1000;
|
|
UINT64 soft_size = hard_size * (UINT64)2 / (UINT64)3;
|
|
|
|
if (sa->TotalSize >= soft_size)
|
|
{
|
|
start_qm = true;
|
|
}
|
|
}
|
|
|
|
if (start_qm)
|
|
{
|
|
if (sa->StartQM_FlagSet == false)
|
|
{
|
|
sa->StartQM_FlagSet = true;
|
|
c->StartQuickModeAsSoon = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Encryption failure
|
|
Free(esp);
|
|
}
|
|
}
|
|
void IPsecSendPacketByIkeClient(IKE_SERVER *ike, IKE_CLIENT *c, UCHAR *data, UINT data_size, UCHAR protocol_id)
|
|
{
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c->CurrentIpSecSaSend == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IPsecSendPacketByIPsecSa(ike, c->CurrentIpSecSaSend, data, data_size, protocol_id);
|
|
}
|
|
|
|
// Send an UDP packet via IPsec
|
|
void IPsecSendUdpPacket(IKE_SERVER *ike, IKE_CLIENT *c, UINT src_port, UINT dst_port, UCHAR *data, UINT data_size)
|
|
{
|
|
UCHAR *udp;
|
|
UINT udp_size;
|
|
UDP_HEADER *u;
|
|
UCHAR tmp1600[1600];
|
|
bool no_free = false;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Build an UDP packet
|
|
udp_size = sizeof(UDP_HEADER) + data_size;
|
|
|
|
if (udp_size > sizeof(tmp1600))
|
|
{
|
|
udp = Malloc(udp_size);
|
|
}
|
|
else
|
|
{
|
|
udp = tmp1600;
|
|
no_free = true;
|
|
}
|
|
|
|
// UDP header
|
|
u = (UDP_HEADER *)udp;
|
|
u->SrcPort = Endian16(src_port);
|
|
u->DstPort = Endian16(dst_port);
|
|
u->PacketLength = Endian16(udp_size);
|
|
u->Checksum = 0;
|
|
|
|
//Debug("IPsec UDP Send: %u -> %u %u\n", src_port, dst_port, data_size);
|
|
#ifdef RAW_DEBUG
|
|
IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, data, data_size);
|
|
#endif // RAW_DEBUG
|
|
|
|
// Payload
|
|
Copy(udp + sizeof(UDP_HEADER), data, data_size);
|
|
|
|
if (IsIP6(&c->ClientIP))
|
|
{
|
|
if (IsIPsecSaTunnelMode(c->CurrentIpSecSaSend) == false)
|
|
{
|
|
u->Checksum = CalcChecksumForIPv6((IPV6_ADDR *)c->TransportModeServerIP.address,
|
|
(IPV6_ADDR *)c->TransportModeClientIP.address,
|
|
IP_PROTO_UDP,
|
|
u,
|
|
udp_size, 0);
|
|
}
|
|
else
|
|
{
|
|
u->Checksum = CalcChecksumForIPv6((IPV6_ADDR *)c->TunnelModeServerIP.address,
|
|
(IPV6_ADDR *)c->TunnelModeClientIP.address,
|
|
IP_PROTO_UDP,
|
|
u,
|
|
udp_size, 0);
|
|
}
|
|
}
|
|
|
|
IPsecSendPacketByIkeClient(ike, c, udp, udp_size, IP_PROTO_UDP);
|
|
|
|
if (no_free == false)
|
|
{
|
|
Free(udp);
|
|
}
|
|
}
|
|
|
|
// Get whether the specified IPsec SA is in tunnel mode
|
|
bool IsIPsecSaTunnelMode(IPSECSA *sa)
|
|
{
|
|
// Validate arguments
|
|
if (sa == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TUNNEL ||
|
|
sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_NAT_TUNNEL_1 ||
|
|
sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_NAT_TUNNEL_2)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Reception process of ESP packet
|
|
void ProcIPsecEspPacketRecv(IKE_SERVER *ike, UDPPACKET *p)
|
|
{
|
|
UCHAR *src;
|
|
UINT src_size;
|
|
UINT spi;
|
|
UINT seq;
|
|
IPSECSA *ipsec_sa;
|
|
IKE_CLIENT *c;
|
|
UINT block_size;
|
|
UINT hash_size;
|
|
bool update_status = false;
|
|
UCHAR *iv;
|
|
UCHAR *hash;
|
|
UCHAR *encrypted_payload_data;
|
|
UINT size_of_payload_data;
|
|
IKE_CRYPTO_PARAM cp;
|
|
BUF *dec;
|
|
UCHAR calced_hash[IKE_MAX_HASH_SIZE];
|
|
bool is_tunnel_mode = false;
|
|
// Validate arguments
|
|
if (ike == NULL || p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
src = (UCHAR *)p->Data;
|
|
src_size = p->Size;
|
|
|
|
if (p->DestPort == IPSEC_PORT_IPSEC_ESP_RAW)
|
|
{
|
|
if (IsIP4(&p->DstIP))
|
|
{
|
|
// Skip the IP header when received in Raw mode (only in the case of IPv4)
|
|
UINT ip_header_size = GetIpHeaderSize(src, src_size);
|
|
|
|
src += ip_header_size;
|
|
src_size -= ip_header_size;
|
|
}
|
|
}
|
|
|
|
// Get the SPI
|
|
if (src_size < sizeof(UINT))
|
|
{
|
|
return;
|
|
}
|
|
|
|
spi = READ_UINT(src + 0);
|
|
if (spi == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Get the sequence number
|
|
if (src_size < (sizeof(UINT) * 2))
|
|
{
|
|
return;
|
|
}
|
|
seq = READ_UINT(src + sizeof(UINT));
|
|
|
|
// Search and retrieve the IPsec SA from SPI
|
|
ipsec_sa = SearchClientToServerIPsecSaBySpi(ike, spi);
|
|
if (ipsec_sa == NULL)
|
|
{
|
|
// Invalid SPI
|
|
UINT64 init_cookie = Rand64();
|
|
UINT64 resp_cookie = 0;
|
|
IKE_CLIENT *c = NULL;
|
|
IKE_CLIENT t;
|
|
|
|
|
|
Copy(&t.ClientIP, &p->SrcIP, sizeof(IP));
|
|
t.ClientPort = p->SrcPort;
|
|
Copy(&t.ServerIP, &p->DstIP, sizeof(IP));
|
|
t.ServerPort = p->DestPort;
|
|
t.CurrentIkeSa = NULL;
|
|
|
|
if (p->DestPort == IPSEC_PORT_IPSEC_ESP_RAW)
|
|
{
|
|
t.ClientPort = t.ServerPort = IPSEC_PORT_IPSEC_ISAKMP;
|
|
}
|
|
|
|
c = Search(ike->ClientList, &t);
|
|
|
|
if (c != NULL && c->CurrentIkeSa != NULL)
|
|
{
|
|
init_cookie = c->CurrentIkeSa->InitiatorCookie;
|
|
resp_cookie = c->CurrentIkeSa->ResponderCookie;
|
|
}
|
|
|
|
SendInformationalExchangePacketEx(ike, (c == NULL ? &t : c), IkeNewNoticeErrorInvalidSpiPayload(spi), false,
|
|
init_cookie, resp_cookie);
|
|
|
|
SendDeleteIPsecSaPacket(ike, (c == NULL ? &t : c), spi);
|
|
return;
|
|
}
|
|
|
|
is_tunnel_mode = IsIPsecSaTunnelMode(ipsec_sa);
|
|
|
|
c = ipsec_sa->IkeClient;
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
block_size = ipsec_sa->TransformSetting.Crypto->BlockSize;
|
|
hash_size = IKE_ESP_HASH_SIZE;
|
|
|
|
// Get the IV
|
|
if (src_size < (sizeof(UINT) * 2 + block_size + hash_size + block_size))
|
|
{
|
|
return;
|
|
}
|
|
iv = src + sizeof(UINT) * 2;
|
|
|
|
// Get the hash
|
|
hash = src + src_size - hash_size;
|
|
|
|
// Inspect the HMAC
|
|
IkeHMac(ipsec_sa->TransformSetting.Hash, calced_hash, ipsec_sa->HashKey,
|
|
ipsec_sa->TransformSetting.Hash->HashSize, src, src_size - hash_size);
|
|
|
|
if (Cmp(calced_hash, hash, hash_size) != 0)
|
|
{
|
|
//Debug("IPsec SA 0x%X: Invalid HMAC Value.\n", ipsec_sa->Spi);
|
|
return;
|
|
}
|
|
|
|
// Get the payload data
|
|
encrypted_payload_data = src + sizeof(UINT) * 2 + block_size;
|
|
size_of_payload_data = src_size - hash_size - block_size - sizeof(UINT) * 2;
|
|
if (size_of_payload_data == 0 || (size_of_payload_data % block_size) != 0)
|
|
{
|
|
// Payload data don't exist or is not a multiple of block size
|
|
return;
|
|
}
|
|
|
|
// Decrypt the payload data
|
|
cp.Key = ipsec_sa->CryptoKey;
|
|
Copy(&cp.Iv, iv, block_size);
|
|
|
|
dec = IkeDecrypt(encrypted_payload_data, size_of_payload_data, &cp);
|
|
if (dec != NULL)
|
|
{
|
|
UCHAR *dec_data = dec->Buf;
|
|
UINT dec_size = dec->Size;
|
|
UCHAR size_of_padding = dec_data[dec_size - 2];
|
|
UCHAR next_header = dec_data[dec_size - 1];
|
|
if ((dec_size - 2) >= size_of_padding)
|
|
{
|
|
UINT orig_size = dec_size - 2 - size_of_padding;
|
|
|
|
ipsec_sa->TotalSize += dec_size;
|
|
|
|
if (is_tunnel_mode)
|
|
{
|
|
// Tunnel Mode
|
|
if (next_header == IKE_PROTOCOL_ID_IPV4 || next_header == IKE_PROTOCOL_ID_IPV6)
|
|
{
|
|
// Check the contents by parsing the IPv4 / IPv6 header in the case of tunnel mode
|
|
BUF *b = NewBuf();
|
|
static UCHAR src_mac_dummy[6] = {0, 0, 0, 0, 0, 0, };
|
|
static UCHAR dst_mac_dummy[6] = {0, 0, 0, 0, 0, 0, };
|
|
USHORT tpid = Endian16(next_header == IKE_PROTOCOL_ID_IPV4 ? MAC_PROTO_IPV4 : MAC_PROTO_IPV6);
|
|
PKT *pkt;
|
|
|
|
WriteBuf(b, src_mac_dummy, sizeof(src_mac_dummy));
|
|
WriteBuf(b, dst_mac_dummy, sizeof(dst_mac_dummy));
|
|
WriteBuf(b, &tpid, sizeof(tpid));
|
|
|
|
WriteBuf(b, dec_data, dec_size);
|
|
|
|
// Parse
|
|
pkt = ParsePacket(b->Buf, b->Size);
|
|
|
|
#ifdef RAW_DEBUG
|
|
IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, b->Buf, b->Size);
|
|
#endif // RAW_DEBUG
|
|
|
|
if (pkt == NULL)
|
|
{
|
|
// Parsing failure
|
|
dec_data = NULL;
|
|
dec_size = 0;
|
|
}
|
|
else
|
|
{
|
|
// Parsing success
|
|
switch (pkt->TypeL3)
|
|
{
|
|
case L3_IPV4:
|
|
// Save the internal IP address information
|
|
UINTToIP(&c->TunnelModeServerIP, pkt->L3.IPv4Header->DstIP);
|
|
UINTToIP(&c->TunnelModeClientIP, pkt->L3.IPv4Header->SrcIP);
|
|
|
|
if (IPV4_GET_OFFSET(pkt->L3.IPv4Header) == 0)
|
|
{
|
|
if ((IPV4_GET_FLAGS(pkt->L3.IPv4Header) & 0x01) == 0)
|
|
{
|
|
if (pkt->L3.IPv4Header->Protocol == IPSEC_IP_PROTO_ETHERIP)
|
|
{
|
|
// EtherIP
|
|
if (ike->IPsec->Services.EtherIP_IPsec)
|
|
{
|
|
// An EtherIP packet has been received
|
|
ProcIPsecEtherIPPacketRecv(ike, c, pkt->IPv4PayloadData, pkt->IPv4PayloadSize, true);
|
|
}
|
|
}
|
|
else if (pkt->L3.IPv4Header->Protocol == IPSEC_IP_PROTO_L2TPV3)
|
|
{
|
|
// L2TPv3
|
|
if (ike->IPsec->Services.EtherIP_IPsec)
|
|
{
|
|
// A L2TPv3 packet has been received
|
|
ProcL2TPv3PacketRecv(ike, c, pkt->IPv4PayloadData, pkt->IPv4PayloadSize, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case L3_IPV6:
|
|
// Save the internal IP address information
|
|
SetIP6(&c->TunnelModeServerIP, pkt->IPv6HeaderPacketInfo.IPv6Header->DestAddress.Value);
|
|
SetIP6(&c->TunnelModeClientIP, pkt->IPv6HeaderPacketInfo.IPv6Header->SrcAddress.Value);
|
|
|
|
if (pkt->IPv6HeaderPacketInfo.IsFragment == false)
|
|
{
|
|
if (pkt->IPv6HeaderPacketInfo.FragmentHeader == NULL || (IPV6_GET_FLAGS(pkt->IPv6HeaderPacketInfo.FragmentHeader) & IPV6_FRAGMENT_HEADER_FLAG_MORE_FRAGMENTS) == 0)
|
|
{
|
|
if (pkt->IPv6HeaderPacketInfo.Protocol == IPSEC_IP_PROTO_ETHERIP)
|
|
{
|
|
// EtherIP
|
|
if (ike->IPsec->Services.EtherIP_IPsec)
|
|
{
|
|
// An EtherIP packet has been received
|
|
ProcIPsecEtherIPPacketRecv(ike, c, pkt->IPv6HeaderPacketInfo.Payload, pkt->IPv6HeaderPacketInfo.PayloadSize, true);
|
|
}
|
|
}
|
|
else if (pkt->IPv6HeaderPacketInfo.Protocol == IPSEC_IP_PROTO_L2TPV3)
|
|
{
|
|
// L2TPv3
|
|
if (ike->IPsec->Services.EtherIP_IPsec)
|
|
{
|
|
// A L2TPv3 packet has been received
|
|
ProcL2TPv3PacketRecv(ike, c, pkt->IPv6HeaderPacketInfo.Payload, pkt->IPv6HeaderPacketInfo.PayloadSize, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
FreePacket(pkt);
|
|
}
|
|
|
|
FreeBuf(b);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Transport mode
|
|
if (next_header == IP_PROTO_UDP)
|
|
{
|
|
if (ike->IPsec->Services.L2TP_IPsec || ike->IPsec->Services.EtherIP_IPsec)
|
|
{
|
|
// An UDP packet has been received
|
|
ProcIPsecUdpPacketRecv(ike, c, dec_data, dec_size);
|
|
}
|
|
}
|
|
else if (next_header == IPSEC_IP_PROTO_ETHERIP)
|
|
{
|
|
if (ike->IPsec->Services.EtherIP_IPsec)
|
|
{
|
|
// An EtherIP packet has been received
|
|
ProcIPsecEtherIPPacketRecv(ike, c, dec_data, dec_size, false);
|
|
}
|
|
}
|
|
else if (next_header == IPSEC_IP_PROTO_L2TPV3)
|
|
{
|
|
if (ike->IPsec->Services.EtherIP_IPsec)
|
|
{
|
|
// A L2TPv3 packet has been received
|
|
ProcL2TPv3PacketRecv(ike, c, dec_data, dec_size, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
update_status = true;
|
|
}
|
|
|
|
FreeBuf(dec);
|
|
}
|
|
|
|
if (update_status)
|
|
{
|
|
bool start_qm = false;
|
|
// Update the status of the client
|
|
c->CurrentIpSecSaRecv = ipsec_sa;
|
|
if (ipsec_sa->PairIPsecSa != NULL)
|
|
{
|
|
c->CurrentIpSecSaSend = ipsec_sa->PairIPsecSa;
|
|
|
|
if (p->DestPort == IPSEC_PORT_IPSEC_ESP_UDP)
|
|
{
|
|
IPSECSA *send_sa = c->CurrentIpSecSaSend;
|
|
if (send_sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TUNNEL)
|
|
{
|
|
send_sa->TransformSetting.CapsuleMode = IKE_P2_CAPSULE_NAT_TUNNEL_1;
|
|
}
|
|
else if (send_sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TRANSPORT)
|
|
{
|
|
send_sa->TransformSetting.CapsuleMode = IKE_P2_CAPSULE_NAT_TRANSPORT_1;
|
|
}
|
|
}
|
|
}
|
|
c->LastCommTick = ike->Now;
|
|
ipsec_sa->LastCommTick = ike->Now;
|
|
if (ipsec_sa->PairIPsecSa != NULL)
|
|
{
|
|
ipsec_sa->PairIPsecSa->LastCommTick = ike->Now;
|
|
}
|
|
|
|
SetIkeClientEndpoint(ike, c, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort);
|
|
|
|
if (seq >= 0xf0000000)
|
|
{
|
|
// Execute a QuickMode forcibly since sequence number is going to exhaust
|
|
start_qm = true;
|
|
}
|
|
|
|
if (ipsec_sa->TransformSetting.LifeKilobytes != 0)
|
|
{
|
|
UINT64 hard_size = (UINT64)ipsec_sa->TransformSetting.LifeKilobytes * (UINT64)1000;
|
|
UINT64 soft_size = hard_size * (UINT64)2 / (UINT64)3;
|
|
|
|
if (ipsec_sa->TotalSize >= soft_size)
|
|
{
|
|
// Execute a QuickMode forcibly because the capacity limit is going to exceed
|
|
start_qm = true;
|
|
}
|
|
}
|
|
|
|
if (start_qm)
|
|
{
|
|
if (ipsec_sa->StartQM_FlagSet == false)
|
|
{
|
|
c->StartQuickModeAsSoon = true;
|
|
ipsec_sa->StartQM_FlagSet = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Received the L2TPv3 packet via the IPsec tunnel
|
|
void ProcL2TPv3PacketRecv(IKE_SERVER *ike, IKE_CLIENT *c, UCHAR *data, UINT data_size, bool is_tunnel_mode)
|
|
{
|
|
UDPPACKET p;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c->IsL2TPOnIPsecTunnelMode = is_tunnel_mode;
|
|
|
|
IPsecIkeClientManageL2TPServer(ike, c);
|
|
|
|
// Pass the received packet to the L2TP server
|
|
p.Type = 0;
|
|
p.Data = data;
|
|
p.DestPort = IPSEC_PORT_L2TPV3_VIRTUAL;
|
|
p.Size = data_size;
|
|
|
|
if (is_tunnel_mode)
|
|
{
|
|
Copy(&p.DstIP, &c->TunnelModeServerIP, sizeof(IP));
|
|
Copy(&p.SrcIP, &c->TunnelModeClientIP, sizeof(IP));
|
|
}
|
|
else
|
|
{
|
|
Copy(&p.DstIP, &c->L2TPServerIP, sizeof(IP));
|
|
Copy(&p.SrcIP, &c->L2TPClientIP, sizeof(IP));
|
|
}
|
|
p.SrcPort = IPSEC_PORT_L2TPV3_VIRTUAL;
|
|
|
|
#ifdef RAW_DEBUG
|
|
IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, ((UCHAR *)p.Data) + 4, p.Size - 4);
|
|
#endif // RAW_DEBUG
|
|
|
|
ProcL2TPPacketRecv(c->L2TP, &p);
|
|
}
|
|
|
|
// An EtherIP packet has been received via an IPsec tunnel
|
|
void ProcIPsecEtherIPPacketRecv(IKE_SERVER *ike, IKE_CLIENT *c, UCHAR *data, UINT data_size, bool is_tunnel_mode)
|
|
{
|
|
BLOCK *b;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c->IsEtherIPOnIPsecTunnelMode = is_tunnel_mode;
|
|
|
|
IPsecIkeClientManageEtherIPServer(ike, c);
|
|
|
|
b = NewBlock(data, data_size, 0);
|
|
|
|
EtherIPProcRecvPackets(c->EtherIP, b);
|
|
|
|
Free(b);
|
|
}
|
|
|
|
// An UDP packet has been received via the IPsec tunnel
|
|
void ProcIPsecUdpPacketRecv(IKE_SERVER *ike, IKE_CLIENT *c, UCHAR *data, UINT data_size)
|
|
{
|
|
UDP_HEADER *u;
|
|
UINT payload_size;
|
|
UINT src_port, dst_port;
|
|
UINT packet_length;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (data_size <= sizeof(UDP_HEADER))
|
|
{
|
|
// There is no UDP header or the data is 0 bytes
|
|
return;
|
|
}
|
|
|
|
// UDP header
|
|
u = (UDP_HEADER *)data;
|
|
|
|
packet_length = Endian16(u->PacketLength);
|
|
|
|
if (packet_length <= sizeof(UDP_HEADER))
|
|
{
|
|
return;
|
|
}
|
|
|
|
payload_size = packet_length - sizeof(UDP_HEADER);
|
|
|
|
if (payload_size == 0)
|
|
{
|
|
// No data
|
|
return;
|
|
}
|
|
|
|
if (data_size < (sizeof(UDP_HEADER) + payload_size))
|
|
{
|
|
// Data is not followed
|
|
return;
|
|
}
|
|
|
|
src_port = Endian16(u->SrcPort);
|
|
dst_port = Endian16(u->DstPort);
|
|
|
|
if (dst_port == IPSEC_PORT_L2TP)
|
|
{
|
|
UDPPACKET p;
|
|
// A L2TP packet has been received
|
|
IPsecIkeClientManageL2TPServer(ike, c);
|
|
|
|
// Update Port number
|
|
c->L2TPClientPort = src_port;
|
|
|
|
// Pass the received packet to the L2TP server
|
|
p.Type = 0;
|
|
p.Data = data + sizeof(UDP_HEADER);
|
|
p.DestPort = IPSEC_PORT_L2TP;
|
|
Copy(&p.DstIP, &c->L2TPServerIP, sizeof(IP));
|
|
p.Size = payload_size;
|
|
Copy(&p.SrcIP, &c->L2TPClientIP, sizeof(IP));
|
|
p.SrcPort = IPSEC_PORT_L2TP;
|
|
|
|
ProcL2TPPacketRecv(c->L2TP, &p);
|
|
|
|
//Debug("IPsec UDP Recv: %u <= %u %u\n", dst_port, src_port, p.Size);
|
|
|
|
#ifdef RAW_DEBUG
|
|
IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, p.Data, p.Size);
|
|
#endif // RAW_DEBUG
|
|
}
|
|
}
|
|
|
|
// Send a raw packet for debugging
|
|
void IPsecIkeSendUdpForDebug(UINT dst_port, UINT dst_ip, void *data, UINT size)
|
|
{
|
|
SOCK *s = NewUDP(0);
|
|
IP d;
|
|
|
|
SetIP(&d, dst_ip, dst_ip, dst_ip, dst_ip);
|
|
|
|
SendTo(s, &d, dst_port, data, size);
|
|
|
|
ReleaseSock(s);
|
|
}
|
|
|
|
// L2TP packet transmission (via IPsec SA tunnel)
|
|
void IPsecIkeClientSendL2TPPackets(IKE_SERVER *ike, IKE_CLIENT *c, L2TP_SERVER *l2tp)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || l2tp == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(l2tp->SendPacketList);i++)
|
|
{
|
|
UDPPACKET *u = LIST_DATA(l2tp->SendPacketList, i);
|
|
|
|
if (u->SrcPort != IPSEC_PORT_L2TPV3_VIRTUAL)
|
|
{
|
|
// L2TP UDP packet transmission
|
|
IPsecSendUdpPacket(ike, c, IPSEC_PORT_L2TP, c->L2TPClientPort,
|
|
u->Data, u->Size);
|
|
}
|
|
else
|
|
{
|
|
// L2TPv3 special IP packet transmission
|
|
IPsecSendPacketByIkeClient(ike, c, u->Data, u->Size, IPSEC_IP_PROTO_L2TPV3);
|
|
|
|
#ifdef RAW_DEBUG
|
|
IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, ((UCHAR *)u->Data) + 4, u->Size - 4);
|
|
#endif // RAW_DEBUG
|
|
}
|
|
|
|
FreeUdpPacket(u);
|
|
}
|
|
|
|
DeleteAll(l2tp->SendPacketList);
|
|
}
|
|
|
|
// Manage the L2TP server that is associated with the IKE_CLIENT
|
|
void IPsecIkeClientManageL2TPServer(IKE_SERVER *ike, IKE_CLIENT *c)
|
|
{
|
|
L2TP_SERVER *l2tp;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c->L2TP == NULL)
|
|
{
|
|
UINT crypt_block_size = IKE_MAX_BLOCK_SIZE;
|
|
|
|
if (c->CurrentIpSecSaRecv != NULL)
|
|
{
|
|
crypt_block_size = c->CurrentIpSecSaRecv->TransformSetting.Crypto->BlockSize;
|
|
}
|
|
|
|
c->L2TP = NewL2TPServerEx(ike->Cedar, ike, IsIP6(&c->ClientIP), crypt_block_size);
|
|
c->L2TP->IkeClient = c;
|
|
|
|
Copy(&c->L2TPServerIP, &c->ServerIP, sizeof(IP));
|
|
Copy(&c->L2TPClientIP, &c->ClientIP, sizeof(IP));
|
|
|
|
if (c->CurrentIpSecSaRecv != NULL)
|
|
{
|
|
Format(c->L2TP->CryptName, sizeof(c->L2TP->CryptName),
|
|
"IPsec - %s (%u bits)",
|
|
c->CurrentIpSecSaRecv->TransformSetting.Crypto->Name,
|
|
c->CurrentIpSecSaRecv->TransformSetting.CryptoKeySize * 8);
|
|
}
|
|
|
|
Debug("IKE_CLIENT 0x%X: L2TP Server Started.\n", c);
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_L2TP_SERVER_STARTED");
|
|
}
|
|
|
|
l2tp = c->L2TP;
|
|
|
|
if (l2tp->Interrupts == NULL)
|
|
{
|
|
l2tp->Interrupts = ike->Interrupts;
|
|
}
|
|
|
|
if (l2tp->SockEvent == NULL)
|
|
{
|
|
SetL2TPServerSockEvent(l2tp, ike->SockEvent);
|
|
}
|
|
|
|
l2tp->Now = ike->Now;
|
|
}
|
|
|
|
// Manage the EtherIP server that is associated with the IKE_CLIENT
|
|
void IPsecIkeClientManageEtherIPServer(IKE_SERVER *ike, IKE_CLIENT *c)
|
|
{
|
|
ETHERIP_SERVER *s;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c->EtherIP == NULL)
|
|
{
|
|
char crypt_name[MAX_SIZE];
|
|
UINT crypt_block_size = IKE_MAX_BLOCK_SIZE;
|
|
|
|
Zero(crypt_name, sizeof(crypt_name));
|
|
|
|
if (c->CurrentIpSecSaRecv != NULL)
|
|
{
|
|
Format(crypt_name, sizeof(crypt_name),
|
|
"IPsec - %s (%u bits)",
|
|
c->CurrentIpSecSaRecv->TransformSetting.Crypto->Name,
|
|
c->CurrentIpSecSaRecv->TransformSetting.CryptoKeySize * 8);
|
|
|
|
crypt_block_size = c->CurrentIpSecSaRecv->TransformSetting.Crypto->BlockSize;
|
|
}
|
|
|
|
c->EtherIP = NewEtherIPServer(ike->Cedar, ike->IPsec, ike,
|
|
&c->ClientIP, c->ClientPort,
|
|
&c->ServerIP, c->ServerPort, crypt_name,
|
|
c->IsEtherIPOnIPsecTunnelMode, crypt_block_size, c->ClientId,
|
|
++ike->CurrentEtherId);
|
|
|
|
Debug("IKE_CLIENT 0x%X: EtherIP Server Started.\n", c);
|
|
|
|
IPsecLog(ike, c, NULL, NULL, NULL, "LI_ETHERIP_SERVER_STARTED", ike->CurrentEtherId);
|
|
}
|
|
else
|
|
{
|
|
StrCpy(c->EtherIP->ClientId, sizeof(c->EtherIP->ClientId), c->ClientId);
|
|
}
|
|
|
|
s = c->EtherIP;
|
|
|
|
if (s->Interrupts == NULL)
|
|
{
|
|
s->Interrupts = ike->Interrupts;
|
|
}
|
|
|
|
if (s->SockEvent == NULL)
|
|
{
|
|
SetEtherIPServerSockEvent(s, ike->SockEvent);
|
|
}
|
|
|
|
s->Now = ike->Now;
|
|
}
|
|
|
|
// EtherIP packet transmission (via IPsec SA tunnel)
|
|
void IPsecIkeClientSendEtherIPPackets(IKE_SERVER *ike, IKE_CLIENT *c, ETHERIP_SERVER *s)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || s == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(s->SendPacketList);i++)
|
|
{
|
|
BLOCK *b = LIST_DATA(s->SendPacketList, i);
|
|
|
|
// Packet transmission
|
|
IPsecSendPacketByIkeClient(ike, c, b->Buf, b->Size, IPSEC_IP_PROTO_ETHERIP);
|
|
|
|
FreeBlock(b);
|
|
}
|
|
|
|
DeleteAll(s->SendPacketList);
|
|
}
|
|
|
|
// Handle the deletion payload
|
|
void ProcDeletePayload(IKE_SERVER *ike, IKE_CLIENT *c, IKE_PACKET_DELETE_PAYLOAD *d)
|
|
{
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || d == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (d->ProtocolId == IKE_PROTOCOL_ID_IPSEC_ESP)
|
|
{
|
|
UINT i;
|
|
// Remove the IPsec SA
|
|
for (i = 0;i < LIST_NUM(d->SpiList);i++)
|
|
{
|
|
BUF *b = LIST_DATA(d->SpiList, i);
|
|
|
|
if (b->Size == 4)
|
|
{
|
|
UINT spi = READ_UINT(b->Buf);
|
|
MarkIPsecSaAsDeleted(ike, SearchIPsecSaBySpi(ike, c, spi));
|
|
}
|
|
}
|
|
}
|
|
else if (d->ProtocolId == IKE_PROTOCOL_ID_IKE)
|
|
{
|
|
UINT i;
|
|
// Remove the IKE SA
|
|
for (i = 0;i < LIST_NUM(d->SpiList);i++)
|
|
{
|
|
BUF *b = LIST_DATA(d->SpiList, i);
|
|
|
|
if (b->Size == 16)
|
|
{
|
|
UINT64 v1 = READ_UINT64(((UCHAR *)b->Buf) + 0);
|
|
UINT64 v2 = READ_UINT64(((UCHAR *)b->Buf) + 8);
|
|
|
|
IKE_SA *sa = FindIkeSaByResponderCookie(ike, v2);
|
|
|
|
if (sa != NULL && sa->IkeClient == c)
|
|
{
|
|
MarkIkeSaAsDeleted(ike, sa);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark the IKE_CLIENT for deletion
|
|
void MarkIkeClientAsDeleted(IKE_SERVER *ike, IKE_CLIENT *c)
|
|
{
|
|
char client_ip_str[MAX_SIZE];
|
|
char server_ip_str[MAX_SIZE];
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c->Deleting)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ike->StateHasChanged = true;
|
|
|
|
c->Deleting = true;
|
|
|
|
IPToStr(client_ip_str, sizeof(client_ip_str), &c->ClientIP);
|
|
IPToStr(server_ip_str, sizeof(server_ip_str), &c->ServerIP);
|
|
|
|
Debug("Deleting IKE_CLIENT: %p: %s:%u -> %s:%u\n", c, client_ip_str, c->ClientPort, server_ip_str, c->ServerPort);
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_DELETE_IKE_CLIENT");
|
|
}
|
|
|
|
// Mark the IKE SA for deletion
|
|
void MarkIkeSaAsDeleted(IKE_SERVER *ike, IKE_SA *sa)
|
|
{
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (sa->Deleting)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ike->StateHasChanged = true;
|
|
|
|
sa->Deleting = true;
|
|
|
|
Debug("IKE SA %I64u - %I64u has been marked as being deleted.\n", sa->InitiatorCookie, sa->ResponderCookie);
|
|
|
|
SendDeleteIkeSaPacket(ike, sa->IkeClient, sa->InitiatorCookie, sa->ResponderCookie);
|
|
|
|
IPsecLog(ike, NULL, sa, NULL, "LI_DELETE_IKE_SA");
|
|
}
|
|
|
|
// Mark the IPsec SA for deletion
|
|
void MarkIPsecSaAsDeleted(IKE_SERVER *ike, IPSECSA *sa)
|
|
{
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (sa->Deleting)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ike->StateHasChanged = true;
|
|
|
|
sa->Deleting = true;
|
|
|
|
Debug("IPsec SA 0x%X has been marked as being deleted.\n", sa->Spi);
|
|
|
|
SendDeleteIPsecSaPacket(ike, sa->IkeClient, sa->Spi);
|
|
|
|
IPsecLog(ike, NULL, NULL, sa, "LI_DELETE_IPSEC_SA");
|
|
}
|
|
|
|
// IPsec SA Deletion packet transmission process
|
|
void SendDeleteIPsecSaPacket(IKE_SERVER *ike, IKE_CLIENT *c, UINT spi)
|
|
{
|
|
IKE_PACKET_PAYLOAD *payload;
|
|
BUF *buf;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || spi == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
buf = NewBuf();
|
|
WriteBufInt(buf, spi);
|
|
|
|
payload = IkeNewDeletePayload(IKE_PROTOCOL_ID_IPSEC_ESP, NewListSingle(buf));
|
|
|
|
SendInformationalExchangePacket(ike, c, payload);
|
|
}
|
|
|
|
// IKE SA deletion packet transmission process
|
|
void SendDeleteIkeSaPacket(IKE_SERVER *ike, IKE_CLIENT *c, UINT64 init_cookie, UINT64 resp_cookie)
|
|
{
|
|
IKE_PACKET_PAYLOAD *payload;
|
|
BUF *buf;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
buf = NewBuf();
|
|
WriteBufInt64(buf, init_cookie);
|
|
WriteBufInt64(buf, resp_cookie);
|
|
|
|
payload = IkeNewDeletePayload(IKE_PROTOCOL_ID_IKE, NewListSingle(buf));
|
|
|
|
SendInformationalExchangePacket(ike, c, payload);
|
|
}
|
|
|
|
// Information exchange packet transmission process
|
|
void SendInformationalExchangePacket(IKE_SERVER *ike, IKE_CLIENT *c, IKE_PACKET_PAYLOAD *payload)
|
|
{
|
|
SendInformationalExchangePacketEx(ike, c, payload, false, 0, 0);
|
|
}
|
|
void SendInformationalExchangePacketEx(IKE_SERVER *ike, IKE_CLIENT *c, IKE_PACKET_PAYLOAD *payload, bool force_plain, UINT64 init_cookie, UINT64 resp_cookie)
|
|
{
|
|
IKE_SA *sa;
|
|
IKE_PACKET *ps;
|
|
LIST *payload_list;
|
|
UCHAR dummy_hash_data[IKE_MAX_HASH_SIZE];
|
|
IKE_PACKET_PAYLOAD *hash_payload;
|
|
BUF *ps_buf;
|
|
UINT after_hash_offset, after_hash_size;
|
|
BUF *ps_buf_after_hash;
|
|
BUF *tmp_buf;
|
|
UCHAR hash[IKE_MAX_HASH_SIZE];
|
|
IKE_CRYPTO_PARAM cp;
|
|
bool plain = false;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || payload == NULL)
|
|
{
|
|
IkeFreePayload(payload);
|
|
return;
|
|
}
|
|
|
|
sa = c->CurrentIkeSa;
|
|
if (sa == NULL)
|
|
{
|
|
plain = true;
|
|
}
|
|
|
|
if (force_plain)
|
|
{
|
|
plain = true;
|
|
}
|
|
|
|
if (plain && (init_cookie == 0 && resp_cookie == 0))
|
|
{
|
|
init_cookie = Rand64();
|
|
resp_cookie = 0;
|
|
}
|
|
|
|
payload_list = NewListFast(NULL);
|
|
|
|
Zero(dummy_hash_data, sizeof(dummy_hash_data));
|
|
|
|
// Hash payload
|
|
if (plain == false)
|
|
{
|
|
hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, dummy_hash_data, sa->HashSize);
|
|
Add(payload_list, hash_payload);
|
|
}
|
|
|
|
// Body
|
|
Add(payload_list, payload);
|
|
|
|
// Packet creation
|
|
ps = IkeNew((plain ? init_cookie : sa->InitiatorCookie), (plain ? resp_cookie : sa->ResponderCookie),
|
|
IKE_EXCHANGE_TYPE_INFORMATION, false, false, false,
|
|
GenerateNewMessageId(ike), payload_list);
|
|
|
|
if (plain == false)
|
|
{
|
|
// Build a temporary packet
|
|
ps_buf = IkeBuild(ps, NULL);
|
|
|
|
// Get the payload after the hash part
|
|
after_hash_offset = sizeof(IKE_HEADER) + hash_payload->BitArray->Size + sizeof(IKE_COMMON_HEADER);
|
|
after_hash_size = ((ps_buf->Size > after_hash_offset) ? (ps_buf->Size - after_hash_offset) : 0);
|
|
|
|
ps_buf_after_hash = MemToBuf(((UCHAR *)ps_buf->Buf) + after_hash_offset, after_hash_size);
|
|
FreeBuf(ps_buf);
|
|
|
|
// Calculate the hash
|
|
tmp_buf = NewBuf();
|
|
WriteBufInt(tmp_buf, ps->MessageId);
|
|
WriteBufBuf(tmp_buf, ps_buf_after_hash);
|
|
IkeHMac(sa->TransformSetting.Hash, hash, sa->SKEYID_a, sa->HashSize, tmp_buf->Buf, tmp_buf->Size);
|
|
FreeBuf(tmp_buf);
|
|
|
|
// Overwrite the hash
|
|
Copy(hash_payload->Payload.Hash.Data->Buf, hash, sa->HashSize);
|
|
|
|
ps->FlagEncrypted = true;
|
|
FreeBuf(ps_buf_after_hash);
|
|
}
|
|
|
|
// Packet reply
|
|
Zero(&cp, sizeof(cp));
|
|
|
|
if (plain == false)
|
|
{
|
|
cp.Key = sa->CryptoKey;
|
|
IkeCalcPhase2InitialIv(cp.Iv, sa, ps->MessageId);
|
|
}
|
|
|
|
ps_buf = IkeBuild(ps, &cp);
|
|
|
|
IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &c->ServerIP, c->ServerPort,
|
|
&c->ClientIP, c->ClientPort,
|
|
ps_buf->Buf, ps_buf->Size);
|
|
|
|
#ifdef RAW_DEBUG
|
|
IkeDebugUdpSendRawPacket(ps);
|
|
#endif // RAW_DEBUG
|
|
|
|
Free(ps_buf);
|
|
|
|
IkeFree(ps);
|
|
}
|
|
|
|
// Information exchange packet reception process
|
|
void ProcIkeInformationalExchangePacketRecv(IKE_SERVER *ike, UDPPACKET *p, IKE_PACKET *header)
|
|
{
|
|
IKE_CLIENT *c;
|
|
IKE_SA *ike_sa;
|
|
// Validate arguments
|
|
if (ike == NULL || p == NULL || header == NULL || header->InitiatorCookie == 0 || header->ResponderCookie == 0
|
|
|| header->MessageId == 0 || header->FlagEncrypted == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c = SearchOrCreateNewIkeClientForIkePacket(ike, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort, header);
|
|
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ike_sa = FindIkeSaByResponderCookieAndClient(ike, header->ResponderCookie, c);
|
|
|
|
if (ike_sa != NULL && ike_sa->Established)
|
|
{
|
|
IKE_PACKET *pr;
|
|
IKE_CRYPTO_PARAM cp;
|
|
|
|
// Packet decoding
|
|
Zero(&cp, sizeof(cp));
|
|
cp.Key = ike_sa->CryptoKey;
|
|
IkeCalcPhase2InitialIv(cp.Iv, ike_sa, header->MessageId);
|
|
|
|
pr = IkeParse(p->Data, p->Size, &cp);
|
|
#ifdef RAW_DEBUG
|
|
IkeDebugUdpSendRawPacket(pr);
|
|
#endif // RAW_DEBUG
|
|
if (pr != NULL)
|
|
{
|
|
// Get the hash payload
|
|
IKE_PACKET_PAYLOAD *hash_payload;
|
|
|
|
hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
|
|
if (hash_payload != NULL)
|
|
{
|
|
// Get the payload after the hash
|
|
UINT header_and_hash_size = sizeof(IKE_COMMON_HEADER) + hash_payload->BitArray->Size;
|
|
void *after_hash_data = ((UCHAR *)pr->DecryptedPayload->Buf) + header_and_hash_size;
|
|
if (pr->DecryptedPayload->Size > header_and_hash_size)
|
|
{
|
|
UINT after_hash_size = pr->DecryptedPayload->Size - header_and_hash_size;
|
|
UCHAR hash1[IKE_MAX_HASH_SIZE];
|
|
BUF *hash1_buf;
|
|
|
|
hash1_buf = NewBuf();
|
|
WriteBufInt(hash1_buf, header->MessageId);
|
|
WriteBuf(hash1_buf, after_hash_data, after_hash_size);
|
|
|
|
IkeHMac(ike_sa->TransformSetting.Hash, hash1, ike_sa->SKEYID_a, ike_sa->HashSize,
|
|
hash1_buf->Buf, hash1_buf->Size);
|
|
|
|
// Compare the hash value
|
|
if (IkeCompareHash(hash_payload, hash1, ike_sa->HashSize))
|
|
{
|
|
UINT i, num;
|
|
// Handle the deletion payload
|
|
num = IkeGetPayloadNum(pr->PayloadList, IKE_PAYLOAD_DELETE);
|
|
for (i = 0;i < num;i++)
|
|
{
|
|
IKE_PACKET_PAYLOAD *payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_DELETE, i);
|
|
IKE_PACKET_DELETE_PAYLOAD *del = &payload->Payload.Delete;
|
|
|
|
ProcDeletePayload(ike, c, del);
|
|
}
|
|
num = IkeGetPayloadNum(pr->PayloadList, IKE_PAYLOAD_NOTICE);
|
|
// Handle the notification payload
|
|
for (i = 0;i < num;i++)
|
|
{
|
|
IKE_PACKET_PAYLOAD *payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NOTICE, i);
|
|
IKE_PACKET_NOTICE_PAYLOAD *n = &payload->Payload.Notice;
|
|
|
|
if (n->MessageType == IKE_NOTICE_DPD_REQUEST || n->MessageType == IKE_NOTICE_DPD_RESPONSE)
|
|
{
|
|
if (n->MessageData != NULL && n->MessageData->Size == sizeof(UINT))
|
|
{
|
|
UINT seq_no = READ_UINT(n->MessageData->Buf);
|
|
|
|
if (n->Spi->Size == (sizeof(UINT64) * 2))
|
|
{
|
|
UINT64 init_cookie = READ_UINT64(((UCHAR *)n->Spi->Buf));
|
|
UINT64 resp_cookie = READ_UINT64(((UCHAR *)n->Spi->Buf) + sizeof(UINT64));
|
|
|
|
if (init_cookie != 0 && resp_cookie != 0)
|
|
{
|
|
IKE_SA *found_ike_sa = SearchIkeSaByCookie(ike, init_cookie, resp_cookie);
|
|
|
|
if (found_ike_sa != NULL && found_ike_sa->IkeClient == c)
|
|
{
|
|
if (n->MessageType == IKE_NOTICE_DPD_REQUEST)
|
|
{
|
|
// Return the DPD Response (ACK) for the DPD Request
|
|
SendInformationalExchangePacket(ike, c,
|
|
IkeNewNoticeDpdPayload(true, init_cookie, resp_cookie,
|
|
seq_no));
|
|
}
|
|
|
|
// Update the status of the IKE SA
|
|
found_ike_sa->LastCommTick = ike->Now;
|
|
ike_sa->LastCommTick = ike->Now;
|
|
found_ike_sa->IkeClient->LastCommTick = ike->Now;
|
|
ike_sa->IkeClient->LastCommTick = ike->Now;
|
|
ike_sa->IkeClient->CurrentIkeSa = ike_sa;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeBuf(hash1_buf);
|
|
}
|
|
}
|
|
|
|
IkeFree(pr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create a new message ID
|
|
UINT GenerateNewMessageId(IKE_SERVER *ike)
|
|
{
|
|
UINT ret;
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
ret = Rand32();
|
|
|
|
if (ret != 0 && ret != 0xffffffff)
|
|
{
|
|
UINT i;
|
|
bool ok = true;
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa->MessageId == ret)
|
|
{
|
|
ok = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start the quick mode
|
|
void StartQuickMode(IKE_SERVER *ike, IKE_CLIENT *c)
|
|
{
|
|
IPSEC_SA_TRANSFORM_SETTING setting;
|
|
IKE_SA *ike_sa;
|
|
UINT message_id;
|
|
UCHAR iv[IKE_MAX_BLOCK_SIZE];
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsZero(&c->CachedTransformSetting, sizeof(IPSEC_SA_TRANSFORM_SETTING)))
|
|
{
|
|
// Cached transform setting does not exist
|
|
Debug("Error: c->CachedTransformSetting is not existing.\n");
|
|
return;
|
|
}
|
|
|
|
ike_sa = c->CurrentIkeSa;
|
|
if (ike_sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IPsecLog(ike, NULL, ike_sa, NULL, "LI_START_QM_FROM_SERVER");
|
|
|
|
Copy(&setting, &c->CachedTransformSetting, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
|
|
message_id = GenerateNewMessageId(ike);
|
|
|
|
IkeCalcPhase2InitialIv(iv, ike_sa, message_id);
|
|
|
|
#ifdef FORCE_LIFETIME_QM
|
|
setting.LifeSeconds = FORCE_LIFETIME_QM;
|
|
#endif // FORCE_LIFETIME_QM
|
|
|
|
if (true)
|
|
{
|
|
IKE_PACKET *ps;
|
|
LIST *payload_list;
|
|
IKE_PACKET_PAYLOAD *send_hash_payload;
|
|
IKE_PACKET_PAYLOAD *send_sa_payload;
|
|
IKE_PACKET_PAYLOAD *send_proposal_payload;
|
|
IKE_PACKET_PAYLOAD *send_transform_payload;
|
|
IKE_PACKET_PAYLOAD *send_rand_payload;
|
|
IKE_PACKET_PAYLOAD *send_key_payload = NULL;
|
|
IKE_PACKET_PAYLOAD *send_id_1 = NULL, *send_id_2 = NULL;
|
|
UINT shared_key_size = 0;
|
|
UCHAR *shared_key = NULL;
|
|
BUF *initiator_rand;
|
|
IPSECSA *ipsec_sa_s_c, *ipsec_sa_c_s;
|
|
BUF *ps_buf;
|
|
UINT after_hash_offset, after_hash_size;
|
|
BUF *ps_buf_after_hash;
|
|
BUF *tmp_buf;
|
|
UINT spi;
|
|
UINT spi_be;
|
|
UCHAR hash1[IKE_MAX_HASH_SIZE];
|
|
DH_CTX *dh = NULL;
|
|
UCHAR dummy_hash_data[IKE_MAX_HASH_SIZE];
|
|
|
|
initiator_rand = RandBuf(IKE_SA_RAND_SIZE);
|
|
|
|
if (setting.Dh != NULL)
|
|
{
|
|
// Generate DH
|
|
dh = IkeDhNewCtx(setting.Dh);
|
|
|
|
if (dh != NULL)
|
|
{
|
|
send_key_payload = IkeNewDataPayload(IKE_PAYLOAD_KEY_EXCHANGE,
|
|
dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
|
|
}
|
|
}
|
|
|
|
Zero(dummy_hash_data, sizeof(dummy_hash_data));
|
|
|
|
// Dummy hash value
|
|
payload_list = NewListFast(NULL);
|
|
send_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, dummy_hash_data, ike_sa->HashSize);
|
|
Add(payload_list, send_hash_payload);
|
|
|
|
// Determine the SPI
|
|
spi = GenerateNewIPsecSaSpi(ike, 0);
|
|
spi_be = Endian32(spi);
|
|
|
|
// SA
|
|
send_transform_payload = TransformSettingToTransformPayloadForIPsec(ike, &setting);
|
|
send_proposal_payload = IkeNewProposalPayload(1, IKE_PROTOCOL_ID_IPSEC_ESP, &spi_be, sizeof(spi_be),
|
|
NewListSingle(send_transform_payload));
|
|
send_sa_payload = IkeNewSaPayload(NewListSingle(send_proposal_payload));
|
|
Add(payload_list, send_sa_payload);
|
|
|
|
// Random number
|
|
send_rand_payload = IkeNewDataPayload(IKE_PAYLOAD_RAND, initiator_rand->Buf, initiator_rand->Size);
|
|
Add(payload_list, send_rand_payload);
|
|
|
|
// Key exchange
|
|
if (send_key_payload != NULL)
|
|
{
|
|
Add(payload_list, send_key_payload);
|
|
}
|
|
|
|
if (c->SendID1andID2)
|
|
{
|
|
// Add the ID payload
|
|
if (setting.CapsuleMode == IKE_P2_CAPSULE_NAT_TUNNEL_1 || setting.CapsuleMode == IKE_P2_CAPSULE_NAT_TUNNEL_2)
|
|
{
|
|
UCHAR zero[32];
|
|
|
|
Zero(zero, sizeof(zero));
|
|
|
|
// Tunnel Mode
|
|
send_id_1 = IkeNewIdPayload((IsIP4(&c->ServerIP) ? IKE_ID_IPV4_ADDR_SUBNET : IKE_ID_IPV6_ADDR_SUBNET),
|
|
0, 0,
|
|
zero, (IsIP4(&c->ServerIP) ? 8 : 32));
|
|
|
|
send_id_2 = IkeNewIdPayload(c->SendID1_Type,
|
|
c->SendID1_Protocol, c->SendID1_Port,
|
|
c->SendID1_Buf->Buf, c->SendID1_Buf->Size);
|
|
}
|
|
else
|
|
{
|
|
// Transport mode
|
|
// Specify in the reverse order in which the client has been specified
|
|
send_id_2 = IkeNewIdPayload(c->SendID1_Type,
|
|
c->SendID1_Protocol, c->SendID1_Port,
|
|
c->SendID1_Buf->Buf, c->SendID1_Buf->Size);
|
|
|
|
send_id_1 = IkeNewIdPayload(c->SendID2_Type,
|
|
c->SendID2_Protocol, c->SendID2_Port,
|
|
c->SendID2_Buf->Buf, c->SendID2_Buf->Size);
|
|
}
|
|
|
|
Add(payload_list, send_id_1);
|
|
Add(payload_list, send_id_2);
|
|
}
|
|
|
|
if (true)
|
|
{
|
|
// NAT-OA payload
|
|
if (c->SendNatOaDraft1)
|
|
{
|
|
Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA_DRAFT, &c->ServerIP));
|
|
}
|
|
|
|
if (c->SendNatOaDraft2)
|
|
{
|
|
Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA_DRAFT_2, &c->ServerIP));
|
|
}
|
|
|
|
if (c->SendNatOaRfc)
|
|
{
|
|
Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA, &c->ClientIP));
|
|
Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA, &c->ServerIP));
|
|
}
|
|
}
|
|
|
|
// Build a packet
|
|
ps = IkeNew(ike_sa->InitiatorCookie, ike_sa->ResponderCookie, IKE_EXCHANGE_TYPE_QUICK,
|
|
false, false, false, message_id, payload_list);
|
|
|
|
// Build a temporary packet
|
|
ps_buf = IkeBuild(ps, NULL);
|
|
|
|
// Get the payload after the hash part
|
|
after_hash_offset = sizeof(IKE_HEADER) + send_hash_payload->BitArray->Size + sizeof(IKE_COMMON_HEADER);
|
|
after_hash_size = ((ps_buf->Size > after_hash_offset) ? (ps_buf->Size - after_hash_offset) : 0);
|
|
|
|
ps_buf_after_hash = MemToBuf(((UCHAR *)ps_buf->Buf) + after_hash_offset, after_hash_size);
|
|
FreeBuf(ps_buf);
|
|
|
|
// Calculate the hash #1
|
|
tmp_buf = NewBuf();
|
|
WriteBufInt(tmp_buf, message_id);
|
|
WriteBufBuf(tmp_buf, ps_buf_after_hash);
|
|
IkeHMac(ike_sa->TransformSetting.Hash, hash1, ike_sa->SKEYID_a, ike_sa->HashSize, tmp_buf->Buf, tmp_buf->Size);
|
|
FreeBuf(tmp_buf);
|
|
|
|
// Overwrite hash #1
|
|
Copy(send_hash_payload->Payload.Hash.Data->Buf, hash1, ike_sa->HashSize);
|
|
|
|
// Create an IPsec SA
|
|
ipsec_sa_c_s = NewIPsecSa(ike, c, ike_sa, true, message_id, false, iv, spi,
|
|
initiator_rand->Buf, initiator_rand->Size, NULL, 0,
|
|
&setting, shared_key, shared_key_size);
|
|
|
|
ipsec_sa_s_c = NewIPsecSa(ike, c, ike_sa, true, message_id, true, iv, 0,
|
|
initiator_rand->Buf, initiator_rand->Size, NULL, 0,
|
|
&setting, shared_key, shared_key_size);
|
|
|
|
ipsec_sa_c_s->PairIPsecSa = ipsec_sa_s_c;
|
|
ipsec_sa_s_c->PairIPsecSa = ipsec_sa_c_s;
|
|
|
|
ipsec_sa_s_c->Dh = dh;
|
|
|
|
Insert(ike->IPsecSaList, ipsec_sa_c_s);
|
|
Insert(ike->IPsecSaList, ipsec_sa_s_c);
|
|
|
|
// Packet transmission
|
|
ps->FlagEncrypted = true;
|
|
IPsecSaSendPacket(ike, ipsec_sa_s_c, ps);
|
|
ipsec_sa_s_c->NumResends = 3;
|
|
#ifdef RAW_DEBUG
|
|
IkeDebugUdpSendRawPacket(ps);
|
|
#endif // RAW_DEBUG
|
|
|
|
IkeFree(ps);
|
|
Free(shared_key);
|
|
FreeBuf(ps_buf_after_hash);
|
|
FreeBuf(initiator_rand);
|
|
}
|
|
}
|
|
|
|
// Process the quick mode received packet
|
|
void ProcIkeQuickModePacketRecv(IKE_SERVER *ike, UDPPACKET *p, IKE_PACKET *header)
|
|
{
|
|
IKE_CLIENT *c;
|
|
IKE_SA *ike_sa;
|
|
// Validate arguments
|
|
if (ike == NULL || p == NULL || header == NULL || header->InitiatorCookie == 0 || header->ResponderCookie == 0
|
|
|| header->MessageId == 0 || header->FlagEncrypted == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c = SearchOrCreateNewIkeClientForIkePacket(ike, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort, header);
|
|
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ike_sa = FindIkeSaByResponderCookieAndClient(ike, header->ResponderCookie, c);
|
|
|
|
if (ike_sa == NULL)
|
|
{
|
|
// IKE SA does not exist
|
|
SendInformationalExchangePacketEx(ike, c, IkeNewNoticeErrorInvalidCookiePayload(header->InitiatorCookie,
|
|
header->ResponderCookie), true, header->InitiatorCookie, header->ResponderCookie);
|
|
}
|
|
|
|
if (ike_sa != NULL && ike_sa->Established)
|
|
{
|
|
// Update the status of the IKE SA
|
|
ike_sa->LastCommTick = ike->Now;
|
|
ike_sa->IkeClient->LastCommTick = ike->Now;
|
|
ike_sa->IkeClient->CurrentIkeSa = ike_sa;
|
|
|
|
// Search whether the Message ID is already in the database
|
|
if (SearchIPsecSaByMessageId(ike, c, header->MessageId) == NULL)
|
|
{
|
|
IKE_PACKET *pr;
|
|
IKE_CRYPTO_PARAM cp;
|
|
|
|
// Message ID does not exist. Start a new Quick Mode session
|
|
Zero(&cp, sizeof(cp));
|
|
cp.Key = ike_sa->CryptoKey;
|
|
IkeCalcPhase2InitialIv(cp.Iv, ike_sa, header->MessageId);
|
|
|
|
pr = IkeParse(p->Data, p->Size, &cp);
|
|
#ifdef RAW_DEBUG
|
|
IkeDebugUdpSendRawPacket(pr);
|
|
#endif // RAW_DEBUG
|
|
if (pr != NULL)
|
|
{
|
|
// Get the hash payload
|
|
IKE_PACKET_PAYLOAD *hash_payload;
|
|
|
|
hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
|
|
if (hash_payload != NULL)
|
|
{
|
|
// Get the payload after the hash
|
|
UINT header_and_hash_size = sizeof(IKE_COMMON_HEADER) + hash_payload->BitArray->Size;
|
|
void *after_hash_data = ((UCHAR *)pr->DecryptedPayload->Buf) + header_and_hash_size;
|
|
if (pr->DecryptedPayload->Size > header_and_hash_size)
|
|
{
|
|
UINT after_hash_size = pr->DecryptedPayload->Size - header_and_hash_size;
|
|
UCHAR hash1[IKE_MAX_HASH_SIZE];
|
|
BUF *hash1_buf;
|
|
|
|
hash1_buf = NewBuf();
|
|
WriteBufInt(hash1_buf, header->MessageId);
|
|
WriteBuf(hash1_buf, after_hash_data, after_hash_size);
|
|
|
|
IkeHMac(ike_sa->TransformSetting.Hash, hash1, ike_sa->SKEYID_a, ike_sa->HashSize,
|
|
hash1_buf->Buf, hash1_buf->Size);
|
|
|
|
// Compare the hash value
|
|
if (IkeCompareHash(hash_payload, hash1, ike_sa->HashSize))
|
|
{
|
|
IKE_PACKET_PAYLOAD *sa_payload, *rand_payload, *key_payload, *id_payload_1, *id_payload_2;
|
|
|
|
// Get the payload of other
|
|
sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
|
|
rand_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_RAND, 0);
|
|
key_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_KEY_EXCHANGE, 0);
|
|
id_payload_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 0);
|
|
id_payload_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 1);
|
|
|
|
if (sa_payload != NULL && rand_payload != NULL)
|
|
{
|
|
IPSEC_SA_TRANSFORM_SETTING setting;
|
|
|
|
Zero(&setting, sizeof(setting));
|
|
|
|
// Interpret the SA payload
|
|
if (GetBestTransformSettingForIPsecSa(ike, pr, &setting, &p->DstIP) && (GetNumberOfIPsecSaOfIkeClient(ike, c) <= IKE_QUOTA_MAX_SA_PER_CLIENT))
|
|
{
|
|
// Appropriate transform setting is selected
|
|
Debug("P2 Transform: %s %s %s(%u) %u %u\n",
|
|
(setting.Dh == NULL ? NULL : setting.Dh->Name), setting.Hash->Name, setting.Crypto->Name, setting.CryptoKeySize,
|
|
setting.LifeKilobytes, setting.LifeSeconds);
|
|
|
|
#ifdef FORCE_LIFETIME_QM
|
|
setting.LifeSeconds = FORCE_LIFETIME_QM;
|
|
#endif // FORCE_LIFETIME_QM
|
|
|
|
// Cache the transform attribute value
|
|
Copy(&c->CachedTransformSetting, &setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
|
|
// Check the key exchange payload if the PFS is specified
|
|
if (setting.Dh == NULL || (setting.Dh != NULL && key_payload != NULL &&
|
|
key_payload->Payload.KeyExchange.Data->Size <= setting.Dh->KeySize))
|
|
{
|
|
// Create a payload for response
|
|
IKE_PACKET *ps;
|
|
LIST *payload_list;
|
|
IKE_PACKET_PAYLOAD *send_hash_payload;
|
|
IKE_PACKET_PAYLOAD *send_sa_payload;
|
|
IKE_PACKET_PAYLOAD *send_proposal_payload;
|
|
IKE_PACKET_PAYLOAD *send_transform_payload;
|
|
IKE_PACKET_PAYLOAD *send_rand_payload;
|
|
IKE_PACKET_PAYLOAD *send_key_payload = NULL;
|
|
IKE_PACKET_PAYLOAD *send_id_1 = NULL, *send_id_2 = NULL;
|
|
UCHAR dummy_hash_data[IKE_MAX_HASH_SIZE];
|
|
DH_CTX *dh = NULL;
|
|
UINT shared_key_size = 0;
|
|
UCHAR *shared_key = NULL;
|
|
BUF *initiator_rand, *responder_rand;
|
|
IPSECSA *ipsec_sa_s_c, *ipsec_sa_c_s;
|
|
BUF *ps_buf;
|
|
UINT after_hash_offset, after_hash_size;
|
|
BUF *ps_buf_after_hash;
|
|
BUF *tmp_buf;
|
|
UINT spi;
|
|
UINT spi_be;
|
|
UCHAR hash2[IKE_MAX_HASH_SIZE];
|
|
UCHAR hash3[IKE_MAX_HASH_SIZE];
|
|
UCHAR zero = 0;
|
|
|
|
IPsecLog(ike, NULL, ike_sa, NULL, "LI_START_QM_FROM_CLIENT");
|
|
|
|
initiator_rand = CloneBuf(rand_payload->Payload.Rand.Data);
|
|
responder_rand = RandBuf(IKE_SA_RAND_SIZE);
|
|
|
|
if (setting.Dh != NULL)
|
|
{
|
|
// Calculate DH
|
|
dh = IkeDhNewCtx(setting.Dh);
|
|
shared_key_size = (dh == NULL ? 0 : dh->Size);
|
|
shared_key = ZeroMalloc(shared_key_size);
|
|
|
|
if (DhCompute(dh, shared_key, key_payload->Payload.KeyExchange.Data->Buf, key_payload->Payload.KeyExchange.Data->Size))
|
|
{
|
|
// DH calculation success
|
|
Debug("P2 DH Ok.\n");
|
|
|
|
send_key_payload = IkeNewDataPayload(IKE_PAYLOAD_KEY_EXCHANGE,
|
|
dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
|
|
|
|
IkeDhFreeCtx(dh);
|
|
}
|
|
else
|
|
{
|
|
// DH calculation failure
|
|
Debug("P2 DhCompute failed.\n");
|
|
|
|
shared_key = NULL;
|
|
Free(shared_key);
|
|
shared_key_size = 0;
|
|
|
|
IPsecLog(ike, NULL, ike_sa, NULL, "LI_QM_DH_ERROR");
|
|
}
|
|
}
|
|
|
|
Zero(dummy_hash_data, sizeof(dummy_hash_data));
|
|
|
|
// Dummy hash value
|
|
payload_list = NewListFast(NULL);
|
|
send_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, dummy_hash_data, ike_sa->HashSize);
|
|
Add(payload_list, send_hash_payload);
|
|
|
|
// Determine the SPI
|
|
spi = GenerateNewIPsecSaSpi(ike, setting.SpiServerToClient);
|
|
spi_be = Endian32(spi);
|
|
|
|
// SA
|
|
send_transform_payload = TransformSettingToTransformPayloadForIPsec(ike, &setting);
|
|
send_proposal_payload = IkeNewProposalPayload(1, IKE_PROTOCOL_ID_IPSEC_ESP, &spi_be, sizeof(spi_be),
|
|
NewListSingle(send_transform_payload));
|
|
send_sa_payload = IkeNewSaPayload(NewListSingle(send_proposal_payload));
|
|
Add(payload_list, send_sa_payload);
|
|
|
|
// Random number
|
|
send_rand_payload = IkeNewDataPayload(IKE_PAYLOAD_RAND, responder_rand->Buf, responder_rand->Size);
|
|
Add(payload_list, send_rand_payload);
|
|
|
|
// Key exchange
|
|
if (send_key_payload != NULL)
|
|
{
|
|
Add(payload_list, send_key_payload);
|
|
}
|
|
|
|
// ID
|
|
if (id_payload_1 != NULL && id_payload_2 != NULL)
|
|
{
|
|
send_id_1 = IkeNewIdPayload(id_payload_1->Payload.Id.Type,
|
|
id_payload_1->Payload.Id.ProtocolId, id_payload_1->Payload.Id.Port,
|
|
id_payload_1->Payload.Id.IdData->Buf, id_payload_1->Payload.Id.IdData->Size);
|
|
|
|
send_id_2 = IkeNewIdPayload(id_payload_2->Payload.Id.Type,
|
|
id_payload_2->Payload.Id.ProtocolId, id_payload_2->Payload.Id.Port,
|
|
id_payload_2->Payload.Id.IdData->Buf, id_payload_2->Payload.Id.IdData->Size);
|
|
|
|
Add(payload_list, send_id_1);
|
|
Add(payload_list, send_id_2);
|
|
|
|
if (c->SendID1_Buf != NULL)
|
|
{
|
|
FreeBuf(c->SendID1_Buf);
|
|
}
|
|
|
|
if (c->SendID2_Buf != NULL)
|
|
{
|
|
FreeBuf(c->SendID2_Buf);
|
|
}
|
|
|
|
c->SendID1_Type = id_payload_1->Payload.Id.Type;
|
|
c->SendID1_Protocol = id_payload_1->Payload.Id.ProtocolId;
|
|
c->SendID1_Port = id_payload_1->Payload.Id.Port;
|
|
c->SendID1_Buf = CloneBuf(id_payload_1->Payload.Id.IdData);
|
|
|
|
c->SendID2_Type = id_payload_2->Payload.Id.Type;
|
|
c->SendID2_Protocol = id_payload_2->Payload.Id.ProtocolId;
|
|
c->SendID2_Port = id_payload_2->Payload.Id.Port;
|
|
c->SendID2_Buf = CloneBuf(id_payload_2->Payload.Id.IdData);
|
|
|
|
c->SendID1andID2 = true;
|
|
}
|
|
else
|
|
{
|
|
c->SendID1andID2 = false;
|
|
}
|
|
|
|
if (true)
|
|
{
|
|
// Reply if NAT-OA payload is presented by the client
|
|
IKE_PACKET_PAYLOAD *nat_oa_draft1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_OA_DRAFT, 0);
|
|
IKE_PACKET_PAYLOAD *nat_oa_draft2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_OA_DRAFT_2, 0);
|
|
IKE_PACKET_PAYLOAD *nat_oa_rfc_0 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_OA, 0);
|
|
IKE_PACKET_PAYLOAD *nat_oa_rfc_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_OA, 1);
|
|
|
|
c->SendNatOaDraft1 = c->SendNatOaDraft2 = c->SendNatOaRfc = false;
|
|
|
|
c->ShouldCalcChecksumForUDP = false;
|
|
|
|
if (nat_oa_draft1 != NULL)
|
|
{
|
|
Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA_DRAFT, &c->ServerIP));
|
|
c->SendNatOaDraft1 = true;
|
|
|
|
if (IsIP4(&nat_oa_draft1->Payload.NatOa.IpAddress) == IsIP4(&c->ServerIP))
|
|
{
|
|
Copy(&c->TransportModeClientIP, &nat_oa_draft1->Payload.NatOa.IpAddress, sizeof(IP));
|
|
Copy(&c->TransportModeServerIP, &c->ServerIP, sizeof(IP));
|
|
|
|
c->ShouldCalcChecksumForUDP = true;
|
|
}
|
|
}
|
|
|
|
if (nat_oa_draft2 != NULL)
|
|
{
|
|
Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA_DRAFT_2, &c->ServerIP));
|
|
c->SendNatOaDraft2 = true;
|
|
|
|
if (IsIP4(&nat_oa_draft2->Payload.NatOa.IpAddress) == IsIP4(&c->ServerIP))
|
|
{
|
|
Copy(&c->TransportModeClientIP, &nat_oa_draft2->Payload.NatOa.IpAddress, sizeof(IP));
|
|
Copy(&c->TransportModeServerIP, &c->ServerIP, sizeof(IP));
|
|
|
|
c->ShouldCalcChecksumForUDP = true;
|
|
}
|
|
}
|
|
|
|
if (nat_oa_rfc_0 != NULL && nat_oa_rfc_1 != NULL)
|
|
{
|
|
Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA, &c->ClientIP));
|
|
Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA, &c->ServerIP));
|
|
c->SendNatOaRfc = true;
|
|
|
|
if (IsIP4(&nat_oa_rfc_0->Payload.NatOa.IpAddress) == IsIP4(&c->ServerIP))
|
|
{
|
|
Copy(&c->TransportModeClientIP, &nat_oa_rfc_0->Payload.NatOa.IpAddress, sizeof(IP));
|
|
Copy(&c->TransportModeServerIP, &c->ServerIP, sizeof(IP));
|
|
|
|
c->ShouldCalcChecksumForUDP = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build a packet
|
|
ps = IkeNew(ike_sa->InitiatorCookie, ike_sa->ResponderCookie, IKE_EXCHANGE_TYPE_QUICK,
|
|
false, false, false, header->MessageId, payload_list);
|
|
|
|
// Build a temporary packet
|
|
ps_buf = IkeBuild(ps, NULL);
|
|
|
|
// Get the payload after the hash part
|
|
after_hash_offset = sizeof(IKE_HEADER) + send_hash_payload->BitArray->Size + sizeof(IKE_COMMON_HEADER);
|
|
after_hash_size = ((ps_buf->Size > after_hash_offset) ? (ps_buf->Size - after_hash_offset) : 0);
|
|
|
|
ps_buf_after_hash = MemToBuf(((UCHAR *)ps_buf->Buf) + after_hash_offset, after_hash_size);
|
|
FreeBuf(ps_buf);
|
|
|
|
// Calculate the hash #2
|
|
tmp_buf = NewBuf();
|
|
WriteBufInt(tmp_buf, header->MessageId);
|
|
WriteBufBuf(tmp_buf, initiator_rand);
|
|
WriteBufBuf(tmp_buf, ps_buf_after_hash);
|
|
IkeHMac(ike_sa->TransformSetting.Hash, hash2, ike_sa->SKEYID_a, ike_sa->HashSize, tmp_buf->Buf, tmp_buf->Size);
|
|
FreeBuf(tmp_buf);
|
|
|
|
// Calculate the hash #3
|
|
tmp_buf = NewBuf();
|
|
WriteBuf(tmp_buf, &zero, 1);
|
|
WriteBufInt(tmp_buf, header->MessageId);
|
|
WriteBufBuf(tmp_buf, initiator_rand);
|
|
WriteBufBuf(tmp_buf, responder_rand);
|
|
IkeHMac(ike_sa->TransformSetting.Hash, hash3, ike_sa->SKEYID_a, ike_sa->HashSize, tmp_buf->Buf, tmp_buf->Size);
|
|
FreeBuf(tmp_buf);
|
|
|
|
// Create an IPsec SA
|
|
ipsec_sa_c_s = NewIPsecSa(ike, c, ike_sa, false, header->MessageId, false, cp.NextIv, spi,
|
|
initiator_rand->Buf, initiator_rand->Size, responder_rand->Buf, responder_rand->Size,
|
|
&setting, shared_key, shared_key_size);
|
|
ipsec_sa_s_c = NewIPsecSa(ike, c, ike_sa, false, header->MessageId, true, cp.NextIv, setting.SpiServerToClient,
|
|
initiator_rand->Buf, initiator_rand->Size, responder_rand->Buf, responder_rand->Size,
|
|
&setting, shared_key, shared_key_size);
|
|
|
|
ipsec_sa_c_s->PairIPsecSa = ipsec_sa_s_c;
|
|
ipsec_sa_s_c->PairIPsecSa = ipsec_sa_c_s;
|
|
|
|
Insert(ike->IPsecSaList, ipsec_sa_c_s);
|
|
Insert(ike->IPsecSaList, ipsec_sa_s_c);
|
|
|
|
Copy(ipsec_sa_c_s->Hash3, hash3, ike_sa->HashSize);
|
|
|
|
// Overwrite hash #2
|
|
Copy(send_hash_payload->Payload.Hash.Data->Buf, hash2, ike_sa->HashSize);
|
|
|
|
// Packet reply
|
|
ps->FlagEncrypted = true;
|
|
IPsecSaSendPacket(ike, ipsec_sa_s_c, ps);
|
|
IkeSaSendPacket(ike, ike_sa, NULL);
|
|
|
|
#ifdef RAW_DEBUG
|
|
IkeDebugUdpSendRawPacket(ps);
|
|
#endif // RAW_DEBUG
|
|
|
|
IkeFree(ps);
|
|
Free(shared_key);
|
|
FreeBuf(ps_buf_after_hash);
|
|
FreeBuf(initiator_rand);
|
|
FreeBuf(responder_rand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No appropriate transform setting
|
|
Debug("No Appropriate Transform was Found.\n");
|
|
|
|
IPsecLog(ike, NULL, ike_sa, NULL, "LI_IPSEC_NO_TRANSFORM");
|
|
|
|
SendInformationalExchangePacket(ike, c, IkeNewNoticeErrorNoProposalChosenPayload(true, header->InitiatorCookie, header->ResponderCookie));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug("QM-1: Hash 1 is invalid.\n");
|
|
}
|
|
|
|
FreeBuf(hash1_buf);
|
|
}
|
|
}
|
|
|
|
IkeFree(pr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get the IPsec SA
|
|
IPSECSA *ipsec_sa_cs = SearchIPsecSaByMessageId(ike, c, header->MessageId);
|
|
if (ipsec_sa_cs != NULL)
|
|
{
|
|
IPSECSA *ipsec_sa_sc = ipsec_sa_cs->PairIPsecSa;
|
|
if (ipsec_sa_sc != NULL)
|
|
{
|
|
if (ipsec_sa_sc->Established == false && ipsec_sa_cs->Established == false)
|
|
{
|
|
IKE_PACKET *pr = IPsecSaRecvPacket(ike, ipsec_sa_cs, p->Data, p->Size);
|
|
|
|
#ifdef RAW_DEBUG
|
|
IkeDebugUdpSendRawPacket(pr);
|
|
#endif // RAW_DEBUG
|
|
|
|
if (pr != NULL)
|
|
{
|
|
if (ipsec_sa_cs->Initiated == false)
|
|
{
|
|
// Initiator is client-side
|
|
// Check hash3 payload
|
|
IKE_PACKET_PAYLOAD *hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
|
|
|
|
if (hash_payload != NULL)
|
|
{
|
|
BUF *hash_buf = hash_payload->Payload.Hash.Data;
|
|
if (hash_buf != NULL)
|
|
{
|
|
if (hash_buf->Size == ipsec_sa_cs->IkeSa->HashSize)
|
|
{
|
|
if (Cmp(hash_buf->Buf, ipsec_sa_cs->Hash3, hash_buf->Size) == 0)
|
|
{
|
|
ipsec_sa_cs->Established = ipsec_sa_sc->Established = true;
|
|
ipsec_sa_cs->EstablishedTick = ipsec_sa_sc->EstablishedTick = ike->Now;
|
|
ipsec_sa_cs->LastCommTick = ipsec_sa_sc->LastCommTick = ike->Now;
|
|
|
|
c->CurrentIpSecSaRecv = ipsec_sa_cs;
|
|
c->CurrentIpSecSaSend = ipsec_sa_sc;
|
|
|
|
Debug("IPsec SA 0x%X & 0x%X Established.\n",
|
|
ipsec_sa_cs->Spi,
|
|
ipsec_sa_sc->Spi);
|
|
|
|
IPsecLog(ike, NULL, NULL, ipsec_sa_sc, "LI_IPSEC_SA_ESTABLISHED");
|
|
|
|
IPsecSaSendPacket(ike, ipsec_sa_sc, NULL);
|
|
}
|
|
else
|
|
{
|
|
Debug("QM-3: Hash 3 is invalid.\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Initiator is server-side
|
|
// Get hash payload
|
|
IKE_PACKET_PAYLOAD *hash_payload;
|
|
|
|
hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
|
|
if (hash_payload != NULL && ipsec_sa_sc->InitiatorRand != NULL)
|
|
{
|
|
// Get the payload after the hash
|
|
UINT header_and_hash_size = sizeof(IKE_COMMON_HEADER) + hash_payload->BitArray->Size;
|
|
void *after_hash_data = ((UCHAR *)pr->DecryptedPayload->Buf) + header_and_hash_size;
|
|
if (pr->DecryptedPayload->Size > header_and_hash_size)
|
|
{
|
|
UINT after_hash_size = pr->DecryptedPayload->Size - header_and_hash_size;
|
|
UCHAR hash2[IKE_MAX_HASH_SIZE];
|
|
BUF *hash2_buf;
|
|
|
|
hash2_buf = NewBuf();
|
|
WriteBufInt(hash2_buf, header->MessageId);
|
|
WriteBufBuf(hash2_buf, ipsec_sa_sc->InitiatorRand);
|
|
WriteBuf(hash2_buf, after_hash_data, after_hash_size);
|
|
|
|
IkeHMac(ipsec_sa_sc->SKEYID_Hash, hash2, ipsec_sa_sc->SKEYID_a, ipsec_sa_sc->SKEYID_Hash->HashSize,
|
|
hash2_buf->Buf, hash2_buf->Size);
|
|
|
|
FreeBuf(hash2_buf);
|
|
|
|
// Compare the hash value
|
|
if (IkeCompareHash(hash_payload, hash2, ike_sa->HashSize))
|
|
{
|
|
IKE_PACKET_PAYLOAD *sa_payload, *rand_payload, *key_payload, *id_payload_1, *id_payload_2;
|
|
|
|
// Get the payload of other
|
|
sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
|
|
rand_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_RAND, 0);
|
|
key_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_KEY_EXCHANGE, 0);
|
|
id_payload_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 0);
|
|
id_payload_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 1);
|
|
|
|
if (sa_payload != NULL && rand_payload != NULL)
|
|
{
|
|
IPSEC_SA_TRANSFORM_SETTING setting;
|
|
|
|
// Interpret the SA payload
|
|
if (GetBestTransformSettingForIPsecSa(ike, pr, &setting, &p->DstIP))
|
|
{
|
|
// Appropriate transform setting is selected
|
|
Debug("P2 Transform: %s %s %s(%u) %u %u\n",
|
|
(setting.Dh == NULL ? NULL : setting.Dh->Name), setting.Hash->Name, setting.Crypto->Name, setting.CryptoKeySize,
|
|
setting.LifeKilobytes, setting.LifeSeconds);
|
|
|
|
#ifdef FORCE_LIFETIME_QM
|
|
setting.LifeSeconds = FORCE_LIFETIME_QM;
|
|
#endif // FORCE_LIFETIME_QM
|
|
|
|
// Check the key exchange payload if the PFS is specified
|
|
if (setting.Dh == NULL || (setting.Dh != NULL && key_payload != NULL && ipsec_sa_sc->Dh != NULL &&
|
|
key_payload->Payload.KeyExchange.Data->Size <= setting.Dh->KeySize))
|
|
{
|
|
IKE_PACKET *ps;
|
|
LIST *payload_list;
|
|
IKE_PACKET_PAYLOAD *send_hash_payload;
|
|
IKE_PACKET_PAYLOAD *send_key_payload = NULL;
|
|
IKE_PACKET_PAYLOAD *send_id_1 = NULL, *send_id_2 = NULL;
|
|
DH_CTX *dh = NULL;
|
|
UINT shared_key_size = 0;
|
|
UCHAR *shared_key = NULL;
|
|
BUF *initiator_rand, *responder_rand;
|
|
BUF *tmp_buf;
|
|
UCHAR hash3[IKE_MAX_HASH_SIZE];
|
|
char tmp[MAX_SIZE];
|
|
UCHAR zero = 0;
|
|
|
|
initiator_rand = ipsec_sa_sc->InitiatorRand;
|
|
responder_rand = CloneBuf(rand_payload->Payload.Rand.Data);
|
|
|
|
if (setting.Dh != NULL)
|
|
{
|
|
// Calculate DH
|
|
DH_CTX *dh = ipsec_sa_sc->Dh;
|
|
|
|
shared_key_size = (dh == NULL ? 0 : dh->Size);
|
|
shared_key = ZeroMalloc(shared_key_size);
|
|
|
|
if (DhCompute(dh, shared_key, key_payload->Payload.KeyExchange.Data->Buf, key_payload->Payload.KeyExchange.Data->Size))
|
|
{
|
|
// DH calculation success
|
|
Debug("P2 DH Ok.\n");
|
|
}
|
|
else
|
|
{
|
|
// DH calculation failure
|
|
Debug("P2 DhCompute failed.\n");
|
|
|
|
shared_key = NULL;
|
|
Free(shared_key);
|
|
shared_key_size = 0;
|
|
|
|
IPsecLog(ike, NULL, ike_sa, NULL, "LI_QM_DH_ERROR");
|
|
}
|
|
}
|
|
|
|
// Update the information of IPsec SA
|
|
if (shared_key != NULL)
|
|
{
|
|
ipsec_sa_sc->SharedKey = NewBuf(shared_key, shared_key_size);
|
|
ipsec_sa_cs->SharedKey = NewBuf(shared_key, shared_key_size);
|
|
}
|
|
|
|
ipsec_sa_sc->Spi = setting.SpiServerToClient;
|
|
IPsecLog(ike, NULL, NULL, ipsec_sa_sc, "LI_IPSEC_SA_SPI_SET", ipsec_sa_sc->Spi);
|
|
ike->IPsecSaList->sorted = false;
|
|
|
|
ipsec_sa_sc->ResponderRand = CloneBuf(responder_rand);
|
|
ipsec_sa_cs->ResponderRand = CloneBuf(responder_rand);
|
|
|
|
Copy(&ipsec_sa_sc->TransformSetting, &setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
Copy(&ipsec_sa_cs->TransformSetting, &setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
|
|
ipsec_sa_sc->Established = true;
|
|
ipsec_sa_cs->Established = true;
|
|
|
|
IPsecLog(ike, NULL, NULL, ipsec_sa_sc, "LI_IPSEC_SA_ESTABLISHED");
|
|
|
|
ipsec_sa_sc->LastCommTick = ike->Now;
|
|
ipsec_sa_cs->LastCommTick = ike->Now;
|
|
|
|
c->CurrentIpSecSaRecv = ipsec_sa_cs;
|
|
c->CurrentIpSecSaSend = ipsec_sa_sc;
|
|
|
|
// Calculate the KEYMAT
|
|
IPsecCalcKeymat(ike, ipsec_sa_sc->SKEYID_Hash, ipsec_sa_sc->KeyMat, sizeof(ipsec_sa_sc->KeyMat),
|
|
ipsec_sa_sc->SKEYID_d, ipsec_sa_sc->SKEYID_Hash->HashSize, IKE_PROTOCOL_ID_IPSEC_ESP,
|
|
ipsec_sa_sc->Spi, initiator_rand->Buf, initiator_rand->Size,
|
|
responder_rand->Buf, responder_rand->Size,
|
|
shared_key, shared_key_size);
|
|
|
|
IPsecCalcKeymat(ike, ipsec_sa_cs->SKEYID_Hash, ipsec_sa_cs->KeyMat, sizeof(ipsec_sa_cs->KeyMat),
|
|
ipsec_sa_cs->SKEYID_d, ipsec_sa_cs->SKEYID_Hash->HashSize, IKE_PROTOCOL_ID_IPSEC_ESP,
|
|
ipsec_sa_cs->Spi, initiator_rand->Buf, initiator_rand->Size,
|
|
responder_rand->Buf, responder_rand->Size,
|
|
shared_key, shared_key_size);
|
|
|
|
IkeFreeKey(ipsec_sa_sc->CryptoKey);
|
|
IkeFreeKey(ipsec_sa_cs->CryptoKey);
|
|
|
|
ipsec_sa_sc->CryptoKey = IkeNewKey(setting.Crypto, ipsec_sa_sc->KeyMat, setting.CryptoKeySize);
|
|
ipsec_sa_cs->CryptoKey = IkeNewKey(setting.Crypto, ipsec_sa_cs->KeyMat, setting.CryptoKeySize);
|
|
|
|
Copy(ipsec_sa_sc->HashKey, ipsec_sa_sc->KeyMat + setting.CryptoKeySize, setting.Hash->HashSize);
|
|
Copy(ipsec_sa_cs->HashKey, ipsec_sa_cs->KeyMat + setting.CryptoKeySize, setting.Hash->HashSize);
|
|
|
|
BinToStrEx(tmp, sizeof(tmp), ipsec_sa_sc->KeyMat, ipsec_sa_sc->TransformSetting.CryptoKeySize);
|
|
Debug(" KEYMAT (SC): %s\n", tmp);
|
|
|
|
BinToStrEx(tmp, sizeof(tmp), ipsec_sa_cs->KeyMat, ipsec_sa_cs->TransformSetting.CryptoKeySize);
|
|
Debug(" KEYMAT (CS): %s\n", tmp);
|
|
|
|
Debug("IPsec SA 0x%X & 0x%X Established (Server is Initiator).\n",
|
|
ipsec_sa_cs->Spi,
|
|
ipsec_sa_sc->Spi);
|
|
|
|
// Calculate the hash #3
|
|
tmp_buf = NewBuf();
|
|
WriteBuf(tmp_buf, &zero, 1);
|
|
WriteBufInt(tmp_buf, header->MessageId);
|
|
WriteBufBuf(tmp_buf, initiator_rand);
|
|
WriteBufBuf(tmp_buf, responder_rand);
|
|
IkeHMac(ipsec_sa_cs->SKEYID_Hash, hash3, ipsec_sa_cs->SKEYID_a, ipsec_sa_cs->SKEYID_Hash->HashSize, tmp_buf->Buf, tmp_buf->Size);
|
|
FreeBuf(tmp_buf);
|
|
|
|
// Return the hash #3
|
|
send_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, hash3, ipsec_sa_cs->SKEYID_Hash->HashSize);
|
|
|
|
payload_list = NewListSingle(send_hash_payload);
|
|
ps = IkeNew(ike_sa->InitiatorCookie, ike_sa->ResponderCookie,
|
|
IKE_EXCHANGE_TYPE_QUICK, true, false, false, header->MessageId, payload_list);
|
|
|
|
IPsecSaSendPacket(ike, ipsec_sa_sc, ps);
|
|
#ifdef RAW_DEBUG
|
|
IkeDebugUdpSendRawPacket(ps);
|
|
#endif // RAW_DEBUG
|
|
ipsec_sa_sc->NumResends = 3;
|
|
|
|
if (false)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa != ipsec_sa_sc && sa != ipsec_sa_cs)
|
|
{
|
|
MarkIPsecSaAsDeleted(ike, sa);
|
|
}
|
|
}
|
|
}
|
|
|
|
IkeFree(ps);
|
|
|
|
// Release the memory
|
|
FreeBuf(responder_rand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No appropriate transform setting
|
|
Debug("No Appropriate Transform was Found.\n");
|
|
|
|
IPsecLog(ike, NULL, ike_sa, NULL, "LI_IPSEC_NO_TRANSFORM");
|
|
|
|
SendInformationalExchangePacket(ike, c, IkeNewNoticeErrorNoProposalChosenPayload(true, header->InitiatorCookie, header->ResponderCookie));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
IkeFree(pr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the KEYMAT
|
|
void IPsecCalcKeymat(IKE_SERVER *ike, IKE_HASH *h, void *dst, UINT dst_size, void *skeyid_d_data, UINT skeyid_d_size, UCHAR protocol, UINT spi, void *rand_init_data, UINT rand_init_size,
|
|
void *rand_resp_data, UINT rand_resp_size, void *df_key_data, UINT df_key_size)
|
|
{
|
|
BUF *k;
|
|
BUF *ret;
|
|
// Validate arguments
|
|
if (ike == NULL || dst == NULL || h == NULL || rand_init_data == NULL || rand_resp_data == NULL||
|
|
(df_key_size != 0 && df_key_data == NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ret = NewBuf();
|
|
|
|
k = NULL;
|
|
|
|
while (true)
|
|
{
|
|
BUF *tmp = NewBuf();
|
|
UCHAR hash[IKE_MAX_HASH_SIZE];
|
|
|
|
if (k != NULL)
|
|
{
|
|
WriteBufBuf(tmp, k);
|
|
}
|
|
|
|
if (df_key_data != NULL)
|
|
{
|
|
WriteBuf(tmp, df_key_data, df_key_size);
|
|
}
|
|
|
|
WriteBuf(tmp, &protocol, 1);
|
|
|
|
WriteBufInt(tmp, spi);
|
|
|
|
WriteBuf(tmp, rand_init_data, rand_init_size);
|
|
WriteBuf(tmp, rand_resp_data, rand_resp_size);
|
|
|
|
if (k != NULL)
|
|
{
|
|
FreeBuf(k);
|
|
}
|
|
|
|
IkeHMac(h, hash, skeyid_d_data, skeyid_d_size, tmp->Buf, tmp->Size);
|
|
|
|
FreeBuf(tmp);
|
|
|
|
k = MemToBuf(hash, h->HashSize);
|
|
|
|
WriteBufBuf(ret, k);
|
|
|
|
if (ret->Size >= dst_size)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Copy(dst, ret->Buf, dst_size);
|
|
|
|
FreeBuf(ret);
|
|
FreeBuf(k);
|
|
}
|
|
|
|
// Search for IPsec SA from Message ID
|
|
IPSECSA *SearchIPsecSaByMessageId(IKE_SERVER *ike, IKE_CLIENT *c, UINT message_id)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || message_id == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa->IkeClient == c)
|
|
{
|
|
if (sa->MessageId == message_id)
|
|
{
|
|
if (sa->ServerToClient == false)
|
|
{
|
|
if (sa->Established == false)
|
|
{
|
|
return sa;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Search for IPsec SA from SPI value
|
|
IPSECSA *SearchClientToServerIPsecSaBySpi(IKE_SERVER *ike, UINT spi)
|
|
{
|
|
IPSECSA t;
|
|
// Validate arguments
|
|
if (ike == NULL || spi == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
t.ServerToClient = false;
|
|
t.Spi = spi;
|
|
|
|
return Search(ike->IPsecSaList, &t);
|
|
}
|
|
IPSECSA *SearchIPsecSaBySpi(IKE_SERVER *ike, IKE_CLIENT *c, UINT spi)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || spi == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa->Spi == spi)
|
|
{
|
|
if (sa->IkeClient == c)
|
|
{
|
|
return sa;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Search an IKE SA from the value of the Cookie
|
|
IKE_SA *SearchIkeSaByCookie(IKE_SERVER *ike, UINT64 init_cookie, UINT64 resp_cookie)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
if (sa->InitiatorCookie == init_cookie && sa->ResponderCookie == resp_cookie)
|
|
{
|
|
return sa;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Generate the SPI value of new IPsec SA
|
|
UINT GenerateNewIPsecSaSpi(IKE_SERVER *ike, UINT counterpart_spi)
|
|
{
|
|
UINT ret;
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
ret = Rand32();
|
|
|
|
if (ret != counterpart_spi)
|
|
{
|
|
if (ret >= 4096 && ret != INFINITE)
|
|
{
|
|
if (SearchClientToServerIPsecSaBySpi(ike, ret) == NULL)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the initial IV for Phase 2
|
|
void IkeCalcPhase2InitialIv(void *iv, IKE_SA *sa, UINT message_id)
|
|
{
|
|
BUF *b;
|
|
UCHAR hash[IKE_MAX_HASH_SIZE];
|
|
// Validate arguments
|
|
if (iv == NULL || sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
message_id = Endian32(message_id);
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, sa->Iv, sa->BlockSize);
|
|
WriteBuf(b, &message_id, sizeof(UINT));
|
|
|
|
IkeHash(sa->TransformSetting.Hash, hash, b->Buf, b->Size);
|
|
|
|
Copy(iv, hash, sa->TransformSetting.Crypto->BlockSize);
|
|
|
|
FreeBuf(b);
|
|
}
|
|
|
|
// Create a new IPsec SA
|
|
IPSECSA *NewIPsecSa(IKE_SERVER *ike, IKE_CLIENT *c, IKE_SA *ike_sa, bool initiate, UINT message_id, bool server_to_client, void *iv, UINT spi, void *init_rand_data, UINT init_rand_size, void *res_rand_data, UINT res_rand_size, IPSEC_SA_TRANSFORM_SETTING *setting, void *shared_key_data, UINT shared_key_size)
|
|
{
|
|
IPSECSA *sa;
|
|
char tmp[MAX_SIZE];
|
|
UINT total_key_size;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || ike_sa == NULL || message_id == 0 || iv == NULL || setting == NULL ||
|
|
(shared_key_data == NULL && shared_key_size != 0))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
sa = ZeroMalloc(sizeof(IPSECSA));
|
|
|
|
if (server_to_client == false)
|
|
{
|
|
ike->CurrentIPsecSaId++;
|
|
}
|
|
sa->Id = ike->CurrentIPsecSaId;
|
|
|
|
sa->IkeClient = c;
|
|
sa->IkeSa = ike_sa;
|
|
|
|
sa->MessageId = message_id;
|
|
sa->FirstCommTick = ike->Now;
|
|
sa->LastCommTick = ike->Now;
|
|
sa->Initiated = initiate;
|
|
|
|
sa->ServerToClient = server_to_client;
|
|
|
|
sa->Spi = spi;
|
|
|
|
sa->SKEYID_Hash = ike_sa->TransformSetting.Hash;
|
|
Copy(sa->SKEYID_a, ike_sa->SKEYID_a, sa->SKEYID_Hash->HashSize);
|
|
Copy(sa->SKEYID_d, ike_sa->SKEYID_d, sa->SKEYID_Hash->HashSize);
|
|
|
|
sa->InitiatorRand = MemToBuf(init_rand_data, init_rand_size);
|
|
|
|
if (initiate == false)
|
|
{
|
|
sa->ResponderRand = MemToBuf(res_rand_data, res_rand_size);
|
|
}
|
|
|
|
Copy(sa->Iv, iv, ike_sa->BlockSize);
|
|
|
|
Copy(&sa->TransformSetting, setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
|
|
if (shared_key_data != NULL)
|
|
{
|
|
sa->SharedKey = MemToBuf(shared_key_data, shared_key_size);
|
|
}
|
|
|
|
total_key_size = sa->TransformSetting.CryptoKeySize + sa->TransformSetting.Hash->HashSize;
|
|
|
|
if (initiate == false)
|
|
{
|
|
IPsecCalcKeymat(ike, ike_sa->TransformSetting.Hash, sa->KeyMat, total_key_size,
|
|
ike_sa->SKEYID_d, ike_sa->HashSize, IKE_PROTOCOL_ID_IPSEC_ESP, spi, sa->InitiatorRand->Buf,
|
|
sa->InitiatorRand->Size, sa->ResponderRand->Buf, sa->ResponderRand->Size,
|
|
shared_key_data, shared_key_size);
|
|
|
|
sa->CryptoKey = IkeNewKey(sa->TransformSetting.Crypto, sa->KeyMat, sa->TransformSetting.CryptoKeySize);
|
|
|
|
Copy(sa->HashKey, sa->KeyMat + sa->TransformSetting.CryptoKeySize, sa->TransformSetting.Hash->HashSize);
|
|
}
|
|
|
|
Debug("New IPsec SA (StoC = %u): 0x%X 0x%X (%s %s %s(%u) %u %u)\n",
|
|
sa->ServerToClient,
|
|
sa->MessageId,
|
|
sa->Spi,
|
|
(setting->Dh == NULL ? NULL : setting->Dh->Name), setting->Hash->Name, setting->Crypto->Name, setting->CryptoKeySize,
|
|
setting->LifeKilobytes, setting->LifeSeconds);
|
|
|
|
IPsecLog(ike, c, NULL, sa, "LI_NEW_IPSEC_SA",
|
|
(sa->ServerToClient ? _UU("LI_TAG_SERVER_TO_CLIENT") : _UU("LI_TAG_CLIENT_TO_SERVER")),
|
|
sa->Spi,
|
|
(setting->Dh == NULL ? NULL : setting->Dh->Name), setting->Hash->Name, setting->Crypto->Name, setting->CryptoKeySize * 8,
|
|
setting->LifeKilobytes, setting->LifeSeconds);
|
|
|
|
Rand(sa->EspIv, sizeof(sa->EspIv));
|
|
|
|
if (initiate == false)
|
|
{
|
|
BinToStrEx(tmp, sizeof(tmp), sa->KeyMat, sa->TransformSetting.CryptoKeySize);
|
|
Debug(" KEYMAT: %s\n", tmp);
|
|
}
|
|
|
|
// Set the expiration time
|
|
if (setting->LifeSeconds != 0)
|
|
{
|
|
const UINT64 span = (UINT64)((UINT64)setting->LifeSeconds * (UINT64)1000) + (UINT64)IKE_SOFT_EXPIRES_MARGIN;
|
|
sa->ExpiresHardTick = ike->Now + span;
|
|
sa->ExpiresSoftTick = ike->Now + span;
|
|
//sa->ExpiresSoftTick = ike->Now + (UINT64)5000;
|
|
|
|
AddInterrupt(ike->Interrupts, sa->ExpiresSoftTick);
|
|
}
|
|
|
|
return sa;
|
|
}
|
|
|
|
// Treat aggressive mode packet reception
|
|
void ProcIkeAggressiveModePacketRecv(IKE_SERVER *ike, UDPPACKET *p, IKE_PACKET *header)
|
|
{
|
|
IKE_CLIENT *c;
|
|
char tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (ike == NULL || p == NULL || header == NULL || header->InitiatorCookie == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c = SearchOrCreateNewIkeClientForIkePacket(ike, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort, header);
|
|
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (header->ResponderCookie == 0)
|
|
{
|
|
// Start process of the state 1
|
|
IKE_CAPS caps;
|
|
IKE_SA *sa;
|
|
IKE_PACKET *pr = IkeParse(p->Data, p->Size, NULL);
|
|
|
|
if (pr != NULL)
|
|
{
|
|
// Determine the CAPS
|
|
IkeCheckCaps(&caps, pr);
|
|
if (caps.MS_L2TPIPSecVPNClient || caps.MS_NT5_ISAKMP_OAKLEY || caps.MS_Vid_InitialContact)
|
|
{
|
|
c->IsMicrosoft = true;
|
|
}
|
|
|
|
if ((caps.NatTraversalDraftIetf || caps.NatTraversalRfc3947) || (IsUdpPortOpened(ike->IPsec->UdpListener, &p->DstIP, IPSEC_PORT_IPSEC_ESP_RAW)))
|
|
{
|
|
sa = FindIkeSaByEndPointAndInitiatorCookie(ike, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, header->InitiatorCookie, IKE_SA_AGGRESSIVE_MODE);
|
|
|
|
if (sa == NULL)
|
|
{
|
|
// Check whether there is acceptable SA parameters by analyzing proposed parameters
|
|
IKE_SA_TRANSFORM_SETTING setting;
|
|
|
|
if (GetBestTransformSettingForIkeSa(ike, pr, &setting) && (GetNumberOfIkeSaOfIkeClient(ike, c) <= IKE_QUOTA_MAX_SA_PER_CLIENT))
|
|
{
|
|
IKE_PACKET_PAYLOAD *tp;
|
|
IKE_PACKET_PAYLOAD *pp;
|
|
IKE_PACKET_PAYLOAD *sap;
|
|
IKE_PACKET_PAYLOAD *client_sa_payload;
|
|
IKE_PACKET_PAYLOAD *your_key_payload;
|
|
IKE_PACKET_PAYLOAD *your_rand_payload;
|
|
IKE_PACKET_PAYLOAD *your_id_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 0);
|
|
|
|
// Appropriate transform setting is selected
|
|
Debug("P1 Transform: %s %s %s(%u) %u %u\n",
|
|
setting.Dh->Name, setting.Hash->Name, setting.Crypto->Name, setting.CryptoKeySize,
|
|
setting.LifeKilobytes, setting.LifeSeconds);
|
|
|
|
// Receive a key exchange packet
|
|
your_key_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_KEY_EXCHANGE, 0);
|
|
your_rand_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_RAND, 0);
|
|
if (your_key_payload != NULL && your_rand_payload != NULL && your_id_payload != NULL)
|
|
{
|
|
// Check the key payload
|
|
BUF *your_key_buf = your_key_payload->Payload.KeyExchange.Data;
|
|
BUF *your_rand_buf = your_rand_payload->Payload.Rand.Data;
|
|
|
|
// DH generation
|
|
DH_CTX *dh = IkeDhNewCtx(setting.Dh);
|
|
UINT shared_key_size = (dh == NULL ? 0 : dh->Size);
|
|
UCHAR *shared_key = ZeroMalloc(shared_key_size);
|
|
|
|
// DH calculation
|
|
if (DhCompute(dh, shared_key, your_key_buf->Buf, your_key_buf->Size))
|
|
{
|
|
IKE_PACKET *ps;
|
|
LIST *payload_list;
|
|
IKE_PACKET_PAYLOAD *my_key_payload;
|
|
IKE_PACKET_PAYLOAD *my_rand_payload;
|
|
BUF *nat_buf1, *nat_buf2;
|
|
BUF *iv_buf;
|
|
UCHAR iv_hashed_data[IKE_MAX_HASH_SIZE];
|
|
UCHAR initiator_hash[IKE_MAX_HASH_SIZE];
|
|
BUF *b;
|
|
IKE_PACKET_PAYLOAD *my_id_payload, *my_hash_payload;
|
|
UCHAR responder_hash[IKE_MAX_HASH_SIZE];
|
|
BUF *idir_b;
|
|
IKE_PACKET_PAYLOAD *your_nat_d_1 = NULL;
|
|
IKE_PACKET_PAYLOAD *your_nat_d_2 = NULL;
|
|
|
|
// Create an IKE SA
|
|
sa = NewIkeSa(ike, c, header->InitiatorCookie, IKE_SA_AGGRESSIVE_MODE, &setting);
|
|
Copy(&sa->Caps, &caps, sizeof(IKE_CAPS));
|
|
sa->State= IKE_SA_AM_STATE_1_SA;
|
|
Insert(ike->IkeSaList, sa);
|
|
|
|
sa->HashSize = sa->TransformSetting.Hash->HashSize;
|
|
sa->KeySize = sa->TransformSetting.CryptoKeySize;
|
|
sa->BlockSize = sa->TransformSetting.Crypto->BlockSize;
|
|
|
|
// Get the Caps additionally
|
|
if (sa->Caps.NatTraversalRfc3947)
|
|
{
|
|
sa->Caps.UsingNatTraversalRfc3947 = true;
|
|
|
|
your_nat_d_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D, 0);
|
|
your_nat_d_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D, 1);
|
|
}
|
|
else if (sa->Caps.NatTraversalDraftIetf)
|
|
{
|
|
sa->Caps.UsingNatTraversalDraftIetf = true;
|
|
|
|
your_nat_d_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT, 0);
|
|
your_nat_d_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT, 1);
|
|
}
|
|
|
|
// Calculation success
|
|
sa->DhSharedKey = MemToBuf(shared_key, shared_key_size);
|
|
sa->InitiatorRand = RandBuf(IKE_SA_RAND_SIZE);
|
|
sa->ResponderRand = CloneBuf(your_rand_buf);
|
|
|
|
// Save a bit array of SA payload presented by the client
|
|
client_sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
|
|
sa->SAi_b = CloneBuf(client_sa_payload->BitArray);
|
|
|
|
// Save the ID payload presented by the client
|
|
sa->YourIDPayloadForAM = CloneBuf(your_id_payload->BitArray);
|
|
|
|
//// Assemble the SA payload
|
|
// Construct transform payload
|
|
tp = TransformSettingToTransformPayloadForIke(ike, &setting);
|
|
|
|
// Build a proposal payload
|
|
pp = IkeNewProposalPayload(1, IKE_PROTOCOL_ID_IKE, NULL, 0, NewListSingle(tp));
|
|
|
|
// Build the SA payload
|
|
sap = IkeNewSaPayload(NewListSingle(pp));
|
|
|
|
payload_list = NewListSingle(sap);
|
|
|
|
// Send a key exchange packet
|
|
my_key_payload = IkeNewDataPayload(IKE_PAYLOAD_KEY_EXCHANGE, dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
|
|
my_rand_payload = IkeNewDataPayload(IKE_PAYLOAD_RAND, sa->InitiatorRand->Buf, sa->InitiatorRand->Size);
|
|
|
|
Add(payload_list, my_key_payload);
|
|
Add(payload_list, my_rand_payload);
|
|
|
|
// NAT-D Packet
|
|
// Address of the opponent. Randomize in order to be forced to use NAT
|
|
nat_buf1 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash, Rand64(), Rand64(), &c->ClientIP, Rand16());
|
|
|
|
// My address
|
|
if (c->IsMicrosoft == false || (your_nat_d_1 == NULL || your_nat_d_2 == NULL || your_nat_d_1->BitArray == NULL))
|
|
{
|
|
// Calculate exactly
|
|
nat_buf2 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash,
|
|
sa->InitiatorCookie, sa->ResponderCookie, &c->ServerIP, c->ServerPort);
|
|
}
|
|
else
|
|
{
|
|
// Parrot the NAT_D payload indicating myself I got from
|
|
// the other if it has connected from a Microsoft VPN Client
|
|
nat_buf2 = CloneBuf(your_nat_d_1->BitArray);
|
|
}
|
|
|
|
// Save DH information
|
|
sa->GXi = CloneBuf(your_key_buf);
|
|
sa->GXr = CloneBuf(dh->MyPublicKey);
|
|
|
|
// Calculate the key set
|
|
IkeCalcSaKeySet(ike, sa, NULL);
|
|
|
|
// Calculate the initiator side hash value
|
|
b = NewBuf();
|
|
WriteBufBuf(b, sa->GXi);
|
|
WriteBufBuf(b, sa->GXr);
|
|
WriteBufInt64(b, sa->InitiatorCookie);
|
|
WriteBufInt64(b, sa->ResponderCookie);
|
|
WriteBufBuf(b, sa->SAi_b);
|
|
WriteBufBuf(b, sa->YourIDPayloadForAM);
|
|
|
|
IkeHMac(sa->TransformSetting.Hash, initiator_hash, sa->SKEYID, sa->HashSize,
|
|
b->Buf, b->Size);
|
|
|
|
FreeBuf(b);
|
|
|
|
Copy(sa->InitiatorHashForAM, initiator_hash, sa->HashSize);
|
|
|
|
// Prepare the response ID payload
|
|
// Generate the ID payload
|
|
if (IsIP6(&sa->IkeClient->ServerIP))
|
|
{
|
|
// IPv6 address
|
|
my_id_payload = IkeNewIdPayload(IKE_ID_IPV6_ADDR, 0, 0, sa->IkeClient->ServerIP.address, 16);
|
|
}
|
|
else
|
|
{
|
|
// IPv4 address
|
|
my_id_payload = IkeNewIdPayload(IKE_ID_IPV4_ADDR, 0, 0, IPV4(sa->IkeClient->ServerIP.address), IPV4_SIZE);
|
|
}
|
|
|
|
// Build the ID payload tentatively
|
|
idir_b = IkeBuildIdPayload(&my_id_payload->Payload.Id);
|
|
|
|
b = NewBuf();
|
|
WriteBufBuf(b, sa->GXr);
|
|
WriteBufBuf(b, sa->GXi);
|
|
WriteBufInt64(b, sa->ResponderCookie);
|
|
WriteBufInt64(b, sa->InitiatorCookie);
|
|
WriteBufBuf(b, sa->SAi_b);
|
|
WriteBufBuf(b, idir_b);
|
|
|
|
IkeHMac(sa->TransformSetting.Hash, responder_hash, sa->SKEYID, sa->HashSize,
|
|
b->Buf, b->Size);
|
|
|
|
FreeBuf(b);
|
|
FreeBuf(idir_b);
|
|
|
|
my_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, responder_hash, sa->HashSize);
|
|
|
|
Add(payload_list, my_id_payload);
|
|
Add(payload_list, my_hash_payload);
|
|
|
|
ps = IkeNew(sa->InitiatorCookie, sa->ResponderCookie, IKE_EXCHANGE_TYPE_AGGRESSIVE,
|
|
false, false, false, 0, payload_list);
|
|
|
|
// Add the vendor ID
|
|
IkeAddVendorIdPayloads(ps);
|
|
|
|
// NAT-D related
|
|
if (sa->Caps.UsingNatTraversalRfc3947)
|
|
{
|
|
// RFC-compliant
|
|
Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D, nat_buf1->Buf, nat_buf1->Size));
|
|
Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D, nat_buf2->Buf, nat_buf2->Size));
|
|
}
|
|
|
|
if (sa->Caps.UsingNatTraversalDraftIetf)
|
|
{
|
|
// Draft compliant
|
|
Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D_DRAFT, nat_buf1->Buf, nat_buf1->Size));
|
|
Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D_DRAFT, nat_buf2->Buf, nat_buf2->Size));
|
|
}
|
|
|
|
FreeBuf(nat_buf1);
|
|
FreeBuf(nat_buf2);
|
|
|
|
StrCpy(c->ClientId, sizeof(c->ClientId), your_id_payload->Payload.Id.StrData);
|
|
Debug("Client ID = %s\n", c->ClientId);
|
|
|
|
IPsecLog(ike, c, NULL, NULL, NULL, "LI_SET_CLIENT_ID", c->ClientId);
|
|
|
|
// Initial IV setting
|
|
iv_buf = NewBuf();
|
|
WriteBuf(iv_buf, your_key_buf->Buf, your_key_buf->Size);
|
|
WriteBuf(iv_buf, dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
|
|
IkeHash(sa->TransformSetting.Hash, iv_hashed_data, iv_buf->Buf, iv_buf->Size);
|
|
|
|
BinToStrEx(tmp, sizeof(tmp), iv_hashed_data, sa->BlockSize);
|
|
Debug("Initial IV: %s\n", tmp);
|
|
|
|
IkeSaUpdateIv(sa, iv_hashed_data, sa->HashSize);
|
|
|
|
FreeBuf(iv_buf);
|
|
|
|
// Transmission
|
|
IkeSaSendPacket(ike, sa, ps);
|
|
|
|
IkeFree(ps);
|
|
}
|
|
else
|
|
{
|
|
// DH calculation failure
|
|
Debug("DhCompute failed.\n");
|
|
}
|
|
|
|
Free(shared_key);
|
|
DhFree(dh);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No appropriate transform setting
|
|
Debug("No Appropriate Transform was Found.\n");
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_IKE_NO_TRANSFORM");
|
|
|
|
SendInformationalExchangePacket(ike, c, IkeNewNoticeErrorNoProposalChosenPayload(false, header->InitiatorCookie, header->ResponderCookie));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Client does not support NAT Traversal
|
|
Debug("Client doesn't support NAT-T.\n");
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_IKE_NO_NAT_T");
|
|
}
|
|
|
|
IkeFree(pr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Process of state 2
|
|
IKE_SA *sa;
|
|
|
|
sa = FindIkeSaByResponderCookieAndClient(ike, header->ResponderCookie, c);
|
|
|
|
if (sa == NULL)
|
|
{
|
|
SendInformationalExchangePacketEx(ike, c, IkeNewNoticeErrorInvalidCookiePayload(header->InitiatorCookie,
|
|
header->ResponderCookie), true, header->InitiatorCookie, header->ResponderCookie);
|
|
}
|
|
|
|
if (sa != NULL && sa->Mode == IKE_SA_AGGRESSIVE_MODE)
|
|
{
|
|
IKE_PACKET *pr = NULL;
|
|
|
|
sa->LastCommTick = ike->Now;
|
|
|
|
switch (sa->State)
|
|
{
|
|
case IKE_SA_AM_STATE_1_SA:
|
|
pr = IkeSaRecvPacket(ike, sa, p->Data, p->Size);
|
|
if (pr != NULL)
|
|
{
|
|
IKE_PACKET_PAYLOAD *your_hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
|
|
|
|
if (your_hash_payload != NULL)
|
|
{
|
|
// Compare the hash
|
|
if (IkeCompareHash(your_hash_payload, sa->InitiatorHashForAM, sa->HashSize))
|
|
{
|
|
// Transit to the established state
|
|
Debug("IKE SA 0x%X Established.\n", sa);
|
|
sa->State = IKE_SA_AM_STATE_2_ESTABLISHED;
|
|
sa->EstablishedTick = ike->Now;
|
|
sa->Established = true;
|
|
c->CurrentIkeSa = sa;
|
|
c->NextDpdSendTick = ike->Now + (UINT64)IKE_INTERVAL_DPD_KEEPALIVE;
|
|
StrCpy(c->Secret, sizeof(c->Secret), sa->Secret);
|
|
|
|
IPsecLog(ike, NULL, sa, NULL, "LI_IKE_SA_ESTABLISHED");
|
|
|
|
IkeSaSendPacket(ike, sa, NULL);
|
|
}
|
|
else
|
|
{
|
|
Debug("IKE SA 0x%X Invalid Hash.\n", sa);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (pr != NULL)
|
|
{
|
|
IkeFree(pr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process of the main mode packet reception
|
|
void ProcIkeMainModePacketRecv(IKE_SERVER *ike, UDPPACKET *p, IKE_PACKET *header)
|
|
{
|
|
IKE_CLIENT *c;
|
|
char tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (ike == NULL || p == NULL || header == NULL || header->InitiatorCookie == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c = SearchOrCreateNewIkeClientForIkePacket(ike, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort, header);
|
|
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (header->ResponderCookie == 0)
|
|
{
|
|
// Start process of the state 1
|
|
IKE_CAPS caps;
|
|
IKE_SA *sa;
|
|
IKE_PACKET *pr = IkeParse(p->Data, p->Size, NULL);
|
|
|
|
if (pr != NULL)
|
|
{
|
|
// Determine the CAPS
|
|
IkeCheckCaps(&caps, pr);
|
|
if (caps.MS_L2TPIPSecVPNClient || caps.MS_NT5_ISAKMP_OAKLEY || caps.MS_Vid_InitialContact)
|
|
{
|
|
c->IsMicrosoft = true;
|
|
}
|
|
|
|
if ((caps.NatTraversalDraftIetf || caps.NatTraversalRfc3947) || (IsUdpPortOpened(ike->IPsec->UdpListener, &p->DstIP, IPSEC_PORT_IPSEC_ESP_RAW)))
|
|
{
|
|
sa = FindIkeSaByEndPointAndInitiatorCookie(ike, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, header->InitiatorCookie, IKE_SA_MAIN_MODE);
|
|
|
|
if (sa == NULL)
|
|
{
|
|
// Check whether there is acceptable SA parameters by analyzing proposed parameters
|
|
IKE_SA_TRANSFORM_SETTING setting;
|
|
|
|
if (GetBestTransformSettingForIkeSa(ike, pr, &setting) && (GetNumberOfIkeSaOfIkeClient(ike, c) <= IKE_QUOTA_MAX_SA_PER_CLIENT))
|
|
{
|
|
IKE_PACKET *ps;
|
|
IKE_PACKET_PAYLOAD *tp;
|
|
IKE_PACKET_PAYLOAD *pp;
|
|
IKE_PACKET_PAYLOAD *sap;
|
|
LIST *payload_list;
|
|
IKE_PACKET_PAYLOAD *client_sa_payload;
|
|
|
|
// Appropriate transform setting is selected
|
|
Debug("P1 Transform: %s %s %s(%u) %u %u\n",
|
|
setting.Dh->Name, setting.Hash->Name, setting.Crypto->Name, setting.CryptoKeySize,
|
|
setting.LifeKilobytes, setting.LifeSeconds);
|
|
|
|
#ifdef FORCE_LIFETIME_MM
|
|
setting.LifeSeconds = FORCE_LIFETIME_MM;
|
|
#endif // FORCE_LIFETIME_MM
|
|
|
|
// Create an IKE SA
|
|
sa = NewIkeSa(ike, c, header->InitiatorCookie, IKE_SA_MAIN_MODE, &setting);
|
|
|
|
Copy(&sa->Caps, &caps, sizeof(IKE_CAPS));
|
|
|
|
Insert(ike->IkeSaList, sa);
|
|
|
|
// Answer the SA parameter selection results
|
|
sa->State = IKE_SA_MM_STATE_1_SA;
|
|
|
|
// Save a bit array of SA payload presented by the client
|
|
client_sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
|
|
sa->SAi_b = CloneBuf(client_sa_payload->BitArray);
|
|
|
|
//// Assemble the SA payload
|
|
// Construct a transform payload
|
|
tp = TransformSettingToTransformPayloadForIke(ike, &setting);
|
|
|
|
// Build a proposal payload
|
|
pp = IkeNewProposalPayload(1, IKE_PROTOCOL_ID_IKE, NULL, 0, NewListSingle(tp));
|
|
|
|
// Build a SA payload
|
|
sap = IkeNewSaPayload(NewListSingle(pp));
|
|
|
|
payload_list = NewListSingle(sap);
|
|
|
|
ps = IkeNew(sa->InitiatorCookie, sa->ResponderCookie, IKE_EXCHANGE_TYPE_MAIN,
|
|
false, false, false, 0, payload_list);
|
|
|
|
// Add the vendor ID payload
|
|
IkeAddVendorIdPayloads(ps);
|
|
|
|
IkeSaSendPacket(ike, sa, ps);
|
|
|
|
sa->HashSize = sa->TransformSetting.Hash->HashSize;
|
|
sa->KeySize = sa->TransformSetting.CryptoKeySize;
|
|
sa->BlockSize = sa->TransformSetting.Crypto->BlockSize;
|
|
|
|
IkeFree(ps);
|
|
}
|
|
else
|
|
{
|
|
// No appropriate transform setting
|
|
Debug("No Appropriate Transform was Found.\n");
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_IKE_NO_TRANSFORM");
|
|
|
|
SendInformationalExchangePacket(ike, c, IkeNewNoticeErrorNoProposalChosenPayload(false, header->InitiatorCookie, header->ResponderCookie));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Ignore for IKE SA which already exists (Because it's likely to be a re-transmission)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// It does not support NAT Traversal
|
|
Debug("Client doesn't support NAT-T.\n");
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_IKE_NO_NAT_T");
|
|
}
|
|
IkeFree(pr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Process of state 2 or later
|
|
IKE_SA *sa;
|
|
|
|
sa = FindIkeSaByResponderCookieAndClient(ike, header->ResponderCookie, c);
|
|
|
|
if (sa == NULL)
|
|
{
|
|
SendInformationalExchangePacketEx(ike, c, IkeNewNoticeErrorInvalidCookiePayload(header->InitiatorCookie,
|
|
header->ResponderCookie), true, header->InitiatorCookie, header->ResponderCookie);
|
|
}
|
|
|
|
if (sa != NULL && sa->Mode == IKE_SA_MAIN_MODE)
|
|
{
|
|
IKE_PACKET *pr = NULL;
|
|
|
|
sa->LastCommTick = ike->Now;
|
|
|
|
switch (sa->State)
|
|
{
|
|
case IKE_SA_MM_STATE_1_SA:
|
|
pr = IkeSaRecvPacket(ike, sa, p->Data, p->Size);
|
|
if (pr != NULL)
|
|
{
|
|
// Receive a key exchange packet
|
|
IKE_PACKET_PAYLOAD *your_key_payload;
|
|
IKE_PACKET_PAYLOAD *your_rand_payload;
|
|
IKE_PACKET_PAYLOAD *your_nat_d_1 = NULL;
|
|
IKE_PACKET_PAYLOAD *your_nat_d_2 = NULL;
|
|
|
|
your_key_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_KEY_EXCHANGE, 0);
|
|
your_rand_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_RAND, 0);
|
|
|
|
if (IkeGetPayloadNum(pr->PayloadList, IKE_PAYLOAD_NAT_D) != 0)
|
|
{
|
|
sa->Caps.UsingNatTraversalRfc3947 = true;
|
|
|
|
your_nat_d_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D, 0);
|
|
your_nat_d_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D, 1);
|
|
}
|
|
|
|
if (IkeGetPayloadNum(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT) != 0)
|
|
{
|
|
sa->Caps.UsingNatTraversalDraftIetf = true;
|
|
|
|
your_nat_d_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT, 0);
|
|
your_nat_d_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT, 1);
|
|
}
|
|
|
|
if (your_key_payload != NULL && your_rand_payload != NULL)
|
|
{
|
|
// Check the key payload
|
|
BUF *your_key_buf = your_key_payload->Payload.KeyExchange.Data;
|
|
BUF *your_rand_buf = your_rand_payload->Payload.Rand.Data;
|
|
|
|
// DH generation
|
|
DH_CTX *dh = IkeDhNewCtx(sa->TransformSetting.Dh);
|
|
UINT shared_key_size = (dh == NULL ? 0 : dh->Size);
|
|
UCHAR *shared_key = ZeroMalloc(shared_key_size);
|
|
|
|
// DH calculation
|
|
if (DhCompute(dh, shared_key, your_key_buf->Buf, your_key_buf->Size))
|
|
{
|
|
IKE_PACKET *ps;
|
|
LIST *payload_list;
|
|
IKE_PACKET_PAYLOAD *my_key_payload;
|
|
IKE_PACKET_PAYLOAD *my_rand_payload;
|
|
BUF *nat_buf1, *nat_buf2;
|
|
BUF *iv_buf;
|
|
UCHAR iv_hashed_data[IKE_MAX_HASH_SIZE];
|
|
|
|
// Calculation success
|
|
sa->DhSharedKey = MemToBuf(shared_key, shared_key_size);
|
|
sa->InitiatorRand = RandBuf(IKE_SA_RAND_SIZE);
|
|
sa->ResponderRand = CloneBuf(your_rand_buf);
|
|
|
|
// Send a key exchange packet
|
|
my_key_payload = IkeNewDataPayload(IKE_PAYLOAD_KEY_EXCHANGE, dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
|
|
my_rand_payload = IkeNewDataPayload(IKE_PAYLOAD_RAND, sa->InitiatorRand->Buf, sa->InitiatorRand->Size);
|
|
|
|
payload_list = NewListSingle(my_key_payload);
|
|
Add(payload_list, my_rand_payload);
|
|
|
|
// NAT-D packet
|
|
// Address of the opponent. Randomize in order to be forced to use NAT
|
|
nat_buf1 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash, Rand64(), Rand64(), &c->ClientIP, Rand16());
|
|
//nat_buf1 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash, sa->InitiatorCookie, sa->ResponderCookie, &c->ClientIP, c->ClientPort);
|
|
// My address
|
|
|
|
if (c->IsMicrosoft == false || (your_nat_d_1 == NULL || your_nat_d_2 == NULL || your_nat_d_1->BitArray == NULL))
|
|
{
|
|
// Calculate exactly
|
|
nat_buf2 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash,
|
|
sa->InitiatorCookie, sa->ResponderCookie, &c->ServerIP, c->ServerPort);
|
|
}
|
|
else
|
|
{
|
|
// Parrot the NAT_D payload indicating myself I got from
|
|
// the other if it has connected from a Microsoft VPN Client
|
|
nat_buf2 = CloneBuf(your_nat_d_1->BitArray);
|
|
}
|
|
|
|
if (sa->Caps.UsingNatTraversalRfc3947)
|
|
{
|
|
// RFC-compliant
|
|
Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D, nat_buf1->Buf, nat_buf1->Size));
|
|
Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D, nat_buf2->Buf, nat_buf2->Size));
|
|
}
|
|
|
|
if (sa->Caps.UsingNatTraversalDraftIetf)
|
|
{
|
|
// Draft compliant
|
|
Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D_DRAFT, nat_buf1->Buf, nat_buf1->Size));
|
|
Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D_DRAFT, nat_buf2->Buf, nat_buf2->Size));
|
|
}
|
|
|
|
FreeBuf(nat_buf1);
|
|
FreeBuf(nat_buf2);
|
|
|
|
ps = IkeNew(sa->InitiatorCookie, sa->ResponderCookie, IKE_EXCHANGE_TYPE_MAIN,
|
|
false, false, false, 0, payload_list);
|
|
|
|
// Initial IV setting
|
|
iv_buf = NewBuf();
|
|
WriteBuf(iv_buf, your_key_buf->Buf, your_key_buf->Size);
|
|
WriteBuf(iv_buf, dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
|
|
IkeHash(sa->TransformSetting.Hash, iv_hashed_data, iv_buf->Buf, iv_buf->Size);
|
|
|
|
BinToStrEx(tmp, sizeof(tmp), iv_hashed_data, sa->BlockSize);
|
|
Debug("Initial IV: %s\n", tmp);
|
|
|
|
IkeSaUpdateIv(sa, iv_hashed_data, sa->HashSize);
|
|
|
|
FreeBuf(iv_buf);
|
|
|
|
// Save the DH information
|
|
sa->GXi = CloneBuf(your_key_buf);
|
|
sa->GXr = CloneBuf(dh->MyPublicKey);
|
|
|
|
// Transmission
|
|
IkeSaSendPacket(ike, sa, ps);
|
|
|
|
IkeFree(ps);
|
|
|
|
// Calculate the key set
|
|
IkeCalcSaKeySet(ike, sa, NULL);
|
|
|
|
sa->State = IKE_SA_MM_STATE_2_KEY;
|
|
}
|
|
else
|
|
{
|
|
// DH calculation failure
|
|
Debug("DhCompute failed.\n");
|
|
}
|
|
|
|
Free(shared_key);
|
|
DhFree(dh);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IKE_SA_MM_STATE_2_KEY:
|
|
pr = IkeSaRecvPacket(ike, sa, p->Data, p->Size);
|
|
if (pr != NULL && pr->FlagEncrypted)
|
|
{
|
|
// Receive an ID exchange packet
|
|
IKE_PACKET_PAYLOAD *your_id_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 0);
|
|
IKE_PACKET_PAYLOAD *your_hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
|
|
|
|
if (your_id_payload && your_hash_payload)
|
|
{
|
|
UCHAR initiator_hash[IKE_MAX_HASH_SIZE];
|
|
BUF *b;
|
|
|
|
// Calculate the initiator side hash value
|
|
b = NewBuf();
|
|
WriteBufBuf(b, sa->GXi);
|
|
WriteBufBuf(b, sa->GXr);
|
|
WriteBufInt64(b, sa->InitiatorCookie);
|
|
WriteBufInt64(b, sa->ResponderCookie);
|
|
WriteBufBuf(b, sa->SAi_b);
|
|
WriteBufBuf(b, your_id_payload->BitArray);
|
|
|
|
StrCpy(c->ClientId, sizeof(c->ClientId), your_id_payload->Payload.Id.StrData);
|
|
Debug("Client ID = %s\n", c->ClientId);
|
|
IPsecLog(ike, c, NULL, NULL, NULL, "LI_SET_CLIENT_ID", c->ClientId);
|
|
|
|
IkeHMac(sa->TransformSetting.Hash, initiator_hash, sa->SKEYID, sa->HashSize,
|
|
b->Buf, b->Size);
|
|
|
|
FreeBuf(b);
|
|
|
|
// Hash comparison
|
|
if (IkeCompareHash(your_hash_payload, initiator_hash, sa->HashSize))
|
|
{
|
|
// Generate a response packet
|
|
IKE_PACKET *ps;
|
|
LIST *payload_list = NewListFast(NULL);
|
|
IKE_PACKET_PAYLOAD *my_id_payload, *my_hash_payload;
|
|
UCHAR responder_hash[IKE_MAX_HASH_SIZE];
|
|
BUF *idir_b;
|
|
|
|
// Generate an ID payload
|
|
if (IsIP6(&sa->IkeClient->ServerIP))
|
|
{
|
|
// IPv6 address
|
|
my_id_payload = IkeNewIdPayload(IKE_ID_IPV6_ADDR, 0, 0, sa->IkeClient->ServerIP.address, 16);
|
|
}
|
|
else
|
|
{
|
|
// IPv4 address
|
|
my_id_payload = IkeNewIdPayload(IKE_ID_IPV4_ADDR, 0, 0, IPV4(sa->IkeClient->ServerIP.address), IPV4_SIZE);
|
|
}
|
|
|
|
// Build the ID payload tentatively
|
|
idir_b = IkeBuildIdPayload(&my_id_payload->Payload.Id);
|
|
|
|
// Generate the hash payload
|
|
b = NewBuf();
|
|
WriteBufBuf(b, sa->GXr);
|
|
WriteBufBuf(b, sa->GXi);
|
|
WriteBufInt64(b, sa->ResponderCookie);
|
|
WriteBufInt64(b, sa->InitiatorCookie);
|
|
WriteBufBuf(b, sa->SAi_b);
|
|
WriteBufBuf(b, idir_b);
|
|
|
|
IkeHMac(sa->TransformSetting.Hash, responder_hash, sa->SKEYID, sa->HashSize,
|
|
b->Buf, b->Size);
|
|
|
|
FreeBuf(b);
|
|
FreeBuf(idir_b);
|
|
|
|
my_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, responder_hash, sa->HashSize);
|
|
|
|
Add(payload_list, my_id_payload);
|
|
Add(payload_list, my_hash_payload);
|
|
|
|
ps = IkeNew(sa->InitiatorCookie, sa->ResponderCookie, IKE_EXCHANGE_TYPE_MAIN, true, false,
|
|
false, 0, payload_list);
|
|
|
|
// Transmission
|
|
IkeSaSendPacket(ike, sa, ps);
|
|
sa->NumResends = 3;
|
|
|
|
IkeFree(ps);
|
|
|
|
StrCpy(c->ClientId, sizeof(c->ClientId), your_id_payload->Payload.Id.StrData);
|
|
|
|
// Transit to the established state
|
|
Debug("IKE SA 0x%X Established. Client ID=%s\n", sa, c->ClientId);
|
|
sa->State = IKE_SA_MM_STATE_3_ESTABLISHED;
|
|
sa->EstablishedTick = ike->Now;
|
|
c->CurrentIkeSa = sa;
|
|
c->NextDpdSendTick = ike->Now + (UINT64)IKE_INTERVAL_DPD_KEEPALIVE;
|
|
StrCpy(c->Secret, sizeof(c->Secret), sa->Secret);
|
|
sa->Established = true;
|
|
|
|
IPsecLog(ike, NULL, sa, NULL, "LI_IKE_SA_ESTABLISHED");
|
|
}
|
|
else
|
|
{
|
|
Debug("IKE SA 0x%X Invalid Hash.\n", sa);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (pr != NULL)
|
|
{
|
|
IkeFree(pr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the IV of IPsec SA
|
|
void IPsecSaUpdateIv(IPSECSA *sa, void *iv, UINT iv_size)
|
|
{
|
|
// Validate arguments
|
|
if (sa == NULL || iv == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(sa->Iv, iv, MIN(sa->IkeSa->BlockSize, iv_size));
|
|
|
|
if (iv_size < sa->IkeSa->BlockSize)
|
|
{
|
|
Zero(sa->Iv + sa->IkeSa->BlockSize, sa->IkeSa->BlockSize - iv_size);
|
|
}
|
|
|
|
sa->IsIvExisting = true;
|
|
}
|
|
|
|
// Update the IV of the IKE SA
|
|
void IkeSaUpdateIv(IKE_SA *sa, void *iv, UINT iv_size)
|
|
{
|
|
// Validate arguments
|
|
if (sa == NULL || iv == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(sa->Iv, iv, MIN(sa->BlockSize, iv_size));
|
|
|
|
if (iv_size < sa->BlockSize)
|
|
{
|
|
Zero(sa->Iv + sa->BlockSize, sa->BlockSize - iv_size);
|
|
}
|
|
|
|
sa->IsIvExisting = true;
|
|
}
|
|
|
|
// Calculate the key set of the IKE SA
|
|
void IkeCalcSaKeySet(IKE_SERVER *ike, IKE_SA *sa, char *secret)
|
|
{
|
|
BUF *secret_buf;
|
|
BUF *rand_buf;
|
|
BUF *d_buf, *a_buf, *e_buf;
|
|
UCHAR u;
|
|
IKE_HASH *h;
|
|
char tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
h = sa->TransformSetting.Hash;
|
|
|
|
// Calculation of SKEYID
|
|
StrCpy(sa->Secret, sizeof(sa->Secret), secret == NULL ? ike->Secret : secret);
|
|
secret_buf = IkeStrToPassword(sa->Secret);
|
|
rand_buf = CloneBuf(sa->ResponderRand);
|
|
SeekBufToEnd(rand_buf);
|
|
BinToStrEx(tmp, sizeof(tmp), rand_buf->Buf, rand_buf->Size);
|
|
Debug("ResponderRand: %s\n", tmp);
|
|
BinToStrEx(tmp, sizeof(tmp), sa->InitiatorRand->Buf, sa->InitiatorRand->Size);
|
|
Debug("InitiatorRand: %s\n", tmp);
|
|
|
|
WriteBufBuf(rand_buf, sa->InitiatorRand);
|
|
|
|
IkeHMacBuf(h, sa->SKEYID, secret_buf, rand_buf);
|
|
|
|
BinToStrEx(tmp, sizeof(tmp), sa->SKEYID, sa->HashSize);
|
|
Debug("SKEYID: %s\n", tmp);
|
|
|
|
// SKEYID_d
|
|
d_buf = CloneBuf(sa->DhSharedKey);
|
|
SeekBufToEnd(d_buf);
|
|
WriteBufInt64(d_buf, sa->InitiatorCookie);
|
|
WriteBufInt64(d_buf, sa->ResponderCookie);
|
|
u = 0;
|
|
WriteBuf(d_buf, &u, 1);
|
|
IkeHMac(h, sa->SKEYID_d, sa->SKEYID, sa->HashSize, d_buf->Buf, d_buf->Size);
|
|
|
|
BinToStrEx(tmp, sizeof(tmp), sa->SKEYID_d, sa->HashSize);
|
|
Debug("SKEYID_d: %s\n", tmp);
|
|
|
|
// SKEYID_a
|
|
a_buf = MemToBuf(sa->SKEYID_d, sa->HashSize);
|
|
SeekBufToEnd(a_buf);
|
|
WriteBufBuf(a_buf, sa->DhSharedKey);
|
|
WriteBufInt64(a_buf, sa->InitiatorCookie);
|
|
WriteBufInt64(a_buf, sa->ResponderCookie);
|
|
u = 1;
|
|
WriteBuf(a_buf, &u, 1);
|
|
IkeHMac(h, sa->SKEYID_a, sa->SKEYID, sa->HashSize, a_buf->Buf, a_buf->Size);
|
|
|
|
BinToStrEx(tmp, sizeof(tmp), sa->SKEYID_a, sa->HashSize);
|
|
Debug("SKEYID_a: %s\n", tmp);
|
|
|
|
// SKEYID_e
|
|
e_buf = MemToBuf(sa->SKEYID_a, sa->HashSize);
|
|
SeekBufToEnd(e_buf);
|
|
WriteBufBuf(e_buf, sa->DhSharedKey);
|
|
WriteBufInt64(e_buf, sa->InitiatorCookie);
|
|
WriteBufInt64(e_buf, sa->ResponderCookie);
|
|
u = 2;
|
|
WriteBuf(e_buf, &u, 1);
|
|
IkeHMac(h, sa->SKEYID_e, sa->SKEYID, sa->HashSize, e_buf->Buf, e_buf->Size);
|
|
|
|
BinToStrEx(tmp, sizeof(tmp), sa->SKEYID_e, sa->HashSize);
|
|
Debug("SKEYID_e: %s\n", tmp);
|
|
|
|
if (sa->CryptoKey != NULL)
|
|
{
|
|
IkeFreeKey(sa->CryptoKey);
|
|
}
|
|
|
|
sa->CryptoKey = IkeNewCryptoKeyFromK(ike, sa->SKEYID_e, sa->HashSize, sa->TransformSetting.Hash,
|
|
sa->TransformSetting.Crypto, sa->TransformSetting.CryptoKeySize);
|
|
|
|
// Release the memory
|
|
FreeBuf(secret_buf);
|
|
FreeBuf(rand_buf);
|
|
FreeBuf(d_buf);
|
|
FreeBuf(a_buf);
|
|
FreeBuf(e_buf);
|
|
}
|
|
|
|
// Extend the key size
|
|
BUF *IkeExpandKeySize(IKE_HASH *h, void *k, UINT k_size, UINT target_size)
|
|
{
|
|
BUF *b1, *b2;
|
|
UCHAR tmp[IKE_MAX_HASH_SIZE];
|
|
UINT tmp_size;
|
|
// Validate arguments
|
|
if (h == NULL || k == NULL || k_size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (k_size >= target_size)
|
|
{
|
|
return MemToBuf(k, target_size);
|
|
}
|
|
|
|
tmp[0] = 0;
|
|
tmp_size = 1;
|
|
b1 = NewBuf();
|
|
|
|
do
|
|
{
|
|
IkeHMac(h, tmp, k, k_size, tmp, tmp_size);
|
|
WriteBuf(b1, tmp, h->HashSize);
|
|
|
|
tmp_size = h->HashSize;
|
|
}
|
|
while (b1->Size < target_size);
|
|
|
|
b2 = MemToBuf(b1->Buf, target_size);
|
|
|
|
FreeBuf(b1);
|
|
|
|
return b2;
|
|
}
|
|
|
|
// Generate a key from K
|
|
IKE_CRYPTO_KEY *IkeNewCryptoKeyFromK(IKE_SERVER *ike, void *k, UINT k_size, IKE_HASH *h, IKE_CRYPTO *c, UINT crypto_key_size)
|
|
{
|
|
BUF *key_buf;
|
|
IKE_CRYPTO_KEY *ret;
|
|
// Validate arguments
|
|
if (ike == NULL || k == NULL || k_size == 0 || h == NULL || c == NULL || crypto_key_size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
key_buf = IkeExpandKeySize(h, k, k_size, crypto_key_size);
|
|
if (key_buf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = IkeNewKey(c, key_buf->Buf, key_buf->Size);
|
|
|
|
FreeBuf(key_buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Generate a hash for NAT detection
|
|
BUF *IkeCalcNatDetectHash(IKE_SERVER *ike, IKE_HASH *hash, UINT64 initiator_cookie, UINT64 responder_cookie, IP *ip, UINT port)
|
|
{
|
|
BUF *b;
|
|
USHORT us;
|
|
USHORT hash_data[IKE_MAX_HASH_SIZE];
|
|
// Validate arguments
|
|
if (ike == NULL || ip == NULL || hash == NULL)
|
|
{
|
|
return NewBuf();
|
|
}
|
|
|
|
b = NewBuf();
|
|
|
|
WriteBufInt64(b, initiator_cookie);
|
|
WriteBufInt64(b, responder_cookie);
|
|
|
|
if (IsIP6(ip))
|
|
{
|
|
WriteBuf(b, ip->address, sizeof(ip->address));
|
|
}
|
|
else
|
|
{
|
|
WriteBuf(b, IPV4(ip->address), IPV4_SIZE);
|
|
}
|
|
|
|
us = Endian16((USHORT)port);
|
|
|
|
WriteBuf(b, &us, sizeof(USHORT));
|
|
|
|
IkeHash(hash, hash_data, b->Buf, b->Size);
|
|
|
|
FreeBuf(b);
|
|
|
|
return MemToBuf(hash_data, hash->HashSize);
|
|
}
|
|
|
|
// Check the capacity of the opposite IPsec client
|
|
void IkeCheckCaps(IKE_CAPS *caps, IKE_PACKET *p)
|
|
{
|
|
// Validate arguments
|
|
if (caps == NULL || p == NULL)
|
|
{
|
|
Zero(caps, sizeof(IKE_CAPS));
|
|
return;
|
|
}
|
|
|
|
Zero(caps, sizeof(IKE_CAPS));
|
|
|
|
caps->NatTraversalRfc3947 = IkeIsVendorIdExists(p, IKE_VENDOR_ID_RFC3947_NAT_T);
|
|
|
|
caps->NatTraversalDraftIetf = IkeIsVendorIdExists(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_03) ||
|
|
IkeIsVendorIdExists(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_02) ||
|
|
IkeIsVendorIdExists(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_02_2) ||
|
|
IkeIsVendorIdExists(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_00);
|
|
|
|
caps->DpdRfc3706 = IkeIsVendorIdExists(p, IKE_VENDOR_ID_RFC3706_DPD);
|
|
|
|
caps->MS_L2TPIPSecVPNClient = IkeIsVendorIdExists(p, IKE_VENDOR_ID_MICROSOFT_L2TP);
|
|
caps->MS_NT5_ISAKMP_OAKLEY = IkeIsVendorIdExists(p, IKE_VENDOR_ID_MS_NT5_ISAKMPOAKLEY);
|
|
caps->MS_Vid_InitialContact = IkeIsVendorIdExists(p, IKE_VENDOR_ID_MS_VID_INITIALCONTACT);
|
|
}
|
|
|
|
// Check whether the specified vendor ID is contained in the packet
|
|
bool IkeIsVendorIdExists(IKE_PACKET *p, char *str)
|
|
{
|
|
BUF *buf;
|
|
UINT i, num;
|
|
bool ok = false;
|
|
// Validate arguments
|
|
if (p == NULL || str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
buf = IkeStrToVendorId(str);
|
|
if (buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
num = IkeGetPayloadNum(p->PayloadList, IKE_PAYLOAD_VENDOR_ID);
|
|
for (i = 0;i < num;i++)
|
|
{
|
|
IKE_PACKET_PAYLOAD *payload = IkeGetPayload(p->PayloadList, IKE_PAYLOAD_VENDOR_ID, i);
|
|
if (payload == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (CompareBuf(payload->Payload.VendorId.Data, buf))
|
|
{
|
|
ok = true;
|
|
}
|
|
else
|
|
{
|
|
if (payload->Payload.VendorId.Data != NULL)
|
|
{
|
|
if (payload->Payload.VendorId.Data->Size >= buf->Size)
|
|
{
|
|
if (Cmp(payload->Payload.VendorId.Data->Buf, buf->Buf, buf->Size) == 0)
|
|
{
|
|
ok = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeBuf(buf);
|
|
|
|
return ok;
|
|
}
|
|
|
|
// Add the vendor ID payload list
|
|
void IkeAddVendorIdPayloads(IKE_PACKET *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IkeAddVendorId(p, IKE_VENDOR_ID_RFC3947_NAT_T);
|
|
IkeAddVendorId(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_03);
|
|
IkeAddVendorId(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_02);
|
|
IkeAddVendorId(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_02_2);
|
|
IkeAddVendorId(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_00);
|
|
IkeAddVendorId(p, IKE_VENDOR_ID_RFC3706_DPD);
|
|
}
|
|
|
|
// Add the vendor ID payload
|
|
void IkeAddVendorId(IKE_PACKET *p, char *str)
|
|
{
|
|
BUF *buf;
|
|
IKE_PACKET_PAYLOAD *payload;
|
|
// Validate arguments
|
|
if (p == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
buf = IkeStrToVendorId(str);
|
|
if (buf == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
payload = IkeNewDataPayload(IKE_PAYLOAD_VENDOR_ID, buf->Buf, buf->Size);
|
|
|
|
Add(p->PayloadList, payload);
|
|
|
|
FreeBuf(buf);
|
|
}
|
|
|
|
// Convert string to the vendor ID
|
|
BUF *IkeStrToVendorId(char *str)
|
|
{
|
|
// Validate arguments
|
|
if (IsEmptyStr(str))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (StartWith(str, "0x"))
|
|
{
|
|
BUF *buf = StrToBin(str + 2);
|
|
|
|
if (buf == NULL || buf->Size == 0)
|
|
{
|
|
FreeBuf(buf);
|
|
return NULL;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
else
|
|
{
|
|
BUF *buf;
|
|
UCHAR hash[MD5_SIZE];
|
|
|
|
Md5(hash, str, StrLen(str));
|
|
|
|
buf = MemToBuf(hash, sizeof(hash));
|
|
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
// Receive a packet using the IKE SA
|
|
IKE_PACKET *IkeSaRecvPacket(IKE_SERVER *ike, IKE_SA *sa, void *data, UINT size)
|
|
{
|
|
IKE_PACKET *ret;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL || (size != 0 && data == NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (sa->IsIvExisting == false || sa->CryptoKey == NULL)
|
|
{
|
|
ret = IkeParse(data, size, NULL);
|
|
}
|
|
else
|
|
{
|
|
IKE_CRYPTO_PARAM cp;
|
|
|
|
Copy(&cp.Iv, sa->Iv, sa->BlockSize);
|
|
cp.Key = sa->CryptoKey;
|
|
|
|
ret = IkeParse(data, size, &cp);
|
|
|
|
if (ret->FlagEncrypted)
|
|
{
|
|
IkeSaUpdateIv(sa, cp.NextIv, sa->BlockSize);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Receive a packet using IPsec SA (Quick Mode received)
|
|
IKE_PACKET *IPsecSaRecvPacket(IKE_SERVER *ike, IPSECSA *sa, void *data, UINT size)
|
|
{
|
|
IKE_PACKET *ret;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL || (size != 0 && data == NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (sa->IsIvExisting == false || sa->IkeSa->CryptoKey == NULL)
|
|
{
|
|
ret = IkeParse(data, size, NULL);
|
|
}
|
|
else
|
|
{
|
|
IKE_CRYPTO_PARAM cp;
|
|
|
|
Copy(&cp.Iv, sa->Iv, sa->IkeSa->BlockSize);
|
|
cp.Key = sa->IkeSa->CryptoKey;
|
|
|
|
ret = IkeParse(data, size, &cp);
|
|
|
|
if (ret->FlagEncrypted)
|
|
{
|
|
IPsecSaUpdateIv(sa, cp.NextIv, sa->IkeSa->BlockSize);
|
|
IPsecSaUpdateIv(sa->PairIPsecSa, cp.NextIv, sa->IkeSa->BlockSize);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Send a packet using IPsec SA (Quick Mode transmission)
|
|
void IPsecSaSendPacket(IKE_SERVER *ike, IPSECSA *sa, IKE_PACKET *p)
|
|
{
|
|
BUF *buf;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p == NULL)
|
|
{
|
|
FreeBuf(sa->SendBuffer);
|
|
sa->SendBuffer = NULL;
|
|
sa->NextSendTick = 0;
|
|
return;
|
|
}
|
|
|
|
// Build a packet
|
|
if (p->FlagEncrypted == false)
|
|
{
|
|
buf = IkeBuild(p, NULL);
|
|
}
|
|
else
|
|
{
|
|
IKE_CRYPTO_PARAM cp;
|
|
|
|
Copy(cp.Iv, sa->Iv, sa->IkeSa->BlockSize);
|
|
cp.Key = sa->IkeSa->CryptoKey;
|
|
|
|
buf = IkeBuild(p, &cp);
|
|
|
|
IPsecSaUpdateIv(sa, cp.NextIv, sa->IkeSa->BlockSize);
|
|
IPsecSaUpdateIv(sa->PairIPsecSa, cp.NextIv, sa->IkeSa->BlockSize);
|
|
}
|
|
|
|
if (buf == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Register the last packet to re-transmit
|
|
if (sa->SendBuffer != NULL)
|
|
{
|
|
FreeBuf(sa->SendBuffer);
|
|
}
|
|
|
|
sa->SendBuffer = CloneBuf(buf);
|
|
sa->NextSendTick = ike->Now + (UINT64)(IKE_SA_RESEND_INTERVAL);
|
|
AddInterrupt(ike->Interrupts, sa->NextSendTick);
|
|
|
|
IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &sa->IkeClient->ServerIP, sa->IkeClient->ServerPort,
|
|
&sa->IkeClient->ClientIP, sa->IkeClient->ClientPort,
|
|
buf->Buf, buf->Size);
|
|
|
|
Free(buf);
|
|
}
|
|
|
|
// Send a packet using the IKE SA
|
|
void IkeSaSendPacket(IKE_SERVER *ike, IKE_SA *sa, IKE_PACKET *p)
|
|
{
|
|
BUF *buf;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p == NULL)
|
|
{
|
|
FreeBuf(sa->SendBuffer);
|
|
sa->SendBuffer = NULL;
|
|
sa->NextSendTick = 0;
|
|
return;
|
|
}
|
|
|
|
// Build a packet
|
|
if (p->FlagEncrypted == false)
|
|
{
|
|
buf = IkeBuild(p, NULL);
|
|
}
|
|
else
|
|
{
|
|
IKE_CRYPTO_PARAM cp;
|
|
|
|
Copy(cp.Iv, sa->Iv, sa->BlockSize);
|
|
cp.Key = sa->CryptoKey;
|
|
|
|
buf = IkeBuild(p, &cp);
|
|
|
|
IkeSaUpdateIv(sa, cp.NextIv, sa->BlockSize);
|
|
}
|
|
|
|
if (buf == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p->ExchangeType != IKE_EXCHANGE_TYPE_INFORMATION)
|
|
{
|
|
// Register the last packet to re-transmit
|
|
if (sa->SendBuffer != NULL)
|
|
{
|
|
FreeBuf(sa->SendBuffer);
|
|
}
|
|
|
|
sa->SendBuffer = CloneBuf(buf);
|
|
sa->NextSendTick = ike->Now + (UINT64)(IKE_SA_RESEND_INTERVAL);
|
|
AddInterrupt(ike->Interrupts, sa->NextSendTick);
|
|
}
|
|
|
|
IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &sa->IkeClient->ServerIP, sa->IkeClient->ServerPort,
|
|
&sa->IkeClient->ClientIP, sa->IkeClient->ClientPort,
|
|
buf->Buf, buf->Size);
|
|
|
|
Free(buf);
|
|
}
|
|
|
|
// Send an UDP packet
|
|
void IkeSendUdpPacket(IKE_SERVER *ike, UINT type, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, void *data, UINT size)
|
|
{
|
|
UDPPACKET *p;
|
|
// Validate arguments
|
|
if (ike == NULL || server_ip == NULL || client_ip == NULL || server_port == 0 || client_port == 0 || data == NULL || size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
p = NewUdpPacket(server_ip, server_port, client_ip, client_port, data, size);
|
|
|
|
p->Type = type;
|
|
|
|
Add(ike->SendPacketList, p);
|
|
}
|
|
|
|
// Create an IKE SA
|
|
IKE_SA *NewIkeSa(IKE_SERVER *ike, IKE_CLIENT *c, UINT64 init_cookie, UINT mode, IKE_SA_TRANSFORM_SETTING *setting)
|
|
{
|
|
IKE_SA *sa;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || init_cookie == 0 || setting == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
sa = ZeroMalloc(sizeof(IKE_SA));
|
|
|
|
sa->Id = ++ike->CurrentIkeSaId;
|
|
|
|
sa->IkeClient = c;
|
|
sa->InitiatorCookie = init_cookie;
|
|
sa->ResponderCookie = GenerateNewResponserCookie(ike);
|
|
sa->Mode = mode;
|
|
sa->FirstCommTick = sa->LastCommTick = ike->Now;
|
|
Copy(&sa->TransformSetting, setting, sizeof(IKE_SA_TRANSFORM_SETTING));
|
|
|
|
Debug("New IKE SA (Mode = %u): %I64u <--> %I64u (%s %s %s(%u) %u %u)\n",
|
|
mode,
|
|
sa->InitiatorCookie,
|
|
sa->ResponderCookie,
|
|
setting->Dh->Name, setting->Hash->Name, setting->Crypto->Name, setting->CryptoKeySize,
|
|
setting->LifeKilobytes, setting->LifeSeconds);
|
|
|
|
IPsecLog(ike, NULL, sa, NULL, "LI_NEW_IKE_SA",
|
|
(mode == IKE_SA_MAIN_MODE ? _UU("LI_TAG_MAINMODE") : _UU("LI_TAG_AGGRESSIVE")),
|
|
sa->InitiatorCookie, sa->ResponderCookie,
|
|
setting->Dh->Name, setting->Hash->Name, setting->Crypto->Name, setting->CryptoKeySize * 8,
|
|
setting->LifeKilobytes, setting->LifeSeconds);
|
|
|
|
return sa;
|
|
}
|
|
|
|
// Search an IKE SA from the Responder Cookie
|
|
IKE_SA *FindIkeSaByResponderCookie(IKE_SERVER *ike, UINT64 responder_cookie)
|
|
{
|
|
IKE_SA t;
|
|
// Validate arguments
|
|
if (ike == NULL || responder_cookie == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
t.ResponderCookie = responder_cookie;
|
|
|
|
return Search(ike->IkeSaList, &t);
|
|
}
|
|
|
|
// Search an IKE SA from the Responder Cookie and the IKE_CLIENT
|
|
IKE_SA *FindIkeSaByResponderCookieAndClient(IKE_SERVER *ike, UINT64 responder_cookie, IKE_CLIENT *c)
|
|
{
|
|
IKE_SA *sa;
|
|
// Validate arguments
|
|
if (ike == NULL || responder_cookie == 0 || c == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
sa = FindIkeSaByResponderCookie(ike, responder_cookie);
|
|
if (sa == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (sa->IkeClient != c)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return sa;
|
|
}
|
|
|
|
// Search an IKE SA from the endpoint and the Initiator Cookie
|
|
IKE_SA *FindIkeSaByEndPointAndInitiatorCookie(IKE_SERVER *ike, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, UINT64 init_cookie, UINT mode)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL || client_ip == NULL || server_ip == NULL || client_port == 0 || server_port == 0 || init_cookie == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
IKE_CLIENT *c;
|
|
|
|
c = sa->IkeClient;
|
|
|
|
if (CmpIpAddr(&c->ClientIP, client_ip) == 0 &&
|
|
CmpIpAddr(&c->ServerIP, server_ip) == 0 &&
|
|
c->ClientPort == client_port &&
|
|
c->ServerPort == server_port &&
|
|
sa->InitiatorCookie == init_cookie &&
|
|
sa->Mode == mode)
|
|
{
|
|
return sa;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Get the number of IPsec SA that is associated with the IKE_CLIENT
|
|
UINT GetNumberOfIPsecSaOfIkeClient(IKE_SERVER *ike, IKE_CLIENT *c)
|
|
{
|
|
UINT num = 0, i;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa->IkeClient == c)
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
// Get the number of IKE SA that is associated with the IKE_CLIENT
|
|
UINT GetNumberOfIkeSaOfIkeClient(IKE_SERVER *ike, IKE_CLIENT *c)
|
|
{
|
|
UINT num = 0, i;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
if (sa->IkeClient == c)
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
// Get the number of clients that are connected from the specified IP address
|
|
UINT GetNumberOfIkeClientsFromIP(IKE_SERVER *ike, IP *client_ip)
|
|
{
|
|
UINT i, num;
|
|
// Validate arguments
|
|
if (ike == NULL || client_ip == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
num = 0;
|
|
|
|
for (i = 0;i < LIST_NUM(ike->ClientList);i++)
|
|
{
|
|
IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
|
|
|
|
if (CmpIpAddr(&c->ClientIP, client_ip) == 0)
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
// Find the appropriate IKE client. Create if it is absent
|
|
IKE_CLIENT *SearchOrCreateNewIkeClientForIkePacket(IKE_SERVER *ike, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, IKE_PACKET *pr)
|
|
{
|
|
IKE_CLIENT *c;
|
|
// Validate arguments
|
|
if (ike == NULL || pr == NULL || client_ip == NULL || server_ip == NULL || client_port == 0 || server_port == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
c = SearchIkeClientForIkePacket(ike, client_ip, client_port, server_ip, server_port, pr);
|
|
if (c == NULL)
|
|
{
|
|
if (GetNumberOfIkeClientsFromIP(ike, client_ip) > IKE_QUOTA_MAX_NUM_CLIENTS_PER_IP ||
|
|
LIST_NUM(ike->ClientList) > IKE_QUOTA_MAX_NUM_CLIENTS)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
c = NewIkeClient(ike, client_ip, client_port, server_ip, server_port);
|
|
|
|
Insert(ike->ClientList, c);
|
|
}
|
|
|
|
return SetIkeClientEndpoint(ike, c, client_ip, client_port, server_ip, server_port);
|
|
}
|
|
|
|
// Create an IKE client
|
|
IKE_CLIENT *NewIkeClient(IKE_SERVER *ike, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port)
|
|
{
|
|
IKE_CLIENT *c;
|
|
char client_ip_str[MAX_SIZE];
|
|
char server_ip_str[MAX_SIZE];
|
|
// Validate arguments
|
|
if (ike == NULL || client_ip == NULL || server_ip == NULL || client_port == 0 || server_port == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
c = ZeroMalloc(sizeof(IKE_CLIENT));
|
|
|
|
c->Id = ++ike->CurrentIkeClientId;
|
|
|
|
Copy(&c->ClientIP, client_ip, sizeof(IP));
|
|
c->ClientPort = client_port;
|
|
|
|
Copy(&c->ServerIP, server_ip, sizeof(IP));
|
|
Copy(&c->TransportModeServerIP, server_ip, sizeof(IP));
|
|
Copy(&c->TransportModeClientIP, client_ip, sizeof(IP));
|
|
c->ServerPort = server_port;
|
|
|
|
c->LastCommTick = ike->Now;
|
|
c->FirstCommTick = ike->Now;
|
|
|
|
IPToStr(client_ip_str, sizeof(client_ip_str), client_ip);
|
|
IPToStr(server_ip_str, sizeof(server_ip_str), server_ip);
|
|
|
|
Debug("New IKE_CLIENT: %p: %s:%u -> %s:%u\n", c, client_ip_str, client_port, server_ip_str, server_port);
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_NEW_IKE_CLIENT");
|
|
|
|
return c;
|
|
}
|
|
|
|
// Search for the best associated IKE client when an IKE packet has been received
|
|
IKE_CLIENT *SearchIkeClientForIkePacket(IKE_SERVER *ike, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, IKE_PACKET *pr)
|
|
{
|
|
IKE_CLIENT t;
|
|
IKE_CLIENT *c = NULL;
|
|
// Validate arguments
|
|
if (ike == NULL || pr == NULL || client_ip == NULL || server_ip == NULL || client_port == 0 || server_port == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (true)
|
|
{
|
|
UINT i;
|
|
|
|
if (pr->InitiatorCookie != 0 && pr->ResponderCookie != 0)
|
|
{
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
// Extract what Cookie matches exactly
|
|
if (sa->InitiatorCookie == pr->InitiatorCookie && sa->ResponderCookie == pr->ResponderCookie)
|
|
{
|
|
IKE_CLIENT *cc = sa->IkeClient;
|
|
|
|
if (CmpIpAddr(&cc->ServerIP, server_ip) == 0 &&
|
|
CmpIpAddr(&cc->ClientIP, client_ip) == 0)
|
|
{
|
|
c = cc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (c == NULL)
|
|
{
|
|
// Search by a pair of IP address and port number
|
|
Copy(&t.ClientIP, client_ip, sizeof(IP));
|
|
t.ClientPort = client_port;
|
|
Copy(&t.ServerIP, server_ip, sizeof(IP));
|
|
t.ServerPort = server_port;
|
|
|
|
c = Search(ike->ClientList, &t);
|
|
|
|
if (c != NULL)// && server_port == IPSEC_PORT_IPSEC_ISAKMP)
|
|
{
|
|
// Search that the IKE_SA that points to this IKE_CLIENT exists and match the Cookie
|
|
bool ok = false;
|
|
UINT i;
|
|
|
|
if (server_port == IPSEC_PORT_IPSEC_ESP_UDP)
|
|
{
|
|
// Regard as OK if the port number exactly match in the case of connecting to a server-side 4500
|
|
ok = true;
|
|
}
|
|
else
|
|
{
|
|
if (c->CurrentIkeSa != NULL &&
|
|
c->CurrentIkeSa->InitiatorCookie == pr->InitiatorCookie &&
|
|
c->CurrentIkeSa->ResponderCookie == pr->ResponderCookie)
|
|
{
|
|
ok = true;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
if (sa->IkeClient == c)
|
|
{
|
|
if (sa->InitiatorCookie == pr->InitiatorCookie &&
|
|
sa->ResponderCookie == pr->ResponderCookie)
|
|
{
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok == false)
|
|
{
|
|
// Not found
|
|
c = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
// Comparison of IPsec SA
|
|
int CmpIPsecSa(void *p1, void *p2)
|
|
{
|
|
IPSECSA *sa1, *sa2;
|
|
int r;
|
|
// Validate arguments
|
|
if (p1 == NULL || p2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
sa1 = *(IPSECSA **)p1;
|
|
sa2 = *(IPSECSA **)p2;
|
|
if (sa1 == NULL || sa2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
r = COMPARE_RET(sa1->ServerToClient, sa2->ServerToClient);
|
|
if (r != 0)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
r = COMPARE_RET(sa1->Spi, sa2->Spi);
|
|
|
|
return r;
|
|
}
|
|
|
|
// Comparison of IKE_SA
|
|
int CmpIkeSa(void *p1, void *p2)
|
|
{
|
|
IKE_SA *sa1, *sa2;
|
|
int r;
|
|
// Validate arguments
|
|
if (p1 == NULL || p2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
sa1 = *(IKE_SA **)p1;
|
|
sa2 = *(IKE_SA **)p2;
|
|
if (sa1 == NULL || sa2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
r = COMPARE_RET(sa1->ResponderCookie, sa2->ResponderCookie);
|
|
|
|
return r;
|
|
}
|
|
|
|
// Comparison of IKE_CLIENT
|
|
int CmpIkeClient(void *p1, void *p2)
|
|
{
|
|
IKE_CLIENT *c1, *c2;
|
|
int r;
|
|
// Validate arguments
|
|
if (p1 == NULL || p2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
c1 = *(IKE_CLIENT **)p1;
|
|
c2 = *(IKE_CLIENT **)p2;
|
|
if (c1 == NULL || c2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
r = CmpIpAddr(&c1->ClientIP, &c2->ClientIP);
|
|
if (r != 0)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
r = CmpIpAddr(&c1->ServerIP, &c2->ServerIP);
|
|
if (r != 0)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
r = COMPARE_RET(c1->ClientPort, c2->ClientPort);
|
|
if (r != 0)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
r = COMPARE_RET(c1->ServerPort, c2->ServerPort);
|
|
if (r != 0)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Update the endpoint information of IKE_CLIENT
|
|
IKE_CLIENT *SetIkeClientEndpoint(IKE_SERVER *ike, IKE_CLIENT *c, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port)
|
|
{
|
|
char client_ip_str[MAX_SIZE];
|
|
char server_ip_str[MAX_SIZE];
|
|
IKE_CLIENT *ret = c;
|
|
IKE_CLIENT *cc;
|
|
IKE_CLIENT t;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL || client_ip == NULL || client_port == 0 || server_ip == NULL || server_port == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (CmpIpAddr(&c->ClientIP, client_ip) == 0 &&
|
|
CmpIpAddr(&c->ServerIP, server_ip) == 0 &&
|
|
c->ClientPort == client_port &&
|
|
c->ServerPort == server_port)
|
|
{
|
|
// No change
|
|
return ret;
|
|
}
|
|
|
|
if (IS_SPECIAL_PORT(client_port) || IS_SPECIAL_PORT(server_port))
|
|
{
|
|
// Don't change in the case of Raw socket
|
|
return ret;
|
|
}
|
|
|
|
// Search for an existing IKE_CLIENT which exactly matches to combination of the new IP address and the port number
|
|
Copy(&t.ClientIP, client_ip, sizeof(IP));
|
|
t.ClientPort = client_port;
|
|
Copy(&t.ServerIP, server_ip, sizeof(IP));
|
|
t.ServerPort = server_port;
|
|
|
|
cc = Search(ike->ClientList, &t);
|
|
if (cc != NULL && c != cc && cc->Deleting == false && c->L2TP == NULL)
|
|
{
|
|
UINT i;
|
|
// Merge into this existing IKE_CLIENT since it found
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
if (sa->IkeClient == c)
|
|
{
|
|
sa->IkeClient = cc;
|
|
}
|
|
}
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa->IkeClient == c)
|
|
{
|
|
sa->IkeClient = cc;
|
|
}
|
|
}
|
|
|
|
if (cc->LastCommTick < c->LastCommTick)
|
|
{
|
|
StrCpy(cc->ClientId, sizeof(cc->ClientId), c->ClientId);
|
|
}
|
|
|
|
cc->FirstCommTick = MIN(cc->FirstCommTick, c->FirstCommTick);
|
|
cc->LastCommTick = MAX(cc->LastCommTick, c->LastCommTick);
|
|
|
|
ret = cc;
|
|
|
|
IPToStr(client_ip_str, sizeof(client_ip_str), client_ip);
|
|
IPToStr(server_ip_str, sizeof(server_ip_str), server_ip);
|
|
|
|
Debug("Merge IKE_CLIENT: %p->%p: %s:%u -> %s:%u\n", c, cc, client_ip_str, client_port, server_ip_str, server_port);
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_CLIENT_MERGE", c->Id, cc->Id, cc->Id);
|
|
|
|
// Remove old IKE_CLIENT from the list and free
|
|
Delete(ike->ClientList, c);
|
|
FreeIkeClient(ike, c);
|
|
}
|
|
else
|
|
{
|
|
// Rewrite the end point information of this IKE_CLIENT because not found
|
|
Copy(&c->ClientIP, client_ip, sizeof(IP));
|
|
Copy(&c->ServerIP, server_ip, sizeof(IP));
|
|
c->ClientPort = client_port;
|
|
c->ServerPort = server_port;
|
|
|
|
IPToStr(client_ip_str, sizeof(client_ip_str), client_ip);
|
|
IPToStr(server_ip_str, sizeof(server_ip_str), server_ip);
|
|
|
|
Debug("Update IKE_CLIENT: %p: %s:%u -> %s:%u\n", c, client_ip_str, client_port, server_ip_str, server_port);
|
|
|
|
IPsecLog(ike, c, NULL, NULL, "LI_CLIENT_UPDATE");
|
|
|
|
ike->ClientList->sorted = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Select the optimal transform setting for IPsec SA
|
|
bool GetBestTransformSettingForIPsecSa(IKE_SERVER *ike, IKE_PACKET *pr, IPSEC_SA_TRANSFORM_SETTING *setting, IP *server_ip)
|
|
{
|
|
IKE_PACKET_PAYLOAD *sa_payload;
|
|
IKE_PACKET_SA_PAYLOAD *sa;
|
|
UINT i, num;
|
|
bool ocmii_flag = false;
|
|
// Validate arguments
|
|
if (ike == NULL || pr == NULL || setting == NULL || server_ip == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Zero(setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
|
|
// Get the SA payload
|
|
sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
|
|
if (sa_payload == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
sa = &sa_payload->Payload.Sa;
|
|
|
|
// Scan all proposal payloads
|
|
num = IkeGetPayloadNum(sa->PayloadList, IKE_PAYLOAD_PROPOSAL);
|
|
for (i = 0;i < num;i++)
|
|
{
|
|
IKE_PACKET_PAYLOAD *proposal_payload = IkeGetPayload(sa->PayloadList, IKE_PAYLOAD_PROPOSAL, i);
|
|
|
|
if (proposal_payload != NULL)
|
|
{
|
|
IKE_PACKET_PROPOSAL_PAYLOAD *proposal = &proposal_payload->Payload.Proposal;
|
|
|
|
// Examine the contents of the proposal payload
|
|
if (proposal->ProtocolId == IKE_PROTOCOL_ID_IPSEC_ESP && proposal->Spi->Size == 4)
|
|
{
|
|
// Scan all transform payloads
|
|
UINT j, num2;
|
|
|
|
num2 = IkeGetPayloadNum(proposal->PayloadList, IKE_PAYLOAD_TRANSFORM);
|
|
for (j = 0;j < num2;j++)
|
|
{
|
|
IKE_PACKET_PAYLOAD *transform_payload = IkeGetPayload(proposal->PayloadList, IKE_PAYLOAD_TRANSFORM, j);
|
|
if (transform_payload != NULL)
|
|
{
|
|
IKE_PACKET_TRANSFORM_PAYLOAD *transform = &transform_payload->Payload.Transform;
|
|
IPSEC_SA_TRANSFORM_SETTING set;
|
|
|
|
Zero(&set, sizeof(set));
|
|
|
|
if (TransformPayloadToTransformSettingForIPsecSa(ike, transform, &set, server_ip))
|
|
{
|
|
Copy(setting, &set, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
|
|
setting->SpiServerToClient = READ_UINT(proposal->Spi->Buf);
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (set.OnlyCapsuleModeIsInvalid)
|
|
{
|
|
if (ocmii_flag == false)
|
|
{
|
|
Copy(setting, &set, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
ocmii_flag = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Select the optimal transform settings for the IKE SA
|
|
bool GetBestTransformSettingForIkeSa(IKE_SERVER *ike, IKE_PACKET *pr, IKE_SA_TRANSFORM_SETTING *setting)
|
|
{
|
|
IKE_PACKET_PAYLOAD *sa_payload;
|
|
IKE_PACKET_SA_PAYLOAD *sa;
|
|
UINT i, num;
|
|
// Validate arguments
|
|
if (ike == NULL || pr == NULL || setting == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get the SA payload
|
|
sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
|
|
if (sa_payload == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
sa = &sa_payload->Payload.Sa;
|
|
|
|
// Scan all proposal payloads
|
|
num = IkeGetPayloadNum(sa->PayloadList, IKE_PAYLOAD_PROPOSAL);
|
|
for (i = 0;i < num;i++)
|
|
{
|
|
IKE_PACKET_PAYLOAD *proposal_payload = IkeGetPayload(sa->PayloadList, IKE_PAYLOAD_PROPOSAL, i);
|
|
|
|
if (proposal_payload != NULL)
|
|
{
|
|
IKE_PACKET_PROPOSAL_PAYLOAD *proposal = &proposal_payload->Payload.Proposal;
|
|
|
|
// Examine the contents of the proposal payload
|
|
if (proposal->ProtocolId == IKE_PROTOCOL_ID_IKE)
|
|
{
|
|
// Scan all transform payloads
|
|
UINT j, num2;
|
|
|
|
num2 = IkeGetPayloadNum(proposal->PayloadList, IKE_PAYLOAD_TRANSFORM);
|
|
for (j = 0;j < num2;j++)
|
|
{
|
|
IKE_PACKET_PAYLOAD *transform_payload = IkeGetPayload(proposal->PayloadList, IKE_PAYLOAD_TRANSFORM, j);
|
|
if (transform_payload != NULL)
|
|
{
|
|
IKE_PACKET_TRANSFORM_PAYLOAD *transform = &transform_payload->Payload.Transform;
|
|
|
|
if (transform->TransformId == IKE_TRANSFORM_ID_P1_KEY_IKE)
|
|
{
|
|
IKE_SA_TRANSFORM_SETTING set;
|
|
|
|
if (TransformPayloadToTransformSettingForIkeSa(ike, transform, &set))
|
|
{
|
|
Copy(setting, &set, sizeof(IKE_SA_TRANSFORM_SETTING));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Convert a structure to the transform payload (for IPsec SA)
|
|
IKE_PACKET_PAYLOAD *TransformSettingToTransformPayloadForIPsec(IKE_SERVER *ike, IPSEC_SA_TRANSFORM_SETTING *setting)
|
|
{
|
|
LIST *value_list;
|
|
// Validate arguments
|
|
if (ike == NULL || setting == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
value_list = NewListFast(NULL);
|
|
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_HMAC, setting->HashId));
|
|
|
|
if (setting->Dh != NULL)
|
|
{
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_DH_GROUP, setting->DhId));
|
|
}
|
|
|
|
if (setting->LifeSeconds != INFINITE)
|
|
{
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_LIFE_TYPE, IKE_P2_LIFE_TYPE_SECONDS));
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_LIFE_VALUE, setting->LifeSeconds));
|
|
}
|
|
|
|
if (setting->LifeKilobytes != INFINITE)
|
|
{
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_LIFE_TYPE, IKE_P2_LIFE_TYPE_KILOBYTES));
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_LIFE_VALUE, setting->LifeKilobytes));
|
|
}
|
|
|
|
if (setting->Crypto->VariableKeySize)
|
|
{
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_KEY_SIZE, setting->CryptoKeySize * 8));
|
|
}
|
|
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_CAPSULE, setting->CapsuleMode));
|
|
|
|
return IkeNewTransformPayload(1, setting->CryptoId, value_list);
|
|
}
|
|
|
|
// Convert a structure to the transform payload (for IKE SA)
|
|
IKE_PACKET_PAYLOAD *TransformSettingToTransformPayloadForIke(IKE_SERVER *ike, IKE_SA_TRANSFORM_SETTING *setting)
|
|
{
|
|
LIST *value_list;
|
|
// Validate arguments
|
|
if (ike == NULL || setting == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
value_list = NewListFast(NULL);
|
|
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_CRYPTO, setting->CryptoId));
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_HASH, setting->HashId));
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_AUTH_METHOD, IKE_P1_AUTH_METHOD_PRESHAREDKEY));
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_DH_GROUP, setting->DhId));
|
|
|
|
if (setting->LifeSeconds != INFINITE)
|
|
{
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_LIFE_TYPE, IKE_P1_LIFE_TYPE_SECONDS));
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_LIFE_VALUE, setting->LifeSeconds));
|
|
}
|
|
|
|
if (setting->LifeKilobytes != INFINITE)
|
|
{
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_LIFE_TYPE, IKE_P1_LIFE_TYPE_KILOBYTES));
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_LIFE_VALUE, setting->LifeKilobytes));
|
|
}
|
|
|
|
if (setting->Crypto->VariableKeySize)
|
|
{
|
|
Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_KET_SIZE, setting->CryptoKeySize * 8));
|
|
}
|
|
|
|
return IkeNewTransformPayload(1, IKE_TRANSFORM_ID_P1_KEY_IKE, value_list);
|
|
}
|
|
|
|
// Convert a transform payload to a structure (for IPsec SA)
|
|
bool TransformPayloadToTransformSettingForIPsecSa(IKE_SERVER *ike, IKE_PACKET_TRANSFORM_PAYLOAD *transform, IPSEC_SA_TRANSFORM_SETTING *setting, IP *server_ip)
|
|
{
|
|
UINT i;
|
|
UINT capsule_mode;
|
|
bool is_esp_supported;
|
|
// Validate arguments
|
|
if (ike == NULL || transform == NULL || setting == NULL || server_ip == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
is_esp_supported = IsUdpPortOpened(ike->IPsec->UdpListener, server_ip, IPSEC_PORT_IPSEC_ESP_RAW);
|
|
|
|
Zero(setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));
|
|
|
|
setting->CryptoId = transform->TransformId;
|
|
setting->HashId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_HMAC, 0);
|
|
|
|
setting->DhId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_DH_GROUP, 0);
|
|
|
|
setting->LifeKilobytes = INFINITE;
|
|
setting->LifeSeconds = INFINITE;
|
|
|
|
for (i = 0;i < IkeGetTransformValueNum(transform, IKE_TRANSFORM_VALUE_P2_LIFE_TYPE);i++)
|
|
{
|
|
UINT life_type = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_LIFE_TYPE, i);
|
|
|
|
switch (life_type)
|
|
{
|
|
case IKE_P2_LIFE_TYPE_SECONDS: // Number of seconds
|
|
setting->LifeSeconds = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_LIFE_VALUE, i);
|
|
break;
|
|
|
|
case IKE_P2_LIFE_TYPE_KILOBYTES: // Kilobytes
|
|
setting->LifeKilobytes = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_LIFE_VALUE, i);
|
|
break;
|
|
|
|
default:
|
|
// Unsupported expiration type
|
|
return false;
|
|
}
|
|
}
|
|
|
|
setting->Crypto = GetIkeCrypto(ike->Engine, true, setting->CryptoId);
|
|
setting->Hash = GetIkeHash(ike->Engine, true, setting->HashId);
|
|
setting->Dh = GetIkeDh(ike->Engine, true, setting->DhId);
|
|
|
|
if (setting->Crypto == NULL || setting->Hash == NULL)
|
|
{
|
|
// Unsupported algorithm
|
|
return false;
|
|
}
|
|
|
|
if (setting->Crypto->VariableKeySize)
|
|
{
|
|
// Get the actual key size in the case of variable key size
|
|
setting->CryptoKeySize = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_KEY_SIZE, 0);
|
|
|
|
// bits -> bytes
|
|
setting->CryptoKeySize = setting->CryptoKeySize / 8;
|
|
|
|
if (setting->CryptoKeySize == 0 || IkeCheckKeySize(setting->Crypto, setting->CryptoKeySize) == false)
|
|
{
|
|
// The key size is not specified or inappropriate
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get a fixed key length for fixed key size
|
|
setting->CryptoKeySize = setting->Crypto->KeySizes[0];
|
|
}
|
|
|
|
capsule_mode = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_CAPSULE, 0);
|
|
if (capsule_mode != IKE_P2_CAPSULE_NAT_TUNNEL_1 && capsule_mode != IKE_P2_CAPSULE_NAT_TUNNEL_2 &&
|
|
capsule_mode != IKE_P2_CAPSULE_NAT_TRANSPORT_1 && capsule_mode != IKE_P2_CAPSULE_NAT_TRANSPORT_2)
|
|
{
|
|
// No support for UDP encapsulation mode except for the NAT-Traversal
|
|
if (capsule_mode == IKE_P2_CAPSULE_TRANSPORT || capsule_mode == IKE_P2_CAPSULE_TUNNEL)
|
|
{
|
|
if (is_esp_supported == false)
|
|
{
|
|
setting->OnlyCapsuleModeIsInvalid = true;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// It is an environment that can send and receive ESP packets
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
setting->CapsuleMode = capsule_mode;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Convert a transform payload to a structure (for IKE SA)
|
|
bool TransformPayloadToTransformSettingForIkeSa(IKE_SERVER *ike, IKE_PACKET_TRANSFORM_PAYLOAD *transform, IKE_SA_TRANSFORM_SETTING *setting)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL || transform == NULL || setting == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Zero(setting, sizeof(IKE_SA_TRANSFORM_SETTING));
|
|
|
|
setting->CryptoId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_CRYPTO, 0);
|
|
setting->HashId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_HASH, 0);
|
|
|
|
if (IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_AUTH_METHOD, 0) != IKE_P1_AUTH_METHOD_PRESHAREDKEY)
|
|
{
|
|
// Only PSK authentication method is supported
|
|
return false;
|
|
}
|
|
|
|
setting->DhId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_DH_GROUP, 0);
|
|
|
|
setting->LifeKilobytes = INFINITE;
|
|
setting->LifeSeconds = INFINITE;
|
|
|
|
for (i = 0;i < IkeGetTransformValueNum(transform, IKE_TRANSFORM_VALUE_P1_LIFE_TYPE);i++)
|
|
{
|
|
UINT life_type = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_LIFE_TYPE, i);
|
|
|
|
switch (life_type)
|
|
{
|
|
case IKE_P1_LIFE_TYPE_SECONDS: // Number of seconds
|
|
setting->LifeSeconds = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_LIFE_VALUE, i);
|
|
break;
|
|
|
|
case IKE_P1_LIFE_TYPE_KILOBYTES: // Kilobytes
|
|
setting->LifeKilobytes = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_LIFE_VALUE, i);
|
|
break;
|
|
|
|
default:
|
|
// Unsupported expiration type
|
|
return false;
|
|
}
|
|
}
|
|
|
|
setting->Crypto = GetIkeCrypto(ike->Engine, false, setting->CryptoId);
|
|
setting->Hash = GetIkeHash(ike->Engine, false, setting->HashId);
|
|
setting->Dh = GetIkeDh(ike->Engine, false, setting->DhId);
|
|
|
|
if (setting->Crypto == NULL || setting->Hash == NULL || setting->Dh == NULL)
|
|
{
|
|
// Unsupported algorithm
|
|
return false;
|
|
}
|
|
|
|
if (setting->Crypto->VariableKeySize)
|
|
{
|
|
// Get the actual key size in the case of variable key size
|
|
setting->CryptoKeySize = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_KET_SIZE, 0);
|
|
|
|
// bits -> bytes
|
|
setting->CryptoKeySize = setting->CryptoKeySize / 8;
|
|
|
|
if (setting->CryptoKeySize == 0 || IkeCheckKeySize(setting->Crypto, setting->CryptoKeySize) == false)
|
|
{
|
|
// The key size is not specified or inappropriate
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get a fixed key length for fixed key size
|
|
setting->CryptoKeySize = setting->Crypto->KeySizes[0];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Creating a new Responder Cookie
|
|
UINT64 GenerateNewResponserCookie(IKE_SERVER *ike)
|
|
{
|
|
UINT64 c;
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
bool b = false;
|
|
UINT i;
|
|
|
|
c = Rand64();
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
if (sa->ResponderCookie == c)
|
|
{
|
|
b = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (b == false)
|
|
{
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse the IKE packet header
|
|
IKE_PACKET *ParseIKEPacketHeader(UDPPACKET *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return IkeParseHeader(p->Data, p->Size, NULL);
|
|
}
|
|
|
|
// Search for another IPsec SA belonging to the IKE_CLIENT which have same conditions to the specified IPsec SA
|
|
IPSECSA *GetOtherLatestIPsecSa(IKE_SERVER *ike, IPSECSA *sa)
|
|
{
|
|
UINT i;
|
|
UINT64 min_value = 0;
|
|
IPSECSA *max_sa = NULL;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (sa->IkeClient == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa2 = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa2 != sa)
|
|
{
|
|
if (sa2->IkeClient == sa->IkeClient)
|
|
{
|
|
if (sa2->ServerToClient == sa->ServerToClient)
|
|
{
|
|
if (sa2->Deleting == false)
|
|
{
|
|
if (sa2->Established)
|
|
{
|
|
UINT64 last_comm_tick = sa2->LastCommTick;
|
|
|
|
if (sa2->ServerToClient)
|
|
{
|
|
if (sa2->PairIPsecSa != NULL)
|
|
{
|
|
last_comm_tick = sa2->PairIPsecSa->LastCommTick;
|
|
}
|
|
}
|
|
|
|
if (min_value < last_comm_tick)
|
|
{
|
|
min_value = last_comm_tick;
|
|
|
|
max_sa = sa2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return max_sa;
|
|
}
|
|
|
|
// Search for another IKE_SA belonging to the IKE_CLIENT which have same conditions to the specified IKE_SA
|
|
IKE_SA *GetOtherLatestIkeSa(IKE_SERVER *ike, IKE_SA *sa)
|
|
{
|
|
UINT i;
|
|
UINT64 min_value = 0;
|
|
IKE_SA *max_sa = NULL;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (sa->IkeClient == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa2 = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
if (sa2 != sa)
|
|
{
|
|
if (sa2->IkeClient == sa->IkeClient)
|
|
{
|
|
if (sa2->Deleting == false)
|
|
{
|
|
if (sa2->Established)
|
|
{
|
|
if (min_value < sa2->LastCommTick)
|
|
{
|
|
min_value = sa2->LastCommTick;
|
|
|
|
max_sa = sa2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return max_sa;
|
|
}
|
|
|
|
// Purge the IPsec SA
|
|
void PurgeIPsecSa(IKE_SERVER *ike, IPSECSA *sa)
|
|
{
|
|
UINT i;
|
|
IPSECSA *other_sa;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
other_sa = GetOtherLatestIPsecSa(ike, sa);
|
|
|
|
// Rewrite the pairing partner by looking for IPsec SA that are paired
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa2 = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa2->PairIPsecSa == sa)
|
|
{
|
|
sa2->PairIPsecSa = other_sa;
|
|
}
|
|
}
|
|
|
|
// Rewrite the IKE_CLIENT using this IPsec SA to use alternate
|
|
for (i = 0;i < LIST_NUM(ike->ClientList);i++)
|
|
{
|
|
IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
|
|
|
|
if (c->CurrentIpSecSaRecv == sa)
|
|
{
|
|
c->CurrentIpSecSaRecv = other_sa;
|
|
}
|
|
|
|
if (c->CurrentIpSecSaSend == sa)
|
|
{
|
|
c->CurrentIpSecSaSend = other_sa;
|
|
}
|
|
}
|
|
|
|
Delete(ike->IPsecSaList, sa);
|
|
FreeIPsecSa(sa);
|
|
}
|
|
|
|
// Remove the IKE SA
|
|
void PurgeIkeSa(IKE_SERVER *ike, IKE_SA *sa)
|
|
{
|
|
IKE_SA *other_sa;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL || sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Debug("Purging IKE SA %I64u-%I64u\n", sa->InitiatorCookie, sa->ResponderCookie);
|
|
|
|
// Rewrite to alternative IKE_SA of all IPsec SA that are using this IKE_SA
|
|
other_sa = GetOtherLatestIkeSa(ike, sa);
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *ipsec_sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (ipsec_sa->IkeSa == sa)
|
|
{
|
|
if (other_sa == NULL)
|
|
{
|
|
// Remove this IPsec SA because there is no alternative IKE_SA
|
|
Debug(" Deleting IPsec SA 0x%X of this IKE SA (no alternatives)\n", ipsec_sa->Spi);
|
|
MarkIPsecSaAsDeleted(ike, ipsec_sa);
|
|
ipsec_sa->IkeSa = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Replace to the alternative IKE_SA
|
|
Debug(" Replacing IKE SA of IPsec SA 0x%X from %I64u-%I64u to %I64u-%I64u\n", ipsec_sa->Spi,
|
|
sa->InitiatorCookie, sa->ResponderCookie,
|
|
other_sa->InitiatorCookie, other_sa->ResponderCookie);
|
|
ipsec_sa->IkeSa = other_sa;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Substitute the IKE_SA of all IKE_CLIENT that are using this IKE_SA with alternative
|
|
for (i = 0;i < LIST_NUM(ike->ClientList);i++)
|
|
{
|
|
IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
|
|
|
|
if (c->CurrentIkeSa == sa)
|
|
{
|
|
c->CurrentIkeSa = other_sa;
|
|
}
|
|
}
|
|
|
|
Delete(ike->IkeSaList, sa);
|
|
FreeIkeSa(sa);
|
|
}
|
|
|
|
// Purge the IKE_CLIENT
|
|
void PurgeIkeClient(IKE_SERVER *ike, IKE_CLIENT *c)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL || c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Delete all of IPsec SA and IKE SA that belong to this IKE Client
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
if (sa->IkeClient == c)
|
|
{
|
|
MarkIkeSaAsDeleted(ike, sa);
|
|
}
|
|
}
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
if (sa->IkeClient == c)
|
|
{
|
|
MarkIPsecSaAsDeleted(ike, sa);
|
|
}
|
|
}
|
|
|
|
Delete(ike->ClientList, c);
|
|
FreeIkeClient(ike, c);
|
|
}
|
|
|
|
// Remove the SA that has been marked to delete
|
|
void PurgeDeletingSAsAndClients(IKE_SERVER *ike)
|
|
{
|
|
UINT i;
|
|
LIST *o = NULL;
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
if (sa->Deleting)
|
|
{
|
|
if (o == NULL)
|
|
{
|
|
o = NewListFast(NULL);
|
|
}
|
|
|
|
Add(o, sa);
|
|
}
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(o, i);
|
|
|
|
PurgeIkeSa(ike, sa);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
|
|
o = NULL;
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
if (sa->Deleting)
|
|
{
|
|
if (o == NULL)
|
|
{
|
|
o = NewListFast(NULL);
|
|
}
|
|
|
|
Add(o, sa);
|
|
}
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(o, i);
|
|
|
|
PurgeIPsecSa(ike, sa);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
|
|
o = NULL;
|
|
|
|
for (i = 0;i < LIST_NUM(ike->ClientList);i++)
|
|
{
|
|
IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
|
|
if (c->Deleting)
|
|
{
|
|
if (o == NULL)
|
|
{
|
|
o = NewListFast(NULL);
|
|
}
|
|
|
|
Add(o, c);
|
|
}
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
IKE_CLIENT *c = LIST_DATA(o, i);
|
|
|
|
PurgeIkeClient(ike, c);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
}
|
|
|
|
// IKE interrupt process
|
|
void ProcessIKEInterrupts(IKE_SERVER *ike)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(ike->ClientList);i++)
|
|
{
|
|
IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
|
|
|
|
c->CurrentExpiresSoftTick_CtoS = 0;
|
|
c->CurrentExpiresSoftTick_StoC = 0;
|
|
c->CurrentNumEstablishedIPsecSA_CtoS = 0;
|
|
c->CurrentNumEstablishedIPsecSA_StoC = 0;
|
|
c->CurrentNumHealtyIPsecSA_CtoS = 0;
|
|
c->CurrentNumHealtyIPsecSA_StoC = 0;
|
|
}
|
|
|
|
// Packet retransmission by scanning all IKE SA
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
if (sa->SendBuffer != NULL)
|
|
{
|
|
if (ike->Now >= sa->NextSendTick)
|
|
{
|
|
IKE_CLIENT *c = sa->IkeClient;
|
|
|
|
IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &c->ServerIP, c->ServerPort, &c->ClientIP, c->ClientPort,
|
|
Clone(sa->SendBuffer->Buf, sa->SendBuffer->Size), sa->SendBuffer->Size);
|
|
|
|
sa->NextSendTick += (UINT64)(IKE_SA_RESEND_INTERVAL);
|
|
|
|
AddInterrupt(ike->Interrupts, sa->NextSendTick);
|
|
|
|
if (sa->NumResends != 0)
|
|
{
|
|
sa->NumResends--;
|
|
if (sa->NumResends == 0)
|
|
{
|
|
sa->NextSendTick = 0;
|
|
FreeBuf(sa->SendBuffer);
|
|
sa->SendBuffer = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove those of non-communication
|
|
if (sa->IkeClient == NULL || (sa->IkeClient->CurrentIkeSa != sa))
|
|
{
|
|
// When the IKE_CLIENT don't point this
|
|
if (sa->Established == false)
|
|
{
|
|
// Make time-out in a short time when it is not established
|
|
if ((sa->LastCommTick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT_FOR_NOT_ESTABLISHED) <= ike->Now)
|
|
{
|
|
WHERE;
|
|
MarkIkeSaAsDeleted(ike, sa);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Timeout in a long time in the case of established
|
|
if ((sa->LastCommTick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT) <= ike->Now)
|
|
{
|
|
WHERE;
|
|
MarkIkeSaAsDeleted(ike, sa);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Packet retransmission by scanning all IPsec SA
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
IKE_CLIENT *c = sa->IkeClient;
|
|
|
|
if (sa->SendBuffer != NULL)
|
|
{
|
|
if (ike->Now >= sa->NextSendTick)
|
|
{
|
|
IKE_CLIENT *c = sa->IkeClient;
|
|
|
|
IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &c->ServerIP, c->ServerPort, &c->ClientIP, c->ClientPort,
|
|
Clone(sa->SendBuffer->Buf, sa->SendBuffer->Size), sa->SendBuffer->Size);
|
|
|
|
sa->NextSendTick += (UINT64)(IKE_SA_RESEND_INTERVAL);
|
|
|
|
AddInterrupt(ike->Interrupts, sa->NextSendTick);
|
|
|
|
if (sa->NumResends != 0)
|
|
{
|
|
sa->NumResends--;
|
|
|
|
if (sa->NumResends == 0)
|
|
{
|
|
sa->NextSendTick = 0;
|
|
FreeBuf(sa->SendBuffer);
|
|
sa->SendBuffer = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sa->Established && sa->Deleting == false && c != NULL)
|
|
{
|
|
// Get the flexible expiration date of SA for each IKE_CLIENT
|
|
if (sa->ServerToClient)
|
|
{
|
|
c->CurrentExpiresSoftTick_StoC = MAX(c->CurrentExpiresSoftTick_StoC, sa->ExpiresSoftTick);
|
|
c->CurrentNumEstablishedIPsecSA_StoC++;
|
|
|
|
if (sa->ExpiresSoftTick == 0 || sa->ExpiresSoftTick > ike->Now)
|
|
{
|
|
c->CurrentNumHealtyIPsecSA_StoC++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
c->CurrentExpiresSoftTick_CtoS = MAX(c->CurrentExpiresSoftTick_CtoS, sa->ExpiresSoftTick);
|
|
c->CurrentNumEstablishedIPsecSA_CtoS++;
|
|
|
|
if (sa->ExpiresSoftTick == 0 || sa->ExpiresSoftTick > ike->Now)
|
|
{
|
|
c->CurrentNumHealtyIPsecSA_CtoS++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove those of non-communication
|
|
if (sa->IkeClient == NULL || (sa->IkeClient->CurrentIpSecSaRecv != sa && sa->IkeClient->CurrentIpSecSaSend != sa))
|
|
{
|
|
// When the IKE_CLIENT don't point this
|
|
UINT64 last_comm_tick = sa->LastCommTick;
|
|
|
|
if (sa->ServerToClient && sa->PairIPsecSa != NULL)
|
|
{
|
|
last_comm_tick = sa->PairIPsecSa->LastCommTick;
|
|
}
|
|
|
|
if (sa->Established == false)
|
|
{
|
|
// Make time-out in a short time when it is not established
|
|
if ((last_comm_tick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT_FOR_NOT_ESTABLISHED) <= ike->Now)
|
|
{
|
|
WHERE;
|
|
MarkIPsecSaAsDeleted(ike, sa);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Timeout in a long time in the case of established
|
|
if ((last_comm_tick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT) <= ike->Now)
|
|
{
|
|
WHERE;
|
|
MarkIPsecSaAsDeleted(ike, sa);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// IKE_CLIENT scanning process
|
|
for (i = 0;i < LIST_NUM(ike->ClientList);i++)
|
|
{
|
|
IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
|
|
UINT64 tick;
|
|
UCHAR data[1];
|
|
bool need_qm = false;
|
|
bool need_qm_hard = false;
|
|
UINT64 qm_soft_tick = 0;
|
|
|
|
// Determine whether it is necessary to start a new Quick Mode
|
|
if (c->CurrentExpiresSoftTick_StoC != 0 && ike->Now >= c->CurrentExpiresSoftTick_StoC)
|
|
{
|
|
need_qm = true;
|
|
qm_soft_tick = MAX(qm_soft_tick, c->CurrentExpiresSoftTick_StoC);
|
|
}
|
|
|
|
if (c->CurrentExpiresSoftTick_CtoS != 0 && ike->Now >= c->CurrentExpiresSoftTick_CtoS)
|
|
{
|
|
need_qm = true;
|
|
qm_soft_tick = MAX(qm_soft_tick, c->CurrentExpiresSoftTick_StoC);
|
|
}
|
|
|
|
if (c->CurrentNumHealtyIPsecSA_CtoS == 0 || c->CurrentNumHealtyIPsecSA_StoC == 0)
|
|
{
|
|
need_qm = true;
|
|
need_qm_hard = true;
|
|
}
|
|
|
|
if (c->StartQuickModeAsSoon)
|
|
{
|
|
need_qm = true;
|
|
need_qm_hard = true;
|
|
}
|
|
|
|
if (c->Deleting || c->CurrentIkeSa == NULL || c->CurrentIkeSa->Deleting)
|
|
{
|
|
need_qm = false;
|
|
need_qm_hard = true;
|
|
}
|
|
|
|
if (need_qm)
|
|
{
|
|
if (c->StartQuickModeAsSoon || ((c->LastQuickModeStartTick + (UINT64)IKE_QUICKMODE_START_INTERVAL) <= ike->Now))
|
|
{
|
|
// Start the Quick Mode
|
|
Debug("IKE_CLIENT 0x%X: Begin QuickMode\n", c);
|
|
c->StartQuickModeAsSoon = false;
|
|
c->LastQuickModeStartTick = ike->Now;
|
|
|
|
AddInterrupt(ike->Interrupts, c->LastQuickModeStartTick + (UINT64)IKE_QUICKMODE_START_INTERVAL);
|
|
|
|
StartQuickMode(ike, c);
|
|
}
|
|
}
|
|
|
|
if (need_qm_hard)
|
|
{
|
|
if (c->NeedQmBeginTick == 0)
|
|
{
|
|
c->NeedQmBeginTick = ike->Now;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
c->NeedQmBeginTick = 0;
|
|
}
|
|
|
|
if (((c->LastCommTick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT) <= ike->Now) ||
|
|
((c->CurrentIkeSa == NULL && c->CurrentIpSecSaRecv == NULL && c->CurrentIpSecSaSend == NULL) && (c->LastCommTick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT_FOR_NOT_ESTABLISHED) <= ike->Now) ||
|
|
(c->NeedQmBeginTick != 0 && ((c->NeedQmBeginTick + (UINT64)IKE_QUICKMODE_FAILED_TIMEOUT) <= ike->Now)))
|
|
{
|
|
// Remove IKE_CLIENT not communicating for a certain period of time
|
|
WHERE;
|
|
MarkIkeClientAsDeleted(ike, c);
|
|
}
|
|
|
|
// L2TP processing
|
|
if (c->L2TP != NULL)
|
|
{
|
|
IPsecIkeClientManageL2TPServer(ike, c);
|
|
|
|
// Interrupt processing occurs
|
|
L2TPProcessInterrupts(c->L2TP);
|
|
|
|
// Packet transmission
|
|
IPsecIkeClientSendL2TPPackets(ike, c, c->L2TP);
|
|
}
|
|
|
|
// EtherIP processing
|
|
if (c->EtherIP != NULL)
|
|
{
|
|
IPsecIkeClientManageEtherIPServer(ike, c);
|
|
|
|
// Interrupt processing occurs
|
|
EtherIPProcInterrupts(c->EtherIP);
|
|
|
|
// Packet transmission
|
|
IPsecIkeClientSendEtherIPPackets(ike, c, c->EtherIP);
|
|
}
|
|
|
|
// KeepAlive transmission
|
|
tick = MAX(c->LastCommTick + (UINT64)IKE_INTERVAL_UDP_KEEPALIVE, c->NextKeepAliveSendTick);
|
|
|
|
if (tick <= ike->Now && c->ServerPort == IPSEC_PORT_IPSEC_ESP_UDP)
|
|
{
|
|
c->NextKeepAliveSendTick = ike->Now + (UINT64)IKE_INTERVAL_UDP_KEEPALIVE;
|
|
|
|
AddInterrupt(ike->Interrupts, c->NextKeepAliveSendTick);
|
|
|
|
Zero(data, sizeof(data));
|
|
data[0] = 0xff;
|
|
|
|
IkeSendUdpPacket(ike, IKE_UDP_KEEPALIVE, &c->ServerIP, c->ServerPort, &c->ClientIP, c->ClientPort, Clone(data, sizeof(data)), sizeof(data));
|
|
}
|
|
|
|
// DPD transmission
|
|
if (c->NextDpdSendTick == 0 || c->NextDpdSendTick <= ike->Now)
|
|
{
|
|
if (c->CurrentIkeSa != NULL && c->CurrentIkeSa->Established)
|
|
{
|
|
if (c->CurrentIkeSa->Caps.DpdRfc3706)
|
|
{
|
|
c->NextDpdSendTick = ike->Now + (UINT64)IKE_INTERVAL_DPD_KEEPALIVE;
|
|
|
|
AddInterrupt(ike->Interrupts, c->NextDpdSendTick);
|
|
|
|
SendInformationalExchangePacket(ike, c,
|
|
IkeNewNoticeDpdPayload(false, c->CurrentIkeSa->InitiatorCookie, c->CurrentIkeSa->ResponderCookie,
|
|
c->DpdSeqNo++));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
do
|
|
{
|
|
ike->StateHasChanged = false;
|
|
|
|
// Deletion process
|
|
PurgeDeletingSAsAndClients(ike);
|
|
}
|
|
while (ike->StateHasChanged);
|
|
|
|
// Maintenance of the thread list
|
|
MaintainThreadList(ike->ThreadList);
|
|
/*Debug("ike->ThreadList: %u\n", LIST_NUM(ike->ThreadList));
|
|
{
|
|
UINT i;
|
|
for (i = 0;i < LIST_NUM(ike->ThreadList);i++)
|
|
{
|
|
THREAD *t = LIST_DATA(ike->ThreadList, i);
|
|
|
|
Debug(" Thread %u: 0x%p ID: %u Stop: %u Ref: %u\n", i, t, t->ThreadId, t->Stopped, t->ref->c->c);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
// Stop the IKE server
|
|
void StopIKEServer(IKE_SERVER *ike)
|
|
{
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Set the socket events in IKE server
|
|
void SetIKEServerSockEvent(IKE_SERVER *ike, SOCK_EVENT *e)
|
|
{
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (e != NULL)
|
|
{
|
|
AddRef(e->ref);
|
|
}
|
|
|
|
if (ike->SockEvent != NULL)
|
|
{
|
|
ReleaseSockEvent(ike->SockEvent);
|
|
}
|
|
|
|
ike->SockEvent = e;
|
|
}
|
|
|
|
// Release the IKE client
|
|
void FreeIkeClient(IKE_SERVER *ike, IKE_CLIENT *c)
|
|
{
|
|
// Validate arguments
|
|
if (c == NULL || ike == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c->L2TP != NULL)
|
|
{
|
|
StopL2TPServer(c->L2TP, true);
|
|
FreeL2TPServer(c->L2TP);
|
|
}
|
|
|
|
if (c->EtherIP != NULL)
|
|
{
|
|
ReleaseEtherIPServer(c->EtherIP);
|
|
}
|
|
|
|
FreeBuf(c->SendID1_Buf);
|
|
FreeBuf(c->SendID2_Buf);
|
|
|
|
Free(c);
|
|
}
|
|
|
|
// Release the IPsec SA
|
|
void FreeIPsecSa(IPSECSA *sa)
|
|
{
|
|
// Validate arguments
|
|
if (sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IkeFreeKey(sa->CryptoKey);
|
|
|
|
FreeBuf(sa->SendBuffer);
|
|
|
|
FreeBuf(sa->InitiatorRand);
|
|
FreeBuf(sa->ResponderRand);
|
|
|
|
FreeBuf(sa->SharedKey);
|
|
|
|
IkeDhFreeCtx(sa->Dh);
|
|
|
|
Free(sa);
|
|
}
|
|
|
|
// Release the IKE SA
|
|
void FreeIkeSa(IKE_SA *sa)
|
|
{
|
|
// Validate arguments
|
|
if (sa == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FreeBuf(sa->SendBuffer);
|
|
|
|
FreeBuf(sa->InitiatorRand);
|
|
FreeBuf(sa->ResponderRand);
|
|
FreeBuf(sa->DhSharedKey);
|
|
FreeBuf(sa->YourIDPayloadForAM);
|
|
|
|
FreeBuf(sa->GXi);
|
|
FreeBuf(sa->GXr);
|
|
|
|
FreeBuf(sa->SAi_b);
|
|
|
|
IkeFreeKey(sa->CryptoKey);
|
|
|
|
Free(sa);
|
|
}
|
|
|
|
// Release the IKE server
|
|
void FreeIKEServer(IKE_SERVER *ike)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IPsecLog(ike, NULL, NULL, NULL, "LI_STOPPING");
|
|
|
|
for (i = 0;i < LIST_NUM(ike->SendPacketList);i++)
|
|
{
|
|
UDPPACKET *udp = LIST_DATA(ike->SendPacketList, i);
|
|
|
|
FreeUdpPacket(udp);
|
|
}
|
|
|
|
ReleaseList(ike->SendPacketList);
|
|
|
|
Debug("Num of IPsec SAs: %u\n", LIST_NUM(ike->IPsecSaList));
|
|
IPsecLog(ike, NULL, NULL, NULL, "LI_NUM_IPSEC_SA", LIST_NUM(ike->IPsecSaList));
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
|
|
{
|
|
IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
|
|
|
|
FreeIPsecSa(sa);
|
|
}
|
|
|
|
ReleaseList(ike->IPsecSaList);
|
|
|
|
Debug("Num of IKE SAs: %u\n", LIST_NUM(ike->IkeSaList));
|
|
IPsecLog(ike, NULL, NULL, NULL, "LI_NUM_IKE_SA", LIST_NUM(ike->IkeSaList));
|
|
|
|
for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
|
|
{
|
|
IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
|
|
|
|
FreeIkeSa(sa);
|
|
}
|
|
|
|
ReleaseList(ike->IkeSaList);
|
|
|
|
Debug("Num of IKE_CLIENTs: %u\n", LIST_NUM(ike->ClientList));
|
|
IPsecLog(ike, NULL, NULL, NULL, "LI_NUM_IKE_CLIENTS", LIST_NUM(ike->ClientList));
|
|
|
|
for (i = 0;i < LIST_NUM(ike->ClientList);i++)
|
|
{
|
|
IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
|
|
|
|
FreeIkeClient(ike, c);
|
|
}
|
|
|
|
ReleaseList(ike->ClientList);
|
|
|
|
ReleaseSockEvent(ike->SockEvent);
|
|
|
|
IPsecLog(ike, NULL, NULL, NULL, "LI_STOP");
|
|
|
|
ReleaseCedar(ike->Cedar);
|
|
|
|
FreeIkeEngine(ike->Engine);
|
|
|
|
Debug("FreeThreadList()...\n");
|
|
FreeThreadList(ike->ThreadList);
|
|
Debug("FreeThreadList() Done.\n");
|
|
|
|
Free(ike);
|
|
}
|
|
|
|
// Create a new IKE server
|
|
IKE_SERVER *NewIKEServer(CEDAR *cedar, IPSEC_SERVER *ipsec)
|
|
{
|
|
IKE_SERVER *ike;
|
|
// Validate arguments
|
|
if (cedar == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ike = ZeroMalloc(sizeof(IKE_SERVER));
|
|
|
|
ike->Cedar = cedar;
|
|
AddRef(cedar->ref);
|
|
|
|
ike->IPsec = ipsec;
|
|
|
|
ike->Now = Tick64();
|
|
|
|
ike->SendPacketList = NewList(NULL);
|
|
|
|
ike->IkeSaList = NewList(CmpIkeSa);
|
|
|
|
ike->IPsecSaList = NewList(CmpIPsecSa);
|
|
|
|
ike->ClientList = NewList(CmpIkeClient);
|
|
|
|
ike->Engine = NewIkeEngine();
|
|
|
|
ike->ThreadList = NewThreadList();
|
|
|
|
IPsecLog(ike, NULL, NULL, NULL, "LI_START");
|
|
|
|
return ike;
|
|
}
|
|
|
|
|
|
|