|
@@ -294,12 +294,39 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
|
|
|
data = recv_packet->Data;
|
|
|
size = recv_packet->DataSize;
|
|
|
|
|
|
- if (size >= (c->MdRecv->Size + c->CipherDecrypt->IvSize + sizeof(UINT)))
|
|
|
+ if(c->CipherDecrypt->IsAeadCipher)
|
|
|
+ {
|
|
|
+ UCHAR *tag;
|
|
|
+
|
|
|
+ // Update variable part (packet ID) of IV
|
|
|
+ Copy(c->IvRecv, data, sizeof(recv_packet->PacketId));
|
|
|
+ data += sizeof(UINT);
|
|
|
+ size -= sizeof(UINT);
|
|
|
+
|
|
|
+ // tag data
|
|
|
+ tag = data;
|
|
|
+ if(size < OPENVPN_TAG_SIZE) goto decode_error;
|
|
|
+ data += OPENVPN_TAG_SIZE;
|
|
|
+ size -= OPENVPN_TAG_SIZE;
|
|
|
+
|
|
|
+ // Decryption
|
|
|
+ if (size == 0 || (c->CipherDecrypt->BlockSize != 0 && (size % c->CipherDecrypt->BlockSize) != 0)) goto decode_error;
|
|
|
+ size = CipherProcessAead(c->CipherDecrypt, c->IvRecv, tag, OPENVPN_TAG_SIZE, s->TmpBuf, data, size, c->IvRecv, sizeof(UINT));
|
|
|
+ if (size == 0)
|
|
|
+ {
|
|
|
+ Debug("OvsDecrypt(): CipherProcessAead() failed!\n");
|
|
|
+ goto decode_error;
|
|
|
+ }
|
|
|
+ data = s->TmpBuf;
|
|
|
+ }
|
|
|
+ else // Non AEAD cipher
|
|
|
{
|
|
|
UCHAR *hmac;
|
|
|
UCHAR *iv;
|
|
|
UCHAR hmac_test[128];
|
|
|
|
|
|
+ if (size < (c->MdRecv->Size + c->CipherDecrypt->IvSize + sizeof(UINT))) goto decode_error;
|
|
|
+
|
|
|
// HMAC
|
|
|
hmac = data;
|
|
|
data += c->MdRecv->Size;
|
|
@@ -307,67 +334,59 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
|
|
|
|
|
|
// Confirmation of HMAC
|
|
|
MdProcess(c->MdRecv, hmac_test, data, size);
|
|
|
- if (Cmp(hmac_test, hmac, c->MdRecv->Size) == 0)
|
|
|
- {
|
|
|
- // Update of last communication time
|
|
|
- se->LastCommTick = s->Now;
|
|
|
-
|
|
|
- // IV
|
|
|
- iv = data;
|
|
|
- data += c->CipherDecrypt->IvSize;
|
|
|
- size -= c->CipherDecrypt->IvSize;
|
|
|
-
|
|
|
- // Payload
|
|
|
- if (size >= 1 && (c->CipherDecrypt->BlockSize == 0 || (size % c->CipherDecrypt->BlockSize) == 0))
|
|
|
- {
|
|
|
- UINT data_packet_id;
|
|
|
-
|
|
|
- // Decryption
|
|
|
- size = CipherProcess(c->CipherDecrypt, iv, s->TmpBuf, data, size);
|
|
|
+ if (Cmp(hmac_test, hmac, c->MdRecv->Size) != 0) goto decode_error;
|
|
|
+
|
|
|
+ // IV
|
|
|
+ iv = data;
|
|
|
+ data += c->CipherDecrypt->IvSize;
|
|
|
+ size -= c->CipherDecrypt->IvSize;
|
|
|
+
|
|
|
+ // Decryption
|
|
|
+ if (size == 0 || (c->CipherDecrypt->BlockSize != 0 && (size % c->CipherDecrypt->BlockSize) != 0)) goto decode_error;
|
|
|
+ size = CipherProcess(c->CipherDecrypt, iv, s->TmpBuf, data, size);
|
|
|
+ if(size == 0) goto decode_error;
|
|
|
+
|
|
|
+ data = s->TmpBuf;
|
|
|
+ // Seek buffer after the packet ID
|
|
|
+ data += sizeof(UINT);
|
|
|
+ size -= sizeof(UINT);
|
|
|
+ }
|
|
|
|
|
|
- data = s->TmpBuf;
|
|
|
+ // Update of last communication time
|
|
|
+ se->LastCommTick = s->Now;
|
|
|
|
|
|
- if (size >= sizeof(UINT))
|
|
|
- {
|
|
|
- data_packet_id = READ_UINT(data);
|
|
|
+ if (size < sizeof(UINT)){
|
|
|
+ goto decode_error;
|
|
|
+ }
|
|
|
|
|
|
- data += sizeof(UINT);
|
|
|
- size -= sizeof(UINT);
|
|
|
|
|
|
- if (size >= sizeof(ping_signature) &&
|
|
|
- Cmp(data, ping_signature, sizeof(ping_signature)) == 0)
|
|
|
- {
|
|
|
- // Ignore since a ping packet has been received
|
|
|
- DoNothing();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // Receive a packet!!
|
|
|
- if (se->Ipc != NULL)
|
|
|
- {
|
|
|
- switch (se->Mode)
|
|
|
- {
|
|
|
- case OPENVPN_MODE_L2: // Send an Ethernet packet to a session
|
|
|
- IPCSendL2(se->Ipc, data, size);
|
|
|
- break;
|
|
|
+ if (size >= sizeof(ping_signature) &&
|
|
|
+ Cmp(data, ping_signature, sizeof(ping_signature)) == 0)
|
|
|
+ {
|
|
|
+ // Ignore since a ping packet has been received
|
|
|
+ DoNothing();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Receive a valid data packet!!
|
|
|
+ if (se->Ipc != NULL)
|
|
|
+ {
|
|
|
+ switch (se->Mode)
|
|
|
+ {
|
|
|
+ case OPENVPN_MODE_L2: // Send an Ethernet packet to a session
|
|
|
+ IPCSendL2(se->Ipc, data, size);
|
|
|
+ break;
|
|
|
|
|
|
- case OPENVPN_MODE_L3: // Send an IPv4 packet to a session
|
|
|
- IPCSendIPv4(se->Ipc, data, size);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ case OPENVPN_MODE_L3: // Send an IPv4 packet to a session
|
|
|
+ IPCSendIPv4(se->Ipc, data, size);
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
-// Debug("HMAC Failed (c=%u)\n", c->KeyId);
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+decode_error:
|
|
|
|
|
|
OvsFreePacket(recv_packet);
|
|
|
}
|
|
@@ -770,6 +789,7 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
|
|
|
LIST *o;
|
|
|
BUF *b;
|
|
|
char opt_str[MAX_SIZE];
|
|
|
+ char *cipher_name, *md_name;
|
|
|
// Validate arguments
|
|
|
if (s == NULL || se == NULL || c == NULL || data == NULL)
|
|
|
{
|
|
@@ -871,14 +891,6 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Encryption algorithm
|
|
|
- c->CipherEncrypt = OvsGetCipher(IniStrValue(o, "cipher"));
|
|
|
- c->CipherDecrypt = NewCipher(c->CipherEncrypt->Name);
|
|
|
-
|
|
|
- // Hash algorithm
|
|
|
- c->MdSend = OvsGetMd(IniStrValue(o, "auth"));
|
|
|
- c->MdRecv = NewMd(c->MdSend->Name);
|
|
|
-
|
|
|
// Random number generation
|
|
|
Rand(c->ServerKey.Random1, sizeof(c->ServerKey.Random1));
|
|
|
Rand(c->ServerKey.Random2, sizeof(c->ServerKey.Random2));
|
|
@@ -904,13 +916,45 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
|
|
|
c->ExpansionKey, sizeof(c->ExpansionKey));
|
|
|
FreeBuf(b);
|
|
|
|
|
|
- // Set the key
|
|
|
+ // Encryption algorithm
|
|
|
+ cipher_name = IniStrValue(o, "cipher");
|
|
|
+ c->CipherEncrypt = OvsGetCipher(cipher_name);
|
|
|
+ c->CipherDecrypt = OvsGetCipher(cipher_name);
|
|
|
SetCipherKey(c->CipherDecrypt, c->ExpansionKey + 0, false);
|
|
|
SetCipherKey(c->CipherEncrypt, c->ExpansionKey + 128, true);
|
|
|
- SetMdKey(c->MdRecv, c->ExpansionKey + 64, c->MdRecv->Size);
|
|
|
- SetMdKey(c->MdSend, c->ExpansionKey + 192, c->MdSend->Size);
|
|
|
|
|
|
- OvsFreeOptions(o);
|
|
|
+ md_name = IniStrValue(o, "auth");
|
|
|
+ if(c->CipherDecrypt->IsAeadCipher){
|
|
|
+ // In AEAD mode the IV is composed by the packet ID and a part of the HMAC key
|
|
|
+ Copy(c->IvRecv + sizeof(c->LastDataPacketId), c->ExpansionKey + 64, c->CipherDecrypt->IvSize - sizeof(c->LastDataPacketId));
|
|
|
+ Copy(c->IvSend + sizeof(c->LastDataPacketId), c->ExpansionKey + 192, c->CipherEncrypt->IvSize - sizeof(c->LastDataPacketId));
|
|
|
+ c->MdSend = NULL;
|
|
|
+ c->MdRecv = NULL;
|
|
|
+ }else{
|
|
|
+ // Hash algorithm
|
|
|
+ c->MdSend = OvsGetMd(md_name);
|
|
|
+ c->MdRecv = OvsGetMd(md_name);
|
|
|
+ SetMdKey(c->MdRecv, c->ExpansionKey + 64, c->MdRecv->Size);
|
|
|
+ SetMdKey(c->MdSend, c->ExpansionKey + 192, c->MdSend->Size);
|
|
|
+ }
|
|
|
+
|
|
|
+ // We pass the cipher name sent from the OpenVPN client, unless it's a different cipher, to prevent a message such as:
|
|
|
+ // WARNING: 'cipher' is used inconsistently, local='cipher AES-128-GCM', remote='cipher aes-128-gcm'
|
|
|
+ // It happens because OpenVPN uses "strcmp()" to compare the local and remote parameters:
|
|
|
+ // https://github.com/OpenVPN/openvpn/blob/a6fd48ba36ede465b0905a95568c3ec0d425ca71/src/openvpn/options.c#L3819-L3831
|
|
|
+ if (StrCmpi(cipher_name, c->CipherEncrypt->Name) != 0)
|
|
|
+ {
|
|
|
+ cipher_name = c->CipherEncrypt->Name;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(c->MdSend != NULL){
|
|
|
+ if (StrCmpi(md_name, c->MdSend->Name) != 0)
|
|
|
+ {
|
|
|
+ md_name = c->MdSend->Name;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ md_name = "[null-digest]";
|
|
|
+ }
|
|
|
|
|
|
// Generate the response option string
|
|
|
Format(c->ServerKey.OptionString, sizeof(c->ServerKey.OptionString),
|
|
@@ -920,9 +964,11 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
|
|
|
se->LinkMtu,
|
|
|
se->TunMtu,
|
|
|
c->Proto,
|
|
|
- c->CipherEncrypt->Name, c->MdSend->Name, c->CipherEncrypt->KeySize * 8);
|
|
|
+ cipher_name, md_name, c->CipherEncrypt->KeySize * 8);
|
|
|
Debug("Building OptionStr: %s\n", c->ServerKey.OptionString);
|
|
|
|
|
|
+ OvsFreeOptions(o);
|
|
|
+
|
|
|
OvsLog(s, se, c, "LO_OPTION_STR_SEND", c->ServerKey.OptionString);
|
|
|
}
|
|
|
|
|
@@ -931,7 +977,7 @@ CIPHER *OvsGetCipher(char *name)
|
|
|
{
|
|
|
CIPHER *c = NULL;
|
|
|
|
|
|
- if (IsEmptyStr(name) == false && IsStrInStrTokenList(OPENVPN_CIPHER_LIST, name, NULL, false))
|
|
|
+ if (IsEmptyStr(name) == false)
|
|
|
{
|
|
|
c = NewCipher(name);
|
|
|
}
|
|
@@ -949,7 +995,7 @@ MD *OvsGetMd(char *name)
|
|
|
{
|
|
|
MD *m = NULL;
|
|
|
|
|
|
- if (IsEmptyStr(name) == false && IsStrInStrTokenList(OPENVPN_MD_LIST, name, NULL, false))
|
|
|
+ if (IsEmptyStr(name) == false)
|
|
|
{
|
|
|
m = NewMd(name);
|
|
|
}
|
|
@@ -1419,7 +1465,8 @@ OPENVPN_CHANNEL *OvsNewChannel(OPENVPN_SESSION *se, UCHAR key_id)
|
|
|
|
|
|
c->KeyId = key_id;
|
|
|
|
|
|
- Rand(c->NextIv, sizeof(c->NextIv));
|
|
|
+ Rand(c->IvSend, sizeof(c->IvSend));
|
|
|
+ Rand(c->IvRecv, sizeof(c->IvRecv));
|
|
|
|
|
|
//c->NextRekey = se->Server->Now + (UINT64)5000;
|
|
|
|
|
@@ -1467,9 +1514,7 @@ UINT64 OvsNewServerSessionId(OPENVPN_SERVER *s)
|
|
|
// Build and submit the OpenVPN data packet
|
|
|
void OvsSendDataPacket(OPENVPN_CHANNEL *c, UCHAR key_id, UINT data_packet_id, void *data, UINT data_size)
|
|
|
{
|
|
|
- UCHAR uc;
|
|
|
- UCHAR *encrypted_data;
|
|
|
- UINT encrypted_size;
|
|
|
+ const UCHAR op = ((OPENVPN_P_DATA_V1 << 3) & 0xF8) | (key_id & 0x07);
|
|
|
UCHAR *dest_data;
|
|
|
UINT dest_size;
|
|
|
UINT r;
|
|
@@ -1480,40 +1525,68 @@ void OvsSendDataPacket(OPENVPN_CHANNEL *c, UCHAR key_id, UINT data_packet_id, vo
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- uc = ((OPENVPN_P_DATA_V1 << 3) & 0xF8) | (key_id & 0x07);
|
|
|
-
|
|
|
// Generate the data to be encrypted
|
|
|
+ if (c->CipherEncrypt->IsAeadCipher){
|
|
|
+ // [ opcode ] [ - packet ID - ] [ TAG ] [ * packet payload * ]
|
|
|
+ UCHAR tag[16];
|
|
|
+ UINT ret;
|
|
|
+
|
|
|
+ // Update variable part (packet ID) of IV
|
|
|
+ WRITE_UINT(c->IvSend, data_packet_id);
|
|
|
+
|
|
|
+ // Prepare a buffer to store the results
|
|
|
+ dest_data = Malloc(sizeof(op) + sizeof(data_packet_id) + sizeof(tag) + data_size + 256);
|
|
|
|
|
|
- encrypted_size = sizeof(UINT) + data_size;
|
|
|
- encrypted_data = ZeroMalloc(encrypted_size);
|
|
|
+ // Set data size to the maximum known
|
|
|
+ dest_size = sizeof(op) + sizeof(data_packet_id) + sizeof(tag);
|
|
|
|
|
|
- WRITE_UINT(encrypted_data, data_packet_id);
|
|
|
- Copy(encrypted_data + sizeof(UINT), data, data_size);
|
|
|
+ // Write opcode
|
|
|
+ dest_data[0] = op;
|
|
|
|
|
|
- // Prepare a buffer to store the results
|
|
|
- dest_data = Malloc(sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize + encrypted_size + 256);
|
|
|
+ // Write packet ID
|
|
|
+ WRITE_UINT(dest_data + sizeof(op), data_packet_id);
|
|
|
|
|
|
- // Encrypt
|
|
|
- r = CipherProcess(c->CipherEncrypt, c->NextIv, dest_data + sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize,
|
|
|
- encrypted_data, encrypted_size);
|
|
|
- dest_size = sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize + r;
|
|
|
+ // Write encrypted payload
|
|
|
+ ret = CipherProcessAead(c->CipherEncrypt, c->IvSend, tag, 16, dest_data + dest_size, data, data_size, c->IvSend, sizeof(data_packet_id));
|
|
|
+ if (ret == 0)
|
|
|
+ {
|
|
|
+ Debug("OvsEncrypt(): CipherProcessAead() failed!\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ dest_size += ret;
|
|
|
|
|
|
- // Copy the IV
|
|
|
- Copy(dest_data + sizeof(UCHAR) + c->MdSend->Size, c->NextIv, c->CipherEncrypt->IvSize);
|
|
|
+ // Write authentication tag
|
|
|
+ Copy(dest_data + sizeof(op) + sizeof(data_packet_id), tag, sizeof(tag));
|
|
|
+ }else{
|
|
|
+ // [ opcode ] [ HMAC ] [ - IV - ] [ * packet ID * ] [ * packet payload * ]
|
|
|
+ UINT encrypted_size = sizeof(data_packet_id) + data_size;
|
|
|
+ UCHAR *encrypted_data = ZeroMalloc(encrypted_size);
|
|
|
+ WRITE_UINT(encrypted_data, data_packet_id);
|
|
|
+ Copy(encrypted_data + sizeof(data_packet_id), data, data_size);
|
|
|
|
|
|
- // Calculate the HMAC
|
|
|
- MdProcess(c->MdSend, dest_data + sizeof(UCHAR), dest_data + sizeof(UCHAR) + c->MdSend->Size,
|
|
|
- dest_size - sizeof(UCHAR) - c->MdSend->Size);
|
|
|
+ // Prepare a buffer to store the results
|
|
|
+ dest_data = Malloc(sizeof(op) + c->MdSend->Size + c->CipherEncrypt->IvSize + encrypted_size + 256);
|
|
|
+ dest_data[0] = op;
|
|
|
|
|
|
- // Update the NextIV
|
|
|
- Copy(c->NextIv, dest_data + dest_size - c->CipherEncrypt->IvSize, c->CipherEncrypt->IvSize);
|
|
|
+ // Encrypt
|
|
|
+ r = CipherProcess(c->CipherEncrypt, c->IvSend, dest_data + sizeof(op) + c->MdSend->Size + c->CipherEncrypt->IvSize,
|
|
|
+ encrypted_data, encrypted_size);
|
|
|
+ Free(encrypted_data);
|
|
|
|
|
|
- // Op-code
|
|
|
- dest_data[0] = uc;
|
|
|
+ dest_size = sizeof(op) + c->MdSend->Size + c->CipherEncrypt->IvSize + r;
|
|
|
|
|
|
- OvsSendPacketRawNow(c->Server, c->Session, dest_data, dest_size);
|
|
|
+ // Copy the IV
|
|
|
+ Copy(dest_data + sizeof(op) + c->MdSend->Size, c->IvSend, c->CipherEncrypt->IvSize);
|
|
|
+
|
|
|
+ // Calculate the HMAC
|
|
|
+ MdProcess(c->MdSend, dest_data + sizeof(op), dest_data + sizeof(op) + c->MdSend->Size,
|
|
|
+ dest_size - sizeof(op) - c->MdSend->Size);
|
|
|
|
|
|
- Free(encrypted_data);
|
|
|
+ // Update the IV for next
|
|
|
+ Copy(c->IvSend, dest_data + dest_size - c->CipherEncrypt->IvSize, c->CipherEncrypt->IvSize);
|
|
|
+ }
|
|
|
+
|
|
|
+ OvsSendPacketRawNow(c->Server, c->Session, dest_data, dest_size);
|
|
|
}
|
|
|
|
|
|
// Build an OpenVPN control packet
|