1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-26 19:39:53 +03:00
SoftEtherVPN/src/Cedar/Proto_PPP.c

3810 lines
94 KiB
C
Raw Normal View History

2017-10-19 05:48:23 +03:00
// SoftEther VPN Source Code - Developer Edition Master Branch
2014-01-04 17:00:08 +04:00
// Cedar Communication Module
// Proto_PPP.c
2014-01-04 17:00:08 +04:00
// PPP protocol stack
#include "CedarPch.h"
// PPP main thread
2014-01-04 17:00:08 +04:00
void PPPThread(THREAD *thread, void *param)
{
PPP_SESSION *p = (PPP_SESSION *)param;
UINT i;
PPP_LCP *c;
USHORT us;
UINT ui;
USHORT next_protocol = 0;
bool ret = false;
char ipstr1[128], ipstr2[128];
bool authReqSent = false;
UINT64 now = Tick64();
2014-01-04 17:00:08 +04:00
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
// Initialize
Debug("PPP Initialize");
PPPSetStatus(p, PPP_STATUS_CONNECTED);
p->IPv4_State = PPP_PROTO_STATUS_CLOSED;
p->IPv6_State = PPP_PROTO_STATUS_CLOSED;
p->Eap_Protocol = PPP_UNSPECIFIED;
2014-01-04 17:00:08 +04:00
p->Mru1 = p->Mru2 = PPP_MRU_DEFAULT;
p->RecvPacketList = NewList(NULL);
p->SentReqPacketList = NewList(NULL);
p->DelayedPackets = NewList(PPPDelayedPacketsComparator);
2014-01-04 17:00:08 +04:00
2015-10-06 14:18:00 +03:00
p->MsChapV2_UseDoubleMsChapV2 = CedarIsThereAnyEapEnabledRadiusConfig(p->Cedar);
Debug("MsChapV2_UseDoubleMsChapV2 = 0x%x\n", p->MsChapV2_UseDoubleMsChapV2);
2014-01-04 17:00:08 +04:00
//// Link establishment phase
Debug("PPP Link establishment phase\n");
2014-01-04 17:00:08 +04:00
IPToStr(ipstr1, sizeof(ipstr1), &p->ClientIP);
IPToStr(ipstr2, sizeof(ipstr2), &p->ServerIP);
PPPLog(p, "LP_CONNECTED", p->Postfix, ipstr1, p->ClientHostname, p->ClientPort, ipstr2, p->ServerPort,
p->ClientSoftwareName, p->AdjustMss);
// We need that so we don't time out on connection immediately
p->LastRecvTime = Tick64();
Debug("PPP starting main dataloop\n");
// Dataloop active if the receiving tube is still connected
while (true)
2014-01-04 17:00:08 +04:00
{
PPP_LCP *lcp;
bool receivedPacketProcessed = false;
TUBE *tubes[2];
UINT r;
PPPGetNextPacket(p);
if (p->CurrentPacket != NULL)
2014-01-04 17:00:08 +04:00
{
// First we process any possible unsupported packets
receivedPacketProcessed = PPPRejectUnsupportedPacket(p, p->CurrentPacket);
2014-01-04 17:00:08 +04:00
// Now do some basic processing
if (!receivedPacketProcessed && p->CurrentPacket->IsControl && p->CurrentPacket->Protocol == PPP_PROTOCOL_LCP)
2014-01-04 17:00:08 +04:00
{
if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_ECHO_REQUEST && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus))
{
// Immediately return the echo response to the echo request
PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET));
2014-01-04 17:00:08 +04:00
pp2->IsControl = true;
pp2->Protocol = PPP_PROTOCOL_LCP;
pp2->Lcp = NewPPPLCP(PPP_LCP_CODE_ECHO_RESPONSE, p->CurrentPacket->Lcp->Id);
pp2->Lcp->Data = Clone(p->CurrentPacket->Lcp->Data, p->CurrentPacket->Lcp->DataSize);
pp2->Lcp->DataSize = p->CurrentPacket->Lcp->DataSize;
2014-01-04 17:00:08 +04:00
if (PPPSendPacketAndFree(p, pp2) == false)
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
2014-01-04 17:00:08 +04:00
}
receivedPacketProcessed = true;
2014-01-04 17:00:08 +04:00
}
else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_ECHO_RESPONSE && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus))
2014-01-04 17:00:08 +04:00
{
receivedPacketProcessed = true;
// Ignore the Echo response packet
2014-01-04 17:00:08 +04:00
}
else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_DROP && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus))
{
receivedPacketProcessed = true;
// Ignore the Drop packet
}
else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_IDENTIFICATION && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus))
{
receivedPacketProcessed = true;
// Ignore the Identification packet
WHERE;
}
else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_TERMINATE_REQ)
{
PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET));;
receivedPacketProcessed = true;
// Return the Terminate ACK If a Terminate Request has been received
2014-01-04 17:00:08 +04:00
pp2->IsControl = true;
pp2->Protocol = PPP_PROTOCOL_LCP;
pp2->Lcp = NewPPPLCP(PPP_LCP_CODE_TERMINATE_ACK, p->CurrentPacket->Lcp->Id);
pp2->Lcp->Data = Clone(p->CurrentPacket->Lcp->Data, p->CurrentPacket->Lcp->DataSize);
pp2->Lcp->DataSize = p->CurrentPacket->Lcp->DataSize;
2014-01-04 17:00:08 +04:00
p->IsTerminateReceived = true;
2014-01-04 17:00:08 +04:00
if (PPPSendPacketAndFree(p, pp2) == false)
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
}
else
{
SleepThread(100);
PPPSetStatus(p, PPP_STATUS_CLOSED);
}
}
else if (p->CurrentPacket->Lcp->Code == PPP_LCP_CODE_TERMINATE_ACK)
{
PPPSetStatus(p, PPP_STATUS_CLOSED);
}
}
2014-01-04 17:00:08 +04:00
// Process responses
if (!receivedPacketProcessed && p->CurrentPacket != NULL && p->CurrentPacket->IsControl && PPP_CODE_IS_RESPONSE(p->CurrentPacket->Protocol, p->CurrentPacket->Lcp->Code) && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus))
{
PPP_PACKET* request = NULL;
// Removing from resend list
for (i = 0; i < LIST_NUM(p->SentReqPacketList); i++)
{
PPP_REQUEST_RESEND *t = LIST_DATA(p->SentReqPacketList, i);
2014-01-04 17:00:08 +04:00
if (t->Id == p->CurrentPacket->Lcp->Id)
{
request = t->Packet;
Delete(p->SentReqPacketList, t);
Free(t);
break;
}
}
PPPProcessResponsePacket(p, p->CurrentPacket, request);
FreePPPPacket(request);
receivedPacketProcessed = true;
}
2014-01-04 17:00:08 +04:00
// Process requests
if (!receivedPacketProcessed && p->CurrentPacket != NULL && p->CurrentPacket->IsControl && PPP_CODE_IS_REQUEST(p->CurrentPacket->Protocol, p->CurrentPacket->Lcp->Code) && !PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus))
2014-01-04 17:00:08 +04:00
{
PPPProcessRequestPacket(p, p->CurrentPacket);
receivedPacketProcessed = true;
2014-01-04 17:00:08 +04:00
}
2015-10-06 14:18:00 +03:00
// Process data packets, discarded before we got any links up
if (!receivedPacketProcessed && p->CurrentPacket != NULL && !p->CurrentPacket->IsControl && p->PPPStatus == PPP_STATUS_NETWORK_LAYER && p->Ipc != NULL)
2015-10-06 14:18:00 +03:00
{
UINT64 timeBeforeLoop = Tick64();
while (true)
2015-10-06 14:18:00 +03:00
{
UINT64 nowL;
// Here client to server
if (p->CurrentPacket->Protocol == PPP_PROTOCOL_IP && p->IPv4_State == PPP_PROTO_STATUS_OPENED)
2015-10-06 14:18:00 +03:00
{
receivedPacketProcessed = true;
IPCSendIPv4(p->Ipc, p->CurrentPacket->Data, p->CurrentPacket->DataSize);
}
else if (p->CurrentPacket->Protocol == PPP_PROTOCOL_IP)
{
Debug("Got IPv4 packet before IPv4 ready!\n");
}
else if (p->CurrentPacket->Protocol == PPP_PROTOCOL_IPV6 && p->IPv6_State == PPP_PROTO_STATUS_OPENED)
{
receivedPacketProcessed = true;
Debug("IPv6 to be implemented\n");
}
else if (p->CurrentPacket->Protocol == PPP_PROTOCOL_IPV6)
{
Debug("Got IPv6 packet before IPv6 ready!\n");
}
2015-10-06 14:18:00 +03:00
// Let's break out of the loop once in a while so we don't get stuck here endlessly
nowL = Tick64();
if (nowL > timeBeforeLoop + PPP_PACKET_RESEND_INTERVAL)
{
break;
2015-10-06 14:18:00 +03:00
}
PPPGetNextPacket(p);
if (p->CurrentPacket == NULL)
{
break;
}
// Making sure we got a correctly parsed packet by rejecting all invalid ones
if (PPPRejectUnsupportedPacket(p, p->CurrentPacket))
{
break;
}
if (p->CurrentPacket->IsControl || p->PPPStatus != PPP_STATUS_NETWORK_LAYER || p->Ipc == NULL)
{
PPPAddNextPacket(p, p->CurrentPacket, 0);
p->CurrentPacket = NULL;
break;
}
2015-10-06 14:18:00 +03:00
}
}
if (!receivedPacketProcessed && p->CurrentPacket != NULL)
{
Debug("Unprocessed and unrejected packet, protocol = 0x%x\n", p->CurrentPacket->Protocol);
}
2015-10-06 14:18:00 +03:00
}
else if (p->PPPStatus == PPP_STATUS_BEFORE_AUTH && p->AuthProtocol == PPP_PROTOCOL_EAP)
{
PPP_LCP* lcpEap;
PPP_EAP* eapPacket;
UCHAR* welcomeMessage = "Welcome to the SoftEther VPN server!";
2020-05-02 10:11:01 +03:00
UCHAR flags = PPP_EAP_TLS_FLAG_NONE;
// We got to start EAP when we got no LCP packets from the client on previous iteration
// which means we parsed all the client requests and responses
switch (p->Eap_Protocol)
{
case PPP_EAP_TYPE_TLS:
// Sending TLS Start...
2020-05-02 10:11:01 +03:00
flags |= PPP_EAP_TLS_FLAG_SSLSTARTED;
lcpEap = BuildEAPTlsRequest(p->Eap_PacketId++, 0, flags);
PPPSetStatus(p, PPP_STATUS_AUTHENTICATING);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcpEap))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
break;
}
break;
case PPP_EAP_TYPE_IDENTITY:
default: // We treat the unspecified protocol as the IDENTITY protocol
p->Eap_Protocol = PPP_EAP_TYPE_IDENTITY;
lcpEap = BuildEAPPacketEx(PPP_EAP_CODE_REQUEST, p->Eap_PacketId++, PPP_EAP_TYPE_IDENTITY, sizeof(welcomeMessage));
eapPacket = lcpEap->Data;
Copy(eapPacket->Data, welcomeMessage, sizeof(welcomeMessage));
PPPSetStatus(p, PPP_STATUS_AUTHENTICATING);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcpEap))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
break;
}
break;
}
}
else if (p->PPPStatus == PPP_STATUS_BEFORE_AUTH && p->AuthProtocol == PPP_PROTOCOL_CHAP)
2015-10-06 14:18:00 +03:00
{
// We got to start CHAP when we got no LCP packets from the client on previous iteration
// which means we parsed all the client requests and responses
Debug("Starting PPP Authentication phase MS-CHAP v2\n");
lcp = BuildMSCHAP2ChallengePacket(p);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_CHAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
}
PPPSetStatus(p, PPP_STATUS_AUTHENTICATING);
2015-10-06 14:18:00 +03:00
}
if (p->PPPStatus == PPP_STATUS_CONNECTED && !authReqSent)
2015-10-06 14:18:00 +03:00
{
// EAP code
PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
2020-05-02 10:11:01 +03:00
USHORT eap_code = Endian16(PPP_LCP_AUTH_EAP);
Debug("Request EAP\n");
Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &eap_code, sizeof(eap_code)));
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
}
authReqSent = true;
2015-10-06 14:18:00 +03:00
}
2014-01-04 17:00:08 +04:00
if (p->PPPStatus == PPP_STATUS_AUTHENTICATING)
2014-01-04 17:00:08 +04:00
{
2020-05-02 10:11:01 +03:00
//Debug("Tick waiting for auth...\n");
2014-01-04 17:00:08 +04:00
}
if (p->PPPStatus == PPP_STATUS_AUTH_FAIL)
2014-01-04 17:00:08 +04:00
{
Debug("PPP auth failed, giving up\n");
p->DisconnectCauseCode = 15;
p->DisconnectCauseDirection = 1;
PPPSetStatus(p, PPP_STATUS_CLOSING);
2014-01-04 17:00:08 +04:00
}
if (p->PPPStatus == PPP_STATUS_NETWORK_LAYER)
2014-01-04 17:00:08 +04:00
{
UINT64 timeBeforeLoop;
2014-01-04 17:00:08 +04:00
if (p->DhcpAllocated)
{
if (now >= p->DhcpNextRenewTime)
{
IP ip;
// DHCP renewal procedure
p->DhcpNextRenewTime = now + p->DhcpRenewInterval;
UINTToIP(&ip, p->ClientAddressOption.ServerAddress);
IPCDhcpRenewIP(p->Ipc, &ip);
}
}
IPCProcessL3Events(p->Ipc);
timeBeforeLoop = Tick64();
2014-01-04 17:00:08 +04:00
while (true)
{
UINT64 nowL;
2014-01-04 17:00:08 +04:00
BLOCK *b = IPCRecvIPv4(p->Ipc);
PPP_PACKET *pp;
PPP_PACKET tmp;
if (b == NULL)
{
break;
}
// Since receiving the IP packet, send it to the client by PPP
pp = &tmp;
pp->IsControl = false;
pp->Protocol = PPP_PROTOCOL_IP;
pp->Lcp = NULL;
pp->Data = b->Buf;
pp->DataSize = b->Size;
PPPSendPacketEx(p, pp, true);
FreePPPPacketEx(pp, true);
Free(b);
// Let's break out of the loop once in a while so we don't get stuck here endlessly
nowL = Tick64();
if (nowL > timeBeforeLoop + PPP_PACKET_RESEND_INTERVAL)
{
break;
}
2014-01-04 17:00:08 +04:00
}
FlushTubeFlushList(p->FlushList);
}
2014-01-04 17:00:08 +04:00
if (p->PPPStatus == PPP_STATUS_AUTH_SUCCESS)
{
Debug("PPP auth success, ready for network layer on next tick\n");
p->AuthOk = true;
PPPSetStatus(p, PPP_STATUS_NETWORK_LAYER);
}
2014-01-04 17:00:08 +04:00
if ((p->PPPStatus == PPP_STATUS_CLOSING || p->PPPStatus == PPP_STATUS_FAIL) && IsTubeConnected(p->TubeRecv) && IsTubeConnected(p->TubeSend))
{
Debug("Trying to cleanly close the connection, status = 0x%x\n", p->PPPStatus);
PPPSetStatus(p, PPP_STATUS_CLOSING_WAIT);
lcp = NewPPPLCP(PPP_LCP_CODE_TERMINATE_REQ, 0);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, lcp))
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
if (!PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus) || p->PPPStatus == PPP_STATUS_CLOSING_WAIT)
{
PPPProcessRetransmissions(p);
PPPSendEchoRequest(p);
}
2014-01-04 17:00:08 +04:00
tubes[0] = p->TubeRecv;
if (p->PPPStatus == PPP_STATUS_NETWORK_LAYER && p->Ipc != NULL && IsIPCConnected(p->Ipc))
{
r = GetNextIntervalForInterrupt(p->Ipc->Interrupt);
2014-01-04 17:00:08 +04:00
tubes[1] = p->Ipc->Sock->RecvTube;
WaitForTubes(tubes, 2, MIN(r, PPP_PACKET_RESEND_INTERVAL));
}
else
{
WaitForTubes(tubes, 1, 300); // Increasing timeout to make the ticks a bit slower
}
2014-01-04 17:00:08 +04:00
if (IsTubeConnected(p->TubeRecv) == false || IsTubeConnected(p->TubeSend) == false)
{
// Higher-level protocol is disconnected
PPPLog(p, "LP_UPPER_PROTOCOL_DISCONNECTED", p->Postfix);
break;
2014-01-04 17:00:08 +04:00
}
if (IsIPCConnected(p->Ipc) == false && p->PPPStatus == PPP_STATUS_NETWORK_LAYER)
{
// IPC VPN session is disconnected
PPPLog(p, "LP_VPN_SESSION_TERMINATED");
break;
}
// Time-out inspection
if ((p->LastRecvTime + (UINT64)p->DataTimeout) <= now)
{
// Communication time-out occurs
PPPLog(p, "LP_DATA_TIMEOUT");
break;
}
// Maximum PPP session time of the user reached inspection
if (p->UserConnectionTick != 0 && p->UserConnectionTimeout != 0 &&
p->UserConnectionTick + p->UserConnectionTimeout <= now)
{
// User connection time-out occurs
PPPLog(p, "LP_USER_TIMEOUT");
break;
}
// Terminate if the PPP disconnected
if (p->IsTerminateReceived)
{
PPPLog(p, "LP_NORMAL_TERMINATE");
break;
}
if (p->PPPStatus == PPP_STATUS_FAIL || p->PPPStatus == PPP_STATUS_CLOSED)
{
Debug("Exiting main dataloop, status = 0x%x\n", p->PPPStatus);
break;
}
2014-01-04 17:00:08 +04:00
}
Debug("Exited main dataloop, status = 0x%x\n", p->PPPStatus);
if (p->PPPStatus != PPP_STATUS_FAIL)
2014-01-04 17:00:08 +04:00
{
IP ip;
char tmp[MAX_SIZE];
// Disconnected normally
PPPLog(p, "LP_DISCONNECTED");
2014-01-04 17:00:08 +04:00
if (p != NULL && p->DhcpAllocated && IsIPCConnected(p->Ipc) && p->ClientAddressOption.ServerAddress != 0)
{
// If any address is assigned from the DHCP, release it
UINTToIP(&ip, p->ClientAddressOption.ServerAddress);
2014-01-04 17:00:08 +04:00
IPToStr(tmp, sizeof(tmp), &ip);
Debug("Releasing IP Address from DHCP Server %s...\n", tmp);
2014-01-04 17:00:08 +04:00
IPCDhcpFreeIP(p->Ipc, &ip);
IPCProcessL3Events(p->Ipc);
2014-01-04 17:00:08 +04:00
SleepThread(300);
}
2014-01-04 17:00:08 +04:00
}
else
2014-01-04 17:00:08 +04:00
{
PPPLog(p, "LP_DISCONNECTED_ABNORMAL");
2014-01-04 17:00:08 +04:00
}
FreePPPSession(p);
Debug("PPP Session ended correctly\n");
2014-01-04 17:00:08 +04:00
}
// Entry point
// Create a new PPP session
PPP_SESSION *NewPPPSession(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, TUBE *send_tube, TUBE *recv_tube, char *postfix, char *client_software_name, char *client_hostname, char *crypt_name, UINT adjust_mss)
2014-01-04 17:00:08 +04:00
{
PPP_SESSION *p;
THREAD *t;
2014-01-04 17:00:08 +04:00
// Validate arguments
if (cedar == NULL || client_ip == NULL || server_ip == NULL || send_tube == NULL || recv_tube == NULL)
2014-01-04 17:00:08 +04:00
{
return NULL;
2014-01-04 17:00:08 +04:00
}
if (IsEmptyStr(postfix))
2014-01-04 17:00:08 +04:00
{
postfix = "PPP";
2014-01-04 17:00:08 +04:00
}
if (IsEmptyStr(crypt_name))
{
crypt_name = "";
}
if (IsEmptyStr(client_software_name))
2014-01-04 17:00:08 +04:00
{
client_software_name = "PPP VPN Client";
}
2014-01-04 17:00:08 +04:00
// Data structure initialization
p = ZeroMalloc(sizeof(PPP_SESSION));
2014-01-04 17:00:08 +04:00
p->EnableMSCHAPv2 = true;
p->AuthProtocol = PPP_UNSPECIFIED;
p->MsChapV2_ErrorCode = 691;
p->EapClient = NULL;
2014-01-04 17:00:08 +04:00
p->DataTimeout = PPP_DATA_TIMEOUT;
p->PacketRecvTimeout = PPP_PACKET_RECV_TIMEOUT;
p->UserConnectionTimeout = 0;
p->Cedar = cedar;
AddRef(cedar->ref);
2014-01-04 17:00:08 +04:00
p->AdjustMss = adjust_mss;
2014-01-04 17:00:08 +04:00
StrCpy(p->CryptName, sizeof(p->CryptName), crypt_name);
2014-01-04 17:00:08 +04:00
Copy(&p->ClientIP, client_ip, sizeof(IP));
p->ClientPort = client_port;
2014-01-04 17:00:08 +04:00
Copy(&p->ServerIP, server_ip, sizeof(IP));
p->ServerPort = server_port;
2014-01-04 17:00:08 +04:00
p->TubeRecv = recv_tube;
p->TubeSend = send_tube;
2014-01-04 17:00:08 +04:00
AddRef(p->TubeRecv->Ref);
AddRef(p->TubeSend->Ref);
2014-01-04 17:00:08 +04:00
StrCpy(p->Postfix, sizeof(p->Postfix), postfix);
StrCpy(p->ClientSoftwareName, sizeof(p->ClientSoftwareName), client_software_name);
if (IsEmptyStr(client_hostname))
2014-01-04 17:00:08 +04:00
{
IPToStr(p->ClientHostname, sizeof(p->ClientHostname), client_ip);
}
else
{
StrCpy(p->ClientHostname, sizeof(p->ClientHostname), client_hostname);
2014-01-04 17:00:08 +04:00
}
p->FlushList = NewTubeFlushList();
// Thread creation
t = NewThread(PPPThread, p);
2014-01-04 17:00:08 +04:00
p->SessionThread = t;
return p;
2014-01-04 17:00:08 +04:00
}
// PPP processing functions
// Finds out if a packet is supported, if not - sends a notification to the peer
// result: false - supported, true - unsupported
bool PPPRejectUnsupportedPacket(PPP_SESSION *p, PPP_PACKET *pp)
2014-01-04 17:00:08 +04:00
{
return PPPRejectUnsupportedPacketEx(p, pp, false);
}
bool PPPRejectUnsupportedPacketEx(PPP_SESSION *p, PPP_PACKET *pp, bool force)
{
bool result = false;
if (p == NULL || pp == NULL)
2014-01-04 17:00:08 +04:00
{
return false;
2014-01-04 17:00:08 +04:00
}
if (PPP_IS_SUPPORTED_PROTOCOL(pp->Protocol) == false || force == true)
{
// Unsupported algorithm
PPP_PACKET *pp2 = ZeroMalloc(sizeof(PPP_PACKET));
BUF *buf;
UCHAR c;
USHORT us;
2014-01-04 17:00:08 +04:00
Debug("Rejecting PPP protocol = 0x%x\n", pp->Protocol);
result = true;
2014-01-04 17:00:08 +04:00
pp2->Protocol = PPP_PROTOCOL_LCP;
pp2->IsControl = false;
2014-01-04 17:00:08 +04:00
buf = NewBuf();
2014-01-04 17:00:08 +04:00
// Code
c = PPP_LCP_CODE_PROTOCOL_REJECT;
WriteBuf(buf, &c, 1);
2014-01-04 17:00:08 +04:00
// ID
c = p->NextId++;
WriteBuf(buf, &c, 1);
2014-01-04 17:00:08 +04:00
// Length
us = Endian16(pp->DataSize + 6);
WriteBuf(buf, &us, 2);
// Rejected Protocol
us = Endian16(pp->Protocol);
WriteBuf(buf, &us, 2);
// Packet Data
WriteBuf(buf, pp->Data, pp->DataSize);
pp2->Data = Clone(buf->Buf, buf->Size);
pp2->DataSize = buf->Size;
FreeBuf(buf);
if (!PPPSendPacketAndFree(p, pp2))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
}
}
return result;
2014-01-04 17:00:08 +04:00
}
// Do the retransmissions if needed
bool PPPProcessRetransmissions(PPP_SESSION *p)
2014-01-04 17:00:08 +04:00
{
INT64 i = 0;
UINT64 now = Tick64();
UINT64 count;
if (p->SentReqPacketList == NULL)
2014-01-04 17:00:08 +04:00
{
Debug("Somehow SentReqPacketList is NULL!\n");
2014-01-04 17:00:08 +04:00
return false;
}
// Making it signed but expanding to 64 bits
count = LIST_NUM(p->SentReqPacketList);
if (count == 0)
2014-01-04 17:00:08 +04:00
{
return true;
2014-01-04 17:00:08 +04:00
}
for (i = count - 1; i >= 0; --i)
2014-01-04 17:00:08 +04:00
{
PPP_REQUEST_RESEND *t = LIST_DATA(p->SentReqPacketList, i);
if (t->TimeoutTime <= now)
2014-01-04 17:00:08 +04:00
{
Debug("Timing out on resending control packet protocol = 0x%x\n", t->Packet->Protocol);
Delete(p->SentReqPacketList, t);
FreePPPPacket(t->Packet);
Free(t);
}
else if (t->ResendTime <= now)
{
Debug("Resending control packet protocol = 0x%x\n", t->Packet->Protocol);
if (!PPPSendPacketEx(p, t->Packet, false))
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
2014-01-04 17:00:08 +04:00
}
t->ResendTime = now + PPP_PACKET_RESEND_INTERVAL;
2014-01-04 17:00:08 +04:00
}
}
return true;
}
2014-01-04 17:00:08 +04:00
// Send the PPP Echo Request
bool PPPSendEchoRequest(PPP_SESSION *p)
{
UINT64 now = Tick64();
if (p->NextEchoSendTime == 0 || now >= p->NextEchoSendTime)
{
PPP_PACKET *pp;
char echo_data[] = "\0\0\0\0Aho Baka Manuke";
2014-01-04 17:00:08 +04:00
p->NextEchoSendTime = now + (UINT64)PPP_ECHO_SEND_INTERVAL;
if (IsIPCConnected(p->Ipc))
{
AddInterrupt(p->Ipc->Interrupt, p->NextEchoSendTime);
}
2014-01-04 17:00:08 +04:00
// Validate arguments
if (p == NULL)
{
return false;
}
pp = ZeroMalloc(sizeof(PPP_PACKET));
pp->Protocol = PPP_PROTOCOL_LCP;
pp->IsControl = true;
pp->Lcp = NewPPPLCP(PPP_LCP_CODE_ECHO_REQUEST, 0);
pp->Lcp->Data = Clone(echo_data, sizeof(echo_data));
pp->Lcp->DataSize = sizeof(echo_data);
if (!PPPSendPacketAndFree(p, pp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return true;
}
2014-01-04 17:00:08 +04:00
return false;
}
// Processes response packets
bool PPPProcessResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req)
2014-01-04 17:00:08 +04:00
{
if (req == NULL)
2014-01-04 17:00:08 +04:00
{
Debug("We received a response for... What? We never sent this request, protocol = 0x%x, code = 0x%x\n", pp->Protocol, pp->Lcp->Code);
// Let's just discard this, as this was probably already parsed, and we just stumbled upon a resend
2014-01-04 17:00:08 +04:00
return false;
}
switch (pp->Protocol)
2014-01-04 17:00:08 +04:00
{
case PPP_PROTOCOL_LCP:
return PPPProcessLCPResponsePacket(p, pp, req);
break;
case PPP_PROTOCOL_PAP:
Debug("Got a response PAP, which is invalid, we should get a request instead\n");
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
break;
case PPP_PROTOCOL_CHAP:
return PPPProcessCHAPResponsePacket(p, pp, req);
break;
case PPP_PROTOCOL_IPCP:
return PPPProcessIPCPResponsePacket(p, pp, req);
break;
case PPP_PROTOCOL_IPV6CP:
Debug("IPv6CP to be implemented\n");
break;
case PPP_PROTOCOL_EAP:
return PPPProcessEAPResponsePacket(p, pp, req);
break;
default:
Debug("We received a response for an unsupported protocol??? Should be filtered out already! protocol = 0x%x, code = 0x%x\n", pp->Protocol, pp->Lcp->Code);
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
2014-01-04 17:00:08 +04:00
}
return false;
2014-01-04 17:00:08 +04:00
}
bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req)
2014-01-04 17:00:08 +04:00
{
UINT i;
bool isAccepted = !PPP_LCP_CODE_IS_NEGATIVE(pp->Lcp->Code);
bool result = true;
// MSCHAPv2 code
UCHAR ms_chap_v2_code[3];
WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP);
ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2;
// We got one of rejects here, not NACKs
if (!isAccepted && pp->Lcp->Code == PPP_LCP_CODE_PROTOCOL_REJECT)
{
// If we receive a protocol reject before we finished authenticating
// probably means the PPP client is not compatible anyway so we fail the connection
if (p->PPPStatus != PPP_STATUS_NETWORK_LAYER)
{
USHORT* protocol = pp->Lcp->Data;
Debug("Protocol 0x%x rejected before auth, probably unsupported client, failing connection\n", *protocol);
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
else
{
USHORT* protocol = pp->Lcp->Data;
if (*protocol == PPP_PROTOCOL_IPCP || *protocol == PPP_PROTOCOL_IP)
{
2020-04-27 22:18:57 +03:00
p->IPv4_State = PPP_PROTO_STATUS_REJECTED;
}
if (*protocol == PPP_PROTOCOL_IPV6CP || *protocol == PPP_PROTOCOL_IPV6)
{
2020-04-27 22:18:57 +03:00
p->IPv6_State = PPP_PROTO_STATUS_REJECTED;
}
}
}
if (!isAccepted && pp->Lcp->Code == PPP_LCP_CODE_CODE_REJECT)
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
2014-01-04 17:00:08 +04:00
return false;
}
for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++)
2014-01-04 17:00:08 +04:00
{
PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i);
PPP_OPTION *opt = NULL;
2014-01-04 17:00:08 +04:00
switch (t->Type)
2014-01-04 17:00:08 +04:00
{
case PPP_LCP_OPTION_MRU:
// MRU
if (t->DataSize == sizeof(USHORT))
{
USHORT value = READ_USHORT(t->Data);
if (!isAccepted)
{
if (pp->Lcp->Code != PPP_LCP_CODE_NAK)
{
Debug("MRU setup failed, rejected");
p->Mru1 = p->Mru2 = PPP_MRU_DEFAULT;
}
if (value < PPP_MRU_MIN || value > PPP_MRU_MAX)
{
Debug("Couldn't agree on an MRU! Breaking link... MRU = 0x%x\n", value);
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
else
{
PPP_LCP* lcp = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
Add(lcp->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &value, sizeof(USHORT)));
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
Debug("PPP: Server got %u as MRU from NACK, re-requesting\n", p->Mru2);
}
}
else if (value < PPP_MRU_MIN || value > PPP_MRU_MAX)
{
Debug("The client somehow ACKed an invalid MRU, breaking link... MRU = 0x%x\n", value);
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
result = false;
}
else
{
p->Mru2 = value;
Debug("PPP: Server set %u as MRU\n", p->Mru2);
}
}
break;
case PPP_LCP_OPTION_AUTH:
opt = PPPGetOptionValue(req->Lcp, PPP_LCP_OPTION_AUTH);
if (opt == NULL)
{
Debug("We got some weird response with option absent in request, wut? Disconnecting\n");
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
if (opt->DataSize == sizeof(USHORT) && *((USHORT*)(opt->Data)) == Endian16(PPP_LCP_AUTH_EAP))
{
// Try to request MS-CHAPv2 then
if (!isAccepted)
{
UINT64 offer = 0;
PPP_LCP* c = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
UCHAR ms_chap_v2_code[3];
WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP);
ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2;
Copy(&offer, ms_chap_v2_code, sizeof(ms_chap_v2_code));
Debug("NACK proto with code = 0x%x, cypher = 0x%x, offered cypher = 0x%x\n", pp->Lcp->Code, *((USHORT*)(opt->Data)), offer);
Debug("Request MSCHAPv2\n");
Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &ms_chap_v2_code, sizeof(ms_chap_v2_code)));
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
}
else
{
p->AuthProtocol = PPP_PROTOCOL_EAP;
Debug("Setting BEFORE_AUTH from ACK on LCP response parse on EAP accept\n");
PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH);
}
}
else if (opt->DataSize == sizeof(ms_chap_v2_code) && Cmp(opt->Data, ms_chap_v2_code, opt->DataSize) == 0)
{
// Try to request PAP then
if (!isAccepted || !p->EnableMSCHAPv2)
{
UINT64 offer = 0;
PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
USHORT proto = Endian16(PPP_LCP_AUTH_PAP);
Copy(&offer, t->Data, t->DataSize > sizeof(UINT64) ? sizeof(UINT64) : t->DataSize);
Debug("NACK proto with code = 0x%x, cypher = 0x%x, offered cypher = 0x%x\n", pp->Lcp->Code, *((USHORT*)(opt->Data)), offer);
Debug("Request PAP\n");
Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &proto, sizeof(USHORT)));
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
}
else if (p->AuthProtocol == PPP_UNSPECIFIED)
{
p->AuthProtocol = PPP_PROTOCOL_CHAP;
Debug("Setting BEFORE_AUTH from ACK on LCP response parse on CHAP accept\n");
PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH);
}
}
else if (opt->DataSize == sizeof(USHORT) && *((USHORT*)(opt->Data)) == Endian16(PPP_LCP_AUTH_PAP))
{
// We couldn't agree on auth proto, failing connection
if (!isAccepted)
{
UINT64 offer = 0;
Copy(&offer, t->Data, t->DataSize > sizeof(UINT64) ? sizeof(UINT64) : t->DataSize);
Debug("NACK proto with code = 0x%x, cypher = 0x%x, offered cypher = 0x%x\n", pp->Lcp->Code, *((USHORT*)(opt->Data)), offer);
Debug("Couldn't agree on auth protocol!\n");
PPPLog(p, "LP_PAP_MSCHAPV2_REJECTED");
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
else if (p->AuthProtocol == PPP_UNSPECIFIED)
{
p->AuthProtocol = PPP_PROTOCOL_PAP;
Debug("Setting BEFORE_AUTH from ACK on LCP response parse on PAP accept\n");
PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH);
}
}
break;
2014-01-04 17:00:08 +04:00
}
}
return result;
}
// Process CHAP responses
bool PPPProcessCHAPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req)
{
PPP_LCP* lcp;
if (pp->Lcp->Code == PPP_CHAP_CODE_RESPONSE)
2014-01-04 17:00:08 +04:00
{
bool ok = false;
if (p->PPPStatus != PPP_STATUS_AUTHENTICATING && !p->AuthOk)
2014-01-04 17:00:08 +04:00
{
Debug("Receiving CHAP response packets outside of auth status, some errors probably!");
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
if (p->AuthProtocol != PPP_PROTOCOL_CHAP)
{
Debug("Receiving CHAP packet when auth protocol set to 0x%x\n", p->AuthProtocol);
PPPLog(p, "LP_NEXT_PROTOCOL_IS_NOT_PAP", pp->Protocol);
PPPRejectUnsupportedPacketEx(p, pp, true);
return false;
}
2014-01-04 17:00:08 +04:00
ok = PPPParseMSCHAP2ResponsePacket(p, pp);
// If we got only first packet of double CHAP then send second challenge
if (ok && p->MsChapV2_UseDoubleMsChapV2 && p->EapClient != NULL && p->Ipc == NULL)
{
lcp = BuildMSCHAP2ChallengePacket(p);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_CHAP, lcp))
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
2014-01-04 17:00:08 +04:00
}
}
// We got a successful MSCHAPv2 response, so let's send a SUCCESS
else if (ok)
{
char hex[MAX_SIZE];
char ret_str[MAX_SIZE];
BUF* lcp_ret_data = NewBuf();
PPP_PACKET* res = ZeroMalloc(sizeof(PPP_PACKET));
BinToStr(hex, sizeof(hex), p->MsChapV2_ServerResponse, 20);
2014-01-04 17:00:08 +04:00
Format(ret_str, sizeof(ret_str),
"S=%s", hex);
2014-01-04 17:00:08 +04:00
WriteBuf(lcp_ret_data, ret_str, StrLen(ret_str));
lcp = NewPPPLCP(PPP_CHAP_CODE_SUCCESS, p->MsChapV2_PacketId);
lcp->Data = Clone(lcp_ret_data->Buf, lcp_ret_data->Size);
lcp->DataSize = lcp_ret_data->Size;
if (lcp_ret_data != NULL)
{
FreeBuf(lcp_ret_data);
}
res->Lcp = lcp;
res->IsControl = true;
res->Protocol = PPP_PROTOCOL_CHAP;
if (!PPPSendPacketAndFree(p, res))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
p->AuthOk = true;
PPPSetStatus(p, PPP_STATUS_AUTH_SUCCESS);
2014-01-04 17:00:08 +04:00
}
// We failed MSCHAPv2 auth
2014-01-04 17:00:08 +04:00
else
{
char hex[MAX_SIZE];
char ret_str[MAX_SIZE];
BUF* lcp_ret_data = NewBuf();
PPP_PACKET* res = ZeroMalloc(sizeof(PPP_PACKET));
2014-01-04 17:00:08 +04:00
BinToStr(hex, sizeof(hex), p->MsChapV2_ServerChallenge, 16);
2014-01-04 17:00:08 +04:00
Format(ret_str, sizeof(ret_str),
"E=%u R=0 C=%s V=3", p->MsChapV2_ErrorCode, hex);
WriteBuf(lcp_ret_data, ret_str, StrLen(ret_str));
lcp = NewPPPLCP(PPP_CHAP_CODE_FAILURE, p->MsChapV2_PacketId);
lcp->Data = Clone(lcp_ret_data->Buf, lcp_ret_data->Size);
lcp->DataSize = lcp_ret_data->Size;
if (lcp_ret_data != NULL)
2014-01-04 17:00:08 +04:00
{
FreeBuf(lcp_ret_data);
}
2014-01-04 17:00:08 +04:00
res->Lcp = lcp;
res->IsControl = true;
res->Protocol = PPP_PROTOCOL_CHAP;
if (!PPPSendPacketAndFree(p, res))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
2014-01-04 17:00:08 +04:00
}
PPPLog(p, "LP_CHAP_FAILED");
PPPSetStatus(p, PPP_STATUS_AUTH_FAIL);
2014-01-04 17:00:08 +04:00
}
return ok;
2014-01-04 17:00:08 +04:00
}
return false;
}
2014-01-04 17:00:08 +04:00
// Process IPCP responses
bool PPPProcessIPCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req)
{
bool isAccepted = !PPP_LCP_CODE_IS_NEGATIVE(pp->Lcp->Code);
IP addrStruct;
char addrStr[MAX_SIZE];
UINT addr;
IP prevAddrStruct;
char prevAddrStr[MAX_SIZE];
UINT prevAddr;
PPP_LCP* c;
UINT ui;
if (!PPPGetIPAddressValueFromLCP(pp->Lcp, PPP_IPCP_OPTION_IP, &addrStruct) || pp->Lcp->Code == PPP_LCP_CODE_REJECT || pp->Lcp->Code == PPP_LCP_CODE_CODE_REJECT)
2014-01-04 17:00:08 +04:00
{
Debug("Unsupported IPCP protocol");
p->IPv4_State = PPP_PROTO_STATUS_REJECTED;
PPPRejectUnsupportedPacketEx(p, pp, true);
return false;
}
// We're dealing either with ACK or NACK
addr = IPToUINT(&addrStruct);
IPToStr(addrStr, MAX_SIZE, &addrStruct);
if (isAccepted)
{
Debug("Accepted server IP address of %s\n", addrStr);
2014-01-04 17:00:08 +04:00
// We already configured client address, now server address is also confirmed, ready for IPv4 data flow
if (p->IPv4_State == PPP_PROTO_STATUS_CONFIG)
{
p->IPv4_State = PPP_PROTO_STATUS_CONFIG_WAIT;
2014-01-04 17:00:08 +04:00
}
return true;
2014-01-04 17:00:08 +04:00
}
2020-04-27 22:18:57 +03:00
p->IPv4_State = PPP_PROTO_STATUS_CONFIG;
2014-01-04 17:00:08 +04:00
PPPGetIPAddressValueFromLCP(req->Lcp, PPP_IPCP_OPTION_IP, &prevAddrStruct);
prevAddr = IPToUINT(&prevAddrStruct);
IPToStr(prevAddrStr, MAX_SIZE, &prevAddrStruct);
2014-01-04 17:00:08 +04:00
Debug("Denied server IP address %s, proposed %s\n", prevAddrStr, addrStr);
2020-02-04 23:51:50 +03:00
// Fallback mechanism - just request 192.0.0.8
if (prevAddr == Endian32(0xc0000008))
2014-01-04 17:00:08 +04:00
{
2020-02-04 23:51:50 +03:00
Debug("We already tried the fallback IP of 192.0.0.8, giving up\n");
p->IPv4_State = PPP_PROTO_STATUS_REJECTED;
PPPRejectUnsupportedPacketEx(p, pp, true);
return false;
2014-01-04 17:00:08 +04:00
}
c = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
2020-02-04 23:51:50 +03:00
ui = Endian32(0xc0000008); // We always push 192.0.0.8, which is defined in RFC7600 as dummy IPv4 address.
Add(c->OptionList, NewPPPOption(PPP_IPCP_OPTION_IP, &ui, sizeof(UINT)));
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_IPCP, c))
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return false;
}
2014-01-04 17:00:08 +04:00
// Process EAP responses
bool PPPProcessEAPResponsePacket(PPP_SESSION* p, PPP_PACKET* pp, PPP_PACKET* req)
{
if (pp->Lcp->DataSize >= 1)
{
PPP_EAP* eap_packet = pp->Lcp->Data;
UINT eap_datasize = pp->Lcp->DataSize - 1;
UINT64 offer = 0;
PPP_LCP* c;
UCHAR ms_chap_v2_code[3];
WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP);
ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2;
switch (eap_packet->Type)
{
case PPP_EAP_TYPE_IDENTITY:
Copy(p->Eap_Identity, eap_packet->Data, MIN(MAX_SIZE, eap_datasize));
// As we received the identity packet, we switch back to BEFORE_AUTH and switch to the EAP_TLS proto to send the TlsStart packet on the next tick
p->Eap_Protocol = PPP_EAP_TYPE_TLS;
PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH);
break;
case PPP_EAP_TYPE_NOTIFICATION:
// Basically this is just an acknoweldgment that the notification was accepted by the client. Nothing to do here...
break;
case PPP_EAP_TYPE_NAK:
/// TODO: implement alternative EAP protocol selection based on received NAK
// For now just fallback to auth protocol selection to try to select MSCHAP or PAP
Debug("Got a EAP_NAK, abandoning EAP protocol\n");
PPPRejectUnsupportedPacketEx(p, pp, true);
PPPSetStatus(p, PPP_STATUS_CONNECTED);
c = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
Copy(&offer, ms_chap_v2_code, sizeof(ms_chap_v2_code));
Debug("Request MSCHAPv2 from EAP NAK\n");
Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &ms_chap_v2_code, sizeof(ms_chap_v2_code)));
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
break;
case PPP_EAP_TYPE_TLS:
PPPProcessEAPTlsResponse(p, eap_packet, eap_datasize);
break;
default:
Debug("We got an unexpected EAP response packet! Ignoring...\n");
break;
}
}
else
{
// Means we got a Success or Failure packet...
Debug("We got a CODE %i from client with zero size EAP structure, that shouldn't be happening!\n", pp->Lcp->Code);
}
return false;
}
// Processes request packets
bool PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *pp)
{
switch (pp->Protocol)
{
case PPP_PROTOCOL_LCP:
return PPPProcessLCPRequestPacket(p, pp);
break;
case PPP_PROTOCOL_PAP:
return PPPProcessPAPRequestPacket(p, pp);
break;
case PPP_PROTOCOL_CHAP:
Debug("Got a CHAP request, which is invalid, we should get CHAP response instead\n");
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
break;
case PPP_PROTOCOL_IPCP:
return PPPProcessIPCPRequestPacket(p, pp);
break;
case PPP_PROTOCOL_IPV6CP:
PPPRejectUnsupportedPacketEx(p, pp, true);
Debug("IPv6CP to be implemented\n");
break;
case PPP_PROTOCOL_EAP:
return PPPProcessEAPRequestPacket(p, pp);
break;
default:
Debug("Unsupported protocols should be already filtered out! protocol = 0x%x, code = 0x%x\n", pp->Protocol, pp->Lcp->Code);
return false;
break;
2014-01-04 17:00:08 +04:00
}
return false;
}
2014-01-04 17:00:08 +04:00
bool PPPProcessLCPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp)
{
bool result = true;
UINT i = 0;
USHORT NegotiatedAuthProto = PPP_UNSPECIFIED;
USHORT NegotiatedMRU = PPP_UNSPECIFIED;
// MSCHAPv2 code
UCHAR ms_chap_v2_code[3];
USHORT eap_code = PPP_LCP_AUTH_EAP;
2020-04-25 05:01:09 +03:00
WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP);
ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2;
2020-04-25 05:01:09 +03:00
Debug("Got LCP packet request ID=%i OptionsListSize=%i\n", pp->Lcp->Id, LIST_NUM(pp->Lcp->OptionList));
for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++)
2014-01-04 17:00:08 +04:00
{
PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i);
2014-01-04 17:00:08 +04:00
switch (t->Type)
{
case PPP_LCP_OPTION_AUTH:
t->IsSupported = true;
2020-05-02 10:11:01 +03:00
if (t->DataSize == sizeof(USHORT) && *((USHORT*)t->Data) == PPP_LCP_AUTH_EAP && p->AuthProtocol == PPP_UNSPECIFIED)
{
t->IsAccepted = true;
NegotiatedAuthProto = PPP_PROTOCOL_EAP;
}
2020-05-02 10:11:01 +03:00
else if (t->DataSize == sizeof(USHORT) && *((USHORT*)t->Data) == PPP_LCP_AUTH_PAP && p->AuthProtocol == PPP_UNSPECIFIED)
{
t->IsAccepted = true;
NegotiatedAuthProto = PPP_PROTOCOL_PAP;
}
else if (t->DataSize == sizeof(ms_chap_v2_code) && Cmp(t->Data, ms_chap_v2_code, t->DataSize) == 0 && p->AuthProtocol == PPP_UNSPECIFIED)
{
t->IsAccepted = true;
NegotiatedAuthProto = PPP_PROTOCOL_CHAP;
}
else
{
// We're recommending EAP by default as a more secure algo
t->IsAccepted = false;
t->AltDataSize = sizeof(eap_code);
Copy(t->AltData, &eap_code, sizeof(eap_code));
}
break;
case PPP_LCP_OPTION_MRU:
t->IsSupported = true;
if (t->DataSize == sizeof(USHORT))
2014-01-04 17:00:08 +04:00
{
USHORT value = READ_USHORT(t->Data);
if (value < PPP_MRU_MIN || value > PPP_MRU_MAX)
2014-01-04 17:00:08 +04:00
{
t->IsAccepted = false;
value = MAKESURE(value, PPP_MRU_MIN, PPP_MRU_MAX);
//Debug("MRU not accepted, sending NACK with value = 0x%x\n", value);
t->AltDataSize = sizeof(USHORT);
WRITE_USHORT(t->AltData, value);
}
else
{
t->IsAccepted = true;
NegotiatedMRU = value;
//Debug("MRU accepted, value = 0x%x\n", value);
2014-01-04 17:00:08 +04:00
}
}
else
{
t->IsAccepted = false;
t->AltDataSize = sizeof(USHORT);
WRITE_USHORT(t->AltData, PPP_MRU_DEFAULT);
}
break;
default:
t->IsSupported = false;
Debug("Unsupported LCP option = 0x%x\n", t->Type);
break;
2014-01-04 17:00:08 +04:00
}
}
if (PPPRejectLCPOptions(p, pp))
2014-01-04 17:00:08 +04:00
{
Debug("Rejected LCP options...\n");
return false;
}
2014-01-04 17:00:08 +04:00
if (PPPNackLCPOptions(p, pp))
{
Debug("NACKed LCP options...\n");
return false;
}
2014-01-04 17:00:08 +04:00
if (!PPPAckLCPOptions(p, pp))
{
return false;
}
if (NegotiatedAuthProto != PPP_UNSPECIFIED)
{
if (p->AuthProtocol == PPP_UNSPECIFIED)
2014-01-04 17:00:08 +04:00
{
p->AuthProtocol = NegotiatedAuthProto;
PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH);
Debug("Setting BEFORE_AUTH from REQ on LCP request parse\n");
}
}
if (NegotiatedMRU != PPP_UNSPECIFIED)
{
p->Mru1 = NegotiatedMRU;
}
2014-01-04 17:00:08 +04:00
return true;
}
2014-01-04 17:00:08 +04:00
bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp)
{
if (p->PPPStatus != PPP_STATUS_BEFORE_AUTH && !p->AuthOk)
{
PPP_LCP* lcp = NewPPPLCP(PPP_PAP_CODE_NAK, pp->Lcp->Id);
PPP_PACKET* ret = ZeroMalloc(sizeof(PPP_PACKET));
Debug("Got a PAP request before we're ready for AUTH procedure!\n");
ret->IsControl = true;
ret->Protocol = PPP_PROTOCOL_PAP;
ret->Lcp = lcp;
if (!PPPSendPacketAndFree(p, ret))
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
2014-01-04 17:00:08 +04:00
}
return false;
}
if (p->AuthProtocol != PPP_PROTOCOL_PAP)
{
Debug("Trying to auth with PAP when should be 0x%x\n", p->AuthProtocol);
PPPLog(p, "LP_NEXT_PROTOCOL_IS_NOT_CHAP", pp->Protocol);
2014-01-04 17:00:08 +04:00
// Forcing rejection of PAP on configured MSCHAPv2
PPPRejectUnsupportedPacketEx(p, pp, true);
2014-01-04 17:00:08 +04:00
return false;
2014-01-04 17:00:08 +04:00
}
if (!p->AuthOk)
2014-01-04 17:00:08 +04:00
{
UCHAR *data;
UINT size;
PPPSetStatus(p, PPP_STATUS_AUTHENTICATING);
2014-01-04 17:00:08 +04:00
if (p->Ipc == NULL)
{
// PAP
// Extract the ID and the password
data = pp->Lcp->Data;
size = pp->Lcp->DataSize;
2014-01-04 17:00:08 +04:00
if (size >= 1)
{
UCHAR len_id = data[0];
data++;
size--;
if (size >= len_id)
{
char username[256];
char password[256];
Zero(username, sizeof(username));
Zero(password, sizeof(password));
Copy(username, data, len_id);
data += len_id;
size -= len_id;
if (size >= 1)
{
UCHAR len_pass = data[0];
data++;
size--;
if (size >= len_pass)
{
IPC *ipc;
char id[MAX_SIZE];
char hub[MAX_SIZE];
ETHERIP_ID d;
Zero(id, sizeof(id));
Zero(hub, sizeof(hub));
Copy(password, data, len_pass);
Debug("PPP: id=%s, pw=%s\n", username, password);
// The user name is divided into the ID and the virtual HUB name
Zero(&d, sizeof(d));
PPPParseUsername(p->Cedar, username, &d);
StrCpy(id, sizeof(id), d.UserName);
StrCpy(hub, sizeof(hub), d.HubName);
if (IsEmptyStr(id) == false)
{
// Attempt to connect with IPC
UINT error_code;
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password,
&error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort,
p->ClientHostname, p->CryptName, false, p->AdjustMss, NULL, NULL,
IPC_LAYER_3);
2014-01-04 17:00:08 +04:00
if (ipc != NULL)
{
p->Ipc = ipc;
// Setting user timeouts
p->PacketRecvTimeout = (UINT64)p->Ipc->Policy->TimeOut * 1000 * 3 / 4; // setting to 3/4 of the user timeout
p->DataTimeout = (UINT64)p->Ipc->Policy->TimeOut * 1000;
p->UserConnectionTimeout = (UINT64)p->Ipc->Policy->AutoDisconnect * 1000;
p->UserConnectionTick = Tick64();
p->AuthOk = true;
}
else
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
2014-01-04 17:00:08 +04:00
}
}
}
}
}
}
}
else
{
// Return success for a request from the second time when it is successfully authenticated once
p->AuthOk = true;
}
}
if (p->AuthOk)
{
PPP_LCP* lcp = NewPPPLCP(PPP_PAP_CODE_ACK, pp->Lcp->Id);
PPP_PACKET* ret = ZeroMalloc(sizeof(PPP_PACKET));
ret->IsControl = true;
ret->Protocol = PPP_PROTOCOL_PAP;
ret->Lcp = lcp;
if (!PPPSendPacketAndFree(p, ret))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
2014-01-04 17:00:08 +04:00
}
if (p->PPPStatus == PPP_STATUS_AUTHENTICATING)
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_AUTH_SUCCESS);
2014-01-04 17:00:08 +04:00
}
return true;
}
if (!p->AuthOk)
{
PPP_LCP* lcp = NewPPPLCP(PPP_PAP_CODE_NAK, pp->Lcp->Id);
PPP_PACKET* ret = ZeroMalloc(sizeof(PPP_PACKET));
ret->IsControl = true;
ret->Protocol = PPP_PROTOCOL_PAP;
ret->Lcp = lcp;
if (!PPPSendPacketAndFree(p, ret))
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
2014-01-04 17:00:08 +04:00
if (p->PPPStatus == PPP_STATUS_AUTHENTICATING)
{
PPPSetStatus(p, PPP_STATUS_AUTH_FAIL);
PPPLog(p, "LP_PAP_FAILED");
2014-01-04 17:00:08 +04:00
}
return false;
2014-01-04 17:00:08 +04:00
}
return p->AuthOk;
}
bool PPPProcessIPCPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp)
{
PPP_IPOPTION o;
PPP_IPOPTION res;
PPP_OPTION *dummyIpOption;
UINT dummyIp = 0;
DHCP_OPTION_LIST cao;
IP client_ip;
IP subnet;
IP zero;
IP gw;
bool ok = true;
bool processed = false;
bool isEmptyIpAddress = false;
PPP_LCP* c;
if (p->IPv4_State == PPP_PROTO_STATUS_REJECTED)
2014-01-04 17:00:08 +04:00
{
Debug("We got an IPCP packet after we had it rejected\n");
return PPPRejectUnsupportedPacketEx(p, pp, true);
}
2014-01-04 17:00:08 +04:00
if (!PPPGetIPOptionFromLCP(&o, pp->Lcp))
{
Debug("IPCP request without client IP address received! Treating as zeroed out client IP...\n");
isEmptyIpAddress = true;
dummyIpOption = NewPPPOption(PPP_IPCP_OPTION_IP, &dummyIp, sizeof(UINT));
dummyIpOption->IsSupported = true;
dummyIpOption->IsAccepted = false;
Add(pp->Lcp->OptionList, dummyIpOption);
}
// Process if not configured yet by server
if ((IsZero(&p->ClientAddressOption, sizeof(DHCP_OPTION_LIST)) || isEmptyIpAddress) && ok)
{
// Decide if we received a static IP from client and it is allowed
if (IsZeroIP(&o.IpAddress) == false)
{
if (p->Ipc->Policy->DHCPForce == false)
2014-01-04 17:00:08 +04:00
{
if (p->DhcpAllocated == false)
2014-01-04 17:00:08 +04:00
{
if (p->UseStaticIPAddress == false)
2014-01-04 17:00:08 +04:00
{
DHCP_OPTION_LIST cao;
2014-01-04 17:00:08 +04:00
// The client specify an IP address
Zero(&cao, sizeof(cao));
2014-01-04 17:00:08 +04:00
cao.ClientAddress = IPToUINT(&o.IpAddress);
2014-01-04 17:00:08 +04:00
Copy(&p->ClientAddressOption, &cao, sizeof(cao));
2014-01-04 17:00:08 +04:00
p->UseStaticIPAddress = true;
2014-01-04 17:00:08 +04:00
}
}
}
}
else
{
p->UseStaticIPAddress = false;
}
2014-01-04 17:00:08 +04:00
// Get additional information for static clients
if (p->UseStaticIPAddress)
{
if (p->DhcpIpInformTried == false)
2014-01-04 17:00:08 +04:00
{
// Get additional information such as the subnet mask from the DHCP server
SetIP(&subnet, 255, 0, 0, 0);
Zero(&zero, sizeof(zero));
2014-01-04 17:00:08 +04:00
UINTToIP(&client_ip, p->ClientAddressOption.ClientAddress);
2014-01-04 17:00:08 +04:00
Zero(&cao, sizeof(cao));
2014-01-04 17:00:08 +04:00
IPCSetIPv4Parameters(p->Ipc, &client_ip, &subnet, &zero, NULL);
2014-01-04 17:00:08 +04:00
p->DhcpIpInformTried = true;
2014-01-04 17:00:08 +04:00
PPPLog(p, "LP_DHCP_INFORM_TRYING");
2014-01-04 17:00:08 +04:00
if (IPCDhcpRequestInformIP(p->Ipc, &cao, p->TubeRecv, &client_ip))
{
Debug("IPCDhcpRequestInformIP ok.\n");
Copy(&p->ClientAddressOption, &cao, sizeof(cao));
p->ClientAddressOption.ClientAddress = IPToUINT(&client_ip);
2014-01-04 17:00:08 +04:00
if (true)
2014-01-04 17:00:08 +04:00
{
char server_ip_str[64];
char subnet_str[64], defgw_str[64];
char dns1_str[64], dns2_str[64];
char wins1_str[64], wins2_str[64];
IPToStr32(server_ip_str, sizeof(server_ip_str), cao.ServerAddress);
IPToStr32(subnet_str, sizeof(subnet_str), cao.SubnetMask);
IPToStr32(defgw_str, sizeof(defgw_str), cao.Gateway);
IPToStr32(dns1_str, sizeof(dns1_str), cao.DnsServer);
IPToStr32(dns2_str, sizeof(dns2_str), cao.DnsServer2);
IPToStr32(wins1_str, sizeof(wins1_str), cao.WinsServer);
IPToStr32(wins2_str, sizeof(wins2_str), cao.WinsServer2);
PPPLog(p, "LP_DHCP_INFORM_OK",
subnet_str, defgw_str, cao.DomainName,
dns1_str, dns2_str, wins1_str, wins2_str,
server_ip_str);
2014-01-04 17:00:08 +04:00
}
}
else
{
Debug("IPCDhcpRequestInformIP failed.\n");
ok = false;
p->DhcpIpInformTried = false;
PPPLog(p, "LP_DHCP_INFORM_NG");
}
IPCSetIPv4Parameters(p->Ipc, &zero, &zero, &zero, NULL);
2014-01-04 17:00:08 +04:00
}
}
// Get IP address and additional information from DHCP
else
{
if (p->DhcpIpAllocTried == false)
2014-01-04 17:00:08 +04:00
{
DHCP_OPTION_LIST cao;
2014-01-04 17:00:08 +04:00
Zero(&cao, sizeof(cao));
p->DhcpIpAllocTried = true;
2014-01-04 17:00:08 +04:00
PPPLog(p, "LP_DHCP_REQUEST_TRYING");
2014-01-04 17:00:08 +04:00
if (IPCDhcpAllocateIP(p->Ipc, &cao, p->TubeRecv))
{
UINT t;
2014-01-04 17:00:08 +04:00
Debug("IPCDhcpAllocateIP ok.\n");
2014-01-04 17:00:08 +04:00
// IP address has been determined
Copy(&p->ClientAddressOption, &cao, sizeof(cao));
2014-01-04 17:00:08 +04:00
p->DhcpAllocated = true;
2014-01-04 17:00:08 +04:00
// Determine the DHCP update interval
t = cao.LeaseTime;
if (t == 0)
{
t = 600;
}
2014-01-04 17:00:08 +04:00
t = t / 3;
2014-01-04 17:00:08 +04:00
if (t == 0)
{
t = 1;
}
2014-01-04 17:00:08 +04:00
2019-11-22 01:26:36 +03:00
p->DhcpRenewInterval = (UINT64)t * (UINT64)1000;
2014-01-04 17:00:08 +04:00
p->DhcpNextRenewTime = Tick64() + p->DhcpRenewInterval;
if (true)
2014-01-04 17:00:08 +04:00
{
char client_ip_str[64], server_ip_str[64];
char subnet_str[64], defgw_str[64];
char dns1_str[64], dns2_str[64];
char wins1_str[64], wins2_str[64];
IPToStr32(client_ip_str, sizeof(client_ip_str), cao.ClientAddress);
IPToStr32(server_ip_str, sizeof(server_ip_str), cao.ServerAddress);
IPToStr32(subnet_str, sizeof(subnet_str), cao.SubnetMask);
IPToStr32(defgw_str, sizeof(defgw_str), cao.Gateway);
IPToStr32(dns1_str, sizeof(dns1_str), cao.DnsServer);
IPToStr32(dns2_str, sizeof(dns2_str), cao.DnsServer2);
IPToStr32(wins1_str, sizeof(wins1_str), cao.WinsServer);
IPToStr32(wins2_str, sizeof(wins2_str), cao.WinsServer2);
PPPLog(p, "LP_DHCP_REQUEST_OK",
client_ip_str, subnet_str, defgw_str, cao.DomainName,
dns1_str, dns2_str, wins1_str, wins2_str,
server_ip_str, cao.LeaseTime);
2014-01-04 17:00:08 +04:00
}
}
else
2014-01-04 17:00:08 +04:00
{
Debug("IPCDhcpAllocateIP failed.\n");
p->DhcpIpAllocTried = false;
ok = false;
PPPLog(p, "LP_DHCP_REQUEST_NG");
2014-01-04 17:00:08 +04:00
}
}
}
}
// If we already have a configured IP data - send it along
if (IsValidUnicastIPAddressUINT4(p->ClientAddressOption.ClientAddress) &&
p->ClientAddressOption.SubnetMask != 0 && ok)
2014-01-04 17:00:08 +04:00
{
// Success to determine the address
UINTToIP(&subnet, p->ClientAddressOption.SubnetMask);
UINTToIP(&gw, p->ClientAddressOption.Gateway);
2014-01-04 17:00:08 +04:00
Zero(&res, sizeof(res));
UINTToIP(&res.IpAddress, p->ClientAddressOption.ClientAddress);
UINTToIP(&res.DnsServer1, p->ClientAddressOption.DnsServer);
UINTToIP(&res.DnsServer2, p->ClientAddressOption.DnsServer2);
UINTToIP(&res.WinsServer1, p->ClientAddressOption.WinsServer);
UINTToIP(&res.WinsServer2, p->ClientAddressOption.WinsServer2);
if (IPCSetIPv4Parameters(p->Ipc, &res.IpAddress, &subnet, &gw, &p->ClientAddressOption.ClasslessRoute))
2014-01-04 17:00:08 +04:00
{
char client_ip_str[64];
char subnet_str[64], defgw_str[64];
char dns1_str[64], dns2_str[64];
char wins1_str[64], wins2_str[64];
// IPv4 parameters have been set for the first time
Debug("Param First Set.\n");
IPToStr(client_ip_str, sizeof(client_ip_str), &res.IpAddress);
IPToStr(subnet_str, sizeof(subnet_str), &subnet);
IPToStr(defgw_str, sizeof(defgw_str), &gw);
IPToStr(dns1_str, sizeof(dns1_str), &res.DnsServer1);
IPToStr(dns2_str, sizeof(dns2_str), &res.DnsServer2);
IPToStr(wins1_str, sizeof(wins1_str), &res.WinsServer1);
IPToStr(wins2_str, sizeof(wins2_str), &res.WinsServer2);
PPPLog(p, "LP_SET_IPV4_PARAM", client_ip_str, subnet_str,
defgw_str, dns1_str, dns2_str, wins1_str, wins2_str);
2014-01-04 17:00:08 +04:00
}
/*// Backporting static configuration received from client - let him use whatever he wants,
// he won't accept anything else anyway (as per testing with Windows clients)
if (!IsZeroIP(&o.DnsServer1))
2014-01-04 17:00:08 +04:00
{
CopyIP(&res.DnsServer1, &o.DnsServer1);
Debug("Setting DNS1 from client\n");
2014-01-04 17:00:08 +04:00
}
if (!IsZeroIP(&o.DnsServer2))
2014-01-04 17:00:08 +04:00
{
CopyIP(&res.DnsServer2, &o.DnsServer2);
Debug("Setting DNS2 from client\n");
2014-01-04 17:00:08 +04:00
}
if (!IsZeroIP(&o.WinsServer1))
2014-01-04 17:00:08 +04:00
{
CopyIP(&res.WinsServer1, &o.WinsServer1);
Debug("Setting WINS1 from client\n");
2014-01-04 17:00:08 +04:00
}
if (!IsZeroIP(&o.WinsServer2))
2014-01-04 17:00:08 +04:00
{
CopyIP(&res.WinsServer2, &o.WinsServer2);
Debug("Setting WINS2 from client\n");
}*/
/*if (!IsZeroIP(&res.DnsServer1) && IsZeroIP(&res.DnsServer2))
{
CopyIP(&res.DnsServer2, &res.DnsServer1);
2014-01-04 17:00:08 +04:00
}
if (!IsZeroIP(&res.WinsServer1) && IsZeroIP(&res.WinsServer2))
{
CopyIP(&res.WinsServer2, &res.WinsServer1);
}*/
PPPSetIPOptionToLCP(&res, pp->Lcp, true);
2014-01-04 17:00:08 +04:00
}
// We couldn't configure address for the client
2014-01-04 17:00:08 +04:00
else
{
// Failed to determine the address
Debug("IP Address Determination Failed.\n");
2014-01-04 17:00:08 +04:00
Zero(&res, sizeof(res));
// We will try to reconfigure if we receive another request by wiping all data
Zero(&p->ClientAddressOption, sizeof(DHCP_OPTION_LIST));
p->UseStaticIPAddress = false;
2014-01-04 17:00:08 +04:00
PPPSetIPOptionToLCP(&res, pp->Lcp, true);
}
if (PPPRejectLCPOptionsEx(p, pp, processed))
2014-01-04 17:00:08 +04:00
{
Debug("Rejected IPCP options ID = 0x%x\n", pp->Lcp->Id);
processed = true;
2014-01-04 17:00:08 +04:00
}
if (ok && PPPNackLCPOptionsEx(p, pp, processed))
{
Debug("NACKed IPCP options ID = 0x%x\n", pp->Lcp->Id);
processed = true;
}
2014-01-04 17:00:08 +04:00
// We will delay this packet ACK and send the server IP first, then wait for a reparse
// it is kind of dirty but fixes issues on some clients (namely VPN Client Pro on Android)
if (p->IPv4_State == PPP_PROTO_STATUS_CLOSED && p->ClientAddressOption.ServerAddress != 0 && ok)
2014-01-04 17:00:08 +04:00
{
PPP_LCP* c = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
UINT ui = p->ClientAddressOption.ServerAddress;
Add(c->OptionList, NewPPPOption(PPP_IPCP_OPTION_IP, &ui, sizeof(UINT)));
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_IPCP, c))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
p->IPv4_State = PPP_PROTO_STATUS_CONFIG;
if (!processed)
{
PPPAddNextPacket(p, pp, 1);
}
2014-01-04 17:00:08 +04:00
return false;
}
// We still haven't received any answer from client about server IP, keep waiting...
if ((p->IPv4_State == PPP_PROTO_STATUS_CONFIG || p->IPv4_State == PPP_PROTO_STATUS_CLOSED) && !processed)
2014-01-04 17:00:08 +04:00
{
PPPAddNextPacket(p, pp, 1);
2014-01-04 17:00:08 +04:00
return false;
}
//Debug("PPPAckLCPOptionsEx ok=%x, processed=%x", ok, processed);
if (!ok || !PPPAckLCPOptionsEx(p, pp, processed))
2014-01-04 17:00:08 +04:00
{
return false;
}
Debug("ACKed IPCP options ID = 0x%x\n", pp->Lcp->Id);
2014-01-04 17:00:08 +04:00
if (ok && p->IPv4_State == PPP_PROTO_STATUS_CONFIG_WAIT)
2014-01-04 17:00:08 +04:00
{
p->IPv4_State = PPP_PROTO_STATUS_OPENED;
Debug("IPv4 OPENED\n");
2014-01-04 17:00:08 +04:00
}
return ok;
}
2014-01-04 17:00:08 +04:00
// Process EAP request packets
bool PPPProcessEAPRequestPacket(PPP_SESSION* p, PPP_PACKET* pp)
{
Debug("We got an EAP request, which is weird...\n");
return false;
}
// LCP option based packets utility
bool PPPRejectLCPOptions(PPP_SESSION *p, PPP_PACKET* pp)
{
return PPPRejectLCPOptionsEx(p, pp, false);
}
bool PPPRejectLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate)
{
UINT i = 0;
bool toBeRejected = false;
PPP_PACKET* ret;
for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++)
{
PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i);
2014-01-04 17:00:08 +04:00
if (t->IsSupported == false)
2014-01-04 17:00:08 +04:00
{
toBeRejected = true;
break;
2014-01-04 17:00:08 +04:00
}
}
if (toBeRejected == false)
2014-01-04 17:00:08 +04:00
{
return false;
}
ret = ZeroMalloc(sizeof(PPP_PACKET));
ret->IsControl = true;
ret->Protocol = pp->Protocol;
// Return a Reject if there are unsupported parameters
ret->Lcp = NewPPPLCP(PPP_LCP_CODE_REJECT, pp->Lcp->Id);
for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++)
2014-01-04 17:00:08 +04:00
{
PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i);
if (t->IsSupported == false)
{
// Attach the original option value as is
Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->Data, t->DataSize));
Debug("Rejected LCP option = 0x%x, proto = 0x%x\n", t->Type, pp->Protocol);
}
2014-01-04 17:00:08 +04:00
}
if (LIST_NUM(ret->Lcp->OptionList) == 0 || simulate)
2014-01-04 17:00:08 +04:00
{
FreePPPPacket(ret);
2014-01-04 17:00:08 +04:00
return false;
}
PPPSendPacketAndFree(p, ret);
2014-01-04 17:00:08 +04:00
return true;
}
bool PPPNackLCPOptions(PPP_SESSION *p, PPP_PACKET* pp)
2014-01-04 17:00:08 +04:00
{
return PPPNackLCPOptionsEx(p, pp, false);
}
bool PPPNackLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate)
{
UINT i = 0;
PPP_PACKET* ret;
bool toBeNACKed = false;
for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++)
2014-01-04 17:00:08 +04:00
{
PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i);
2014-01-04 17:00:08 +04:00
if (t->IsAccepted == false && t->IsSupported == true)
{
toBeNACKed = true;
break;
}
2014-01-04 17:00:08 +04:00
}
if (toBeNACKed == false)
2014-01-04 17:00:08 +04:00
{
return false;
2014-01-04 17:00:08 +04:00
}
ret = ZeroMalloc(sizeof(PPP_PACKET));
ret->IsControl = true;
ret->Protocol = pp->Protocol;
// Return a NAK if there are any unacceptable parameter
// even that all parameters are supported
ret->Lcp = NewPPPLCP(PPP_LCP_CODE_NAK, pp->Lcp->Id);
2014-01-04 17:00:08 +04:00
for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++)
2014-01-04 17:00:08 +04:00
{
PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i);
2014-01-04 17:00:08 +04:00
if (t->IsAccepted == false && t->IsSupported == true)
2014-01-04 17:00:08 +04:00
{
// Replace the original option value with an acceptable value
Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->AltData, t->AltDataSize));
Debug("NACKed LCP option = 0x%x, proto = 0x%x\n", t->Type, pp->Protocol);
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
if (LIST_NUM(ret->Lcp->OptionList) == 0 || simulate)
{
FreePPPPacket(ret);
return false;
}
2014-01-04 17:00:08 +04:00
PPPSendPacketAndFree(p, ret);
return true;
}
bool PPPAckLCPOptions(PPP_SESSION *p, PPP_PACKET* pp)
{
return PPPAckLCPOptionsEx(p, pp, false);
}
bool PPPAckLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate)
{
UINT i = 0;
PPP_PACKET* ret;
bool toBeACKed = false;
2020-04-25 05:01:09 +03:00
if (LIST_NUM(pp->Lcp->OptionList) == 0)
{
// We acknoweldge an empty option list
toBeACKed = true;
Debug("ACKing empty LCP options list, id=%i\n", pp->Lcp->Id);
}
for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++)
{
PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i);
2014-01-04 17:00:08 +04:00
if (t->IsAccepted == true && t->IsSupported == true)
2014-01-04 17:00:08 +04:00
{
toBeACKed = true;
break;
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
if (toBeACKed == false)
{
return false;
}
2014-01-04 17:00:08 +04:00
ret = ZeroMalloc(sizeof(PPP_PACKET));
ret->IsControl = true;
ret->Protocol = pp->Protocol;
// Return an ACK if all parameters are accepted
ret->Lcp = NewPPPLCP(PPP_LCP_CODE_ACK, pp->Lcp->Id);
2014-01-04 17:00:08 +04:00
for (i = 0; i < LIST_NUM(pp->Lcp->OptionList); i++)
{
PPP_OPTION *t = LIST_DATA(pp->Lcp->OptionList, i);
2014-01-04 17:00:08 +04:00
if (t->IsAccepted == true && t->IsSupported == true)
2014-01-04 17:00:08 +04:00
{
// Attach the original option value as is
Add(ret->Lcp->OptionList, NewPPPOption(t->Type, t->Data, t->DataSize));
Debug("ACKed LCP option = 0x%x, proto = 0x%x\n", t->Type, pp->Protocol);
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
2020-04-25 05:01:09 +03:00
if (simulate)
{
FreePPPPacket(ret);
return false;
2014-01-04 17:00:08 +04:00
}
PPPSendPacketAndFree(p, ret);
return true;
2014-01-04 17:00:08 +04:00
}
// PPP networking functions
// Send a request packet in the PPP
bool PPPSendAndRetransmitRequest(PPP_SESSION *p, USHORT protocol, PPP_LCP *c)
2014-01-04 17:00:08 +04:00
{
PPP_PACKET *pp;
UINT64 now = Tick64();
PPP_REQUEST_RESEND* resend;
2014-01-04 17:00:08 +04:00
// Validate arguments
if (p == NULL || c == NULL)
2014-01-04 17:00:08 +04:00
{
return false;
}
pp = ZeroMalloc(sizeof(PPP_PACKET));
pp->Protocol = protocol;
pp->IsControl = true;
pp->Lcp = c;
if (pp->Lcp->Id == 0)
{
pp->Lcp->Id = p->NextId++;
2014-01-04 17:00:08 +04:00
}
// Send the PPP packet
if (!PPPSendPacketEx(p, pp, false))
2014-01-04 17:00:08 +04:00
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
2014-01-04 17:00:08 +04:00
}
resend = ZeroMalloc(sizeof(PPP_REQUEST_RESEND));
resend->Id = pp->Lcp->Id;
resend->Packet = pp;
resend->ResendTime = now + PPP_PACKET_RESEND_INTERVAL;
resend->TimeoutTime = now + p->PacketRecvTimeout;
2014-01-04 17:00:08 +04:00
Add(p->SentReqPacketList, resend);
return true;
}
// Send the PPP packet and frees the sent packet
bool PPPSendPacketAndFree(PPP_SESSION *p, PPP_PACKET *pp)
{
bool result = PPPSendPacketEx(p, pp, false);
FreePPPPacket(pp);
return result;
}
// Send the PPP packet
bool PPPSendPacketEx(PPP_SESSION *p, PPP_PACKET *pp, bool no_flush)
2014-01-04 17:00:08 +04:00
{
bool ret = false;
BUF *b;
2014-01-04 17:00:08 +04:00
// Validate arguments
if (p == NULL || pp == NULL)
2014-01-04 17:00:08 +04:00
{
return false;
}
b = BuildPPPPacketData(pp);
if (b == NULL)
{
return false;
2014-01-04 17:00:08 +04:00
}
ret = TubeSendEx(p->TubeSend, b->Buf, b->Size, NULL, no_flush);
if (no_flush)
2014-01-04 17:00:08 +04:00
{
AddTubeToFlushList(p->FlushList, p->TubeSend);
2014-01-04 17:00:08 +04:00
}
FreeBuf(b);
return ret;
2014-01-04 17:00:08 +04:00
}
// Receive a PPP packet
PPP_PACKET *PPPRecvPacket(PPP_SESSION *p, bool async)
2014-01-04 17:00:08 +04:00
{
TUBEDATA *d;
PPP_PACKET *pp;
2014-01-04 17:00:08 +04:00
// Validate arguments
if (p == NULL)
{
return NULL;
}
LABEL_LOOP:
if (async == false)
2014-01-04 17:00:08 +04:00
{
d = TubeRecvSync(p->TubeRecv, p->PacketRecvTimeout);
2014-01-04 17:00:08 +04:00
}
else
2014-01-04 17:00:08 +04:00
{
d = TubeRecvAsync(p->TubeRecv);
}
2014-01-04 17:00:08 +04:00
if (d == NULL)
{
return NULL;
}
2014-01-04 17:00:08 +04:00
pp = ParsePPPPacket(d->Data, d->DataSize);
FreeTubeData(d);
2014-01-04 17:00:08 +04:00
if (pp == NULL)
{
// A broken packet is received
2014-01-04 17:00:08 +04:00
goto LABEL_LOOP;
}
p->LastRecvTime = Tick64();
2014-01-04 17:00:08 +04:00
return pp;
}
2014-01-04 17:00:08 +04:00
PPP_PACKET *PPPGetNextPacket(PPP_SESSION *p)
{
PPP_PACKET* ret = NULL;
UINT i = 0;
if (p->CurrentPacket != NULL)
{
FreePPPPacket(p->CurrentPacket);
p->CurrentPacket = NULL;
}
for (i = 0; i < LIST_NUM(p->DelayedPackets); i++)
{
PPP_DELAYED_PACKET* t = LIST_DATA(p->DelayedPackets, i);
if (t->DelayTicks > 0)
2014-01-04 17:00:08 +04:00
{
t->DelayTicks--;
2014-01-04 17:00:08 +04:00
}
else
2014-01-04 17:00:08 +04:00
{
ret = t->Packet;
Delete(p->DelayedPackets, t);
Free(t);
break;
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
if (ret != NULL)
{
p->CurrentPacket = ret;
return ret;
}
2014-01-04 17:00:08 +04:00
ret = PPPRecvPacket(p, true);
if (ret != NULL && ret->IsControl && ret->Lcp != NULL)
{
PPP_DELAYED_PACKET* firstRelated = NULL;
for (i = 0; i < LIST_NUM(p->DelayedPackets); i++)
{
PPP_DELAYED_PACKET* t = LIST_DATA(p->DelayedPackets, i);
char related = PPPRelatedPacketComparator(ret, t->Packet);
if (related != 0xF && related != 0xE)
2014-01-04 17:00:08 +04:00
{
if (related == 0)
{
// It's the same packet, just remove it and wait for it's delays
FreePPPPacket(ret);
firstRelated = NULL;
ret = NULL;
break;
}
if (related == 1)
{
// We got a packet which should come later than any of delayed ones
PPPAddNextPacket(p, ret, t->DelayTicks);
firstRelated = NULL;
ret = NULL;
break;
}
if (related == -1)
{
char prevFoundRelated = -1;
if (firstRelated != NULL)
{
prevFoundRelated = PPPRelatedPacketComparator(t->Packet, firstRelated->Packet);
}
if (prevFoundRelated == -1)
{
firstRelated = t;
}
}
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
if (firstRelated != NULL)
{
PPPAddNextPacket(p, ret, firstRelated->DelayTicks);
ret = NULL;
2014-01-04 17:00:08 +04:00
}
}
p->CurrentPacket = ret;
return ret;
2014-01-04 17:00:08 +04:00
}
void PPPAddNextPacket(PPP_SESSION *p, PPP_PACKET *pp, UINT delay)
2014-01-04 17:00:08 +04:00
{
PPP_DELAYED_PACKET* t = ZeroMalloc(sizeof(PPP_DELAYED_PACKET));
UINT i;
if (p->CurrentPacket == pp)
2014-01-04 17:00:08 +04:00
{
p->CurrentPacket = NULL;
2014-01-04 17:00:08 +04:00
}
t->Packet = pp;
t->DelayTicks = delay;
Add(p->DelayedPackets, t);
Sort(p->DelayedPackets);
/*Debug("after sorting delayeds\n");
for (i = 0; i < LIST_NUM(p->DelayedPackets); i++)
2014-01-04 17:00:08 +04:00
{
t = LIST_DATA(p->DelayedPackets, i);
if (t->Packet->Lcp != NULL)
{
Debug("> Packet proto = 0x%x, id = 0x%x, code = 0x%x, delay = 0x%x\n", t->Packet->Protocol, t->Packet->Lcp->Id, t->Packet->Lcp->Code, t->DelayTicks);
}
2014-01-04 17:00:08 +04:00
}
Debug("after sorting delayeds end\n");*/
}
2014-01-04 17:00:08 +04:00
int PPPDelayedPacketsComparator(const void* a, const void* b)
{
PPP_DELAYED_PACKET* first = a;
PPP_DELAYED_PACKET* second = b;
2014-01-04 17:00:08 +04:00
char related = PPPRelatedPacketComparator(first->Packet, second->Packet);
2014-01-04 17:00:08 +04:00
if (related == 0xF || related == 0xE)
2014-01-04 17:00:08 +04:00
{
if (first->DelayTicks < second->DelayTicks)
{
return -1;
}
if (first->DelayTicks > second->DelayTicks)
{
return 1;
}
return 0;
2014-01-04 17:00:08 +04:00
}
// We make all delay ticks to be accounted with the sorting
if (related <= 1 && related >= -1)
{
if (related == -1 && first->DelayTicks >= second->DelayTicks)
{
second->DelayTicks = first->DelayTicks;
second->DelayTicks++;
}
else if (related == 1 && first->DelayTicks <= second->DelayTicks)
{
first->DelayTicks = second->DelayTicks;
first->DelayTicks++;
}
return related;
}
return 0;
2014-01-04 17:00:08 +04:00
}
// -1 - packet a comes before packet b
// 0 - this is the same packet
// 1 - packet a comes after packet b
// 0xF - packet is not related
// 0xE - we got an error while comparing, treating as not related would be the most correct
char PPPRelatedPacketComparator(PPP_PACKET* a, PPP_PACKET* b)
2014-01-04 17:00:08 +04:00
{
if (a->IsControl && b->IsControl &&
a->Lcp != NULL && b->Lcp != NULL &&
a->Protocol == b->Protocol &&
PPP_CODE_IS_REQUEST(a->Protocol, a->Lcp->Code) == PPP_CODE_IS_REQUEST(b->Protocol, b->Lcp->Code) &&
PPP_CODE_IS_RESPONSE(a->Protocol, a->Lcp->Code) == PPP_CODE_IS_RESPONSE(b->Protocol, b->Lcp->Code))
{
// The packet is related!
if (a->Lcp->Id < b->Lcp->Id)
{
return -1;
}
else if (a->Lcp->Id == b->Lcp->Id)
{
if (a->Lcp->Code == b->Lcp->Code)
{
return 0;
}
else
{
return 0xE;
}
}
else if (a->Lcp->Id > b->Lcp->Id)
{
return 1;
}
else
{
return 0xE;
}
2014-01-04 17:00:08 +04:00
}
else
2014-01-04 17:00:08 +04:00
{
// The packet is not related!
return 0xF;
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
// PPP utility functions
// Packet structure creation utilities
2014-01-04 17:00:08 +04:00
// Create the LCP
PPP_LCP *NewPPPLCP(UCHAR code, UCHAR id)
{
PPP_LCP *c = ZeroMalloc(sizeof(PPP_LCP));
2014-01-04 17:00:08 +04:00
c->Code = code;
c->Id = id;
c->OptionList = NewListFast(NULL);
2014-01-04 17:00:08 +04:00
return c;
2014-01-04 17:00:08 +04:00
}
// Create a new PPP options
PPP_OPTION *NewPPPOption(UCHAR type, void *data, UINT size)
{
PPP_OPTION *o;
// Validate arguments
if (size != 0 && data == NULL)
{
return NULL;
}
o = ZeroMalloc(sizeof(PPP_OPTION));
o->Type = type;
Copy(o->Data, data, size);
o->DataSize = size;
return o;
}
// Packet parse utilities
2014-01-04 17:00:08 +04:00
// Analyse the PPP packet
PPP_PACKET *ParsePPPPacket(void *data, UINT size)
{
PPP_PACKET *pp;
UCHAR *buf;
// Validate arguments
if (data == NULL || size == 0)
{
return NULL;
}
pp = ZeroMalloc(sizeof(PPP_PACKET));
buf = (UCHAR *)data;
// Address
if (size < 1)
{
goto LABEL_ERROR;
}
if (buf[0] != 0xff)
{
goto LABEL_ERROR;
}
size--;
buf++;
// Control
if (size < 1)
{
goto LABEL_ERROR;
}
if (buf[0] != 0x03)
{
goto LABEL_ERROR;
}
size--;
buf++;
// Protocol
if (size < 2)
{
goto LABEL_ERROR;
}
pp->Protocol = READ_USHORT(buf);
2014-01-04 17:00:08 +04:00
size -= 2;
buf += 2;
if (pp->Protocol == PPP_PROTOCOL_LCP || pp->Protocol == PPP_PROTOCOL_PAP || pp->Protocol == PPP_PROTOCOL_CHAP || pp->Protocol == PPP_PROTOCOL_IPCP || pp->Protocol == PPP_PROTOCOL_IPV6CP || pp->Protocol == PPP_PROTOCOL_EAP)
{
pp->IsControl = true;
}
pp->Data = Clone(buf, size);
pp->DataSize = size;
if (pp->IsControl)
{
pp->Lcp = PPPParseLCP(pp->Protocol, pp->Data, pp->DataSize);
if (pp->Lcp == NULL)
{
goto LABEL_ERROR;
}
}
return pp;
LABEL_ERROR:
FreePPPPacket(pp);
return NULL;
}
// Analyse the LCP data
PPP_LCP *PPPParseLCP(USHORT protocol, void *data, UINT size)
{
UCHAR *buf;
PPP_LCP *c;
USHORT len;
bool has_option_list = false;
// Validate arguments
if (data == NULL || size == 0)
{
return NULL;
}
buf = (UCHAR *)data;
c = ZeroMalloc(sizeof(PPP_LCP));
c->OptionList = NewListFast(NULL);
// Code
if (size < 1)
{
goto LABEL_ERROR;
}
c->Code = buf[0];
buf++;
size--;
// ID
if (size < 1)
{
goto LABEL_ERROR;
}
c->Id = buf[0];
buf++;
size--;
// Length
if (size < 2)
{
goto LABEL_ERROR;
}
len = READ_USHORT(buf);
if (len < 4)
{
goto LABEL_ERROR;
}
len -= 4;
buf += 2;
size -= 2;
// Options or Data
if (size < len)
{
goto LABEL_ERROR;
}
has_option_list = PPP_CODE_IS_WITH_OPTION_LIST(protocol, c->Code);
if (has_option_list == false)
{
c->Data = Clone(buf, size);
c->DataSize = size;
}
else
{
// Option List
while (len >= 1)
{
PPP_OPTION o;
Zero(&o, sizeof(o));
// Type
if (len < 1)
{
goto LABEL_ERROR;
}
o.Type = buf[0];
buf++;
len--;
// Length
if (len < 1)
{
goto LABEL_ERROR;
}
o.DataSize = buf[0];
if (o.DataSize < 2)
{
goto LABEL_ERROR;
}
o.DataSize -= 2;
buf++;
len--;
// Data
if (len < o.DataSize)
{
goto LABEL_ERROR;
}
Copy(o.Data, buf, o.DataSize);
buf += o.DataSize;
len -= o.DataSize;
Add(c->OptionList, Clone(&o, sizeof(o)));
}
}
return c;
LABEL_ERROR:
FreePPPLCP(c);
return NULL;
}
// Analyse MS CHAP v2 Response packet
bool PPPParseMSCHAP2ResponsePacket(PPP_SESSION* p, PPP_PACKET* pp)
{
bool ok = false;
char client_ip_tmp[256];
EAP_CLIENT *eap;
UCHAR client_response_buffer[49];
UCHAR *client_challenge_16;
UCHAR *client_response_24;
char username_tmp[MAX_SIZE];
IPC *ipc = NULL;
char id[MAX_SIZE];
char hub[MAX_SIZE];
char password[MAX_SIZE];
char server_challenge_hex[MAX_SIZE];
char client_challenge_hex[MAX_SIZE];
char client_response_hex[MAX_SIZE];
char eap_client_hex[64];
ETHERIP_ID d;
UINT error_code;
UINT64 eap_client_ptr = (UINT64)p->EapClient;
if (pp->Lcp != NULL && pp->Lcp->DataSize >= 51)
{
BUF *b;
if (pp->Lcp->Id != p->MsChapV2_PacketId)
{
Debug("Got incorrect LCP PacketId! Should be 0x%x, got 0x%x\n", p->MsChapV2_PacketId, pp->Lcp->Id);
p->MsChapV2_PacketId = pp->Lcp->Id;
}
b = NewBuf();
WriteBuf(b, pp->Lcp->Data, pp->Lcp->DataSize);
SeekBuf(b, 0, 0);
if (ReadBufChar(b) == 49)
{
ReadBuf(b, client_response_buffer, 49);
Zero(username_tmp, sizeof(username_tmp));
ReadBuf(b, username_tmp, sizeof(username_tmp) - 1);
Debug("MS-CHAPv2: id=%s\n", username_tmp);
client_challenge_16 = client_response_buffer + 0;
client_response_24 = client_response_buffer + 16 + 8;
Copy(p->MsChapV2_ClientChallenge, client_challenge_16, 16);
Copy(p->MsChapV2_ClientResponse, client_response_24, 24);
Zero(id, sizeof(id));
Zero(hub, sizeof(hub));
// The user name is divided into the ID and the virtual HUB name
Zero(&d, sizeof(d));
PPPParseUsername(p->Cedar, username_tmp, &d);
StrCpy(id, sizeof(id), d.UserName);
StrCpy(hub, sizeof(hub), d.HubName);
Debug("MS-CHAPv2: username=%s, hubname=%s\n", id, hub);
IPToStr(client_ip_tmp, sizeof(client_ip_tmp), &p->ClientIP);
// Convert the MS-CHAPv2 data to a password string
BinToStr(server_challenge_hex, sizeof(server_challenge_hex),
p->MsChapV2_ServerChallenge, sizeof(p->MsChapV2_ServerChallenge));
BinToStr(client_challenge_hex, sizeof(client_challenge_hex),
p->MsChapV2_ClientChallenge, sizeof(p->MsChapV2_ClientChallenge));
BinToStr(client_response_hex, sizeof(client_response_hex),
p->MsChapV2_ClientResponse, sizeof(p->MsChapV2_ClientResponse));
BinToStr(eap_client_hex, sizeof(eap_client_hex),
&eap_client_ptr, 8);
Format(password, sizeof(password), "%s%s:%s:%s:%s:%s",
IPC_PASSWORD_MSCHAPV2_TAG,
username_tmp,
server_challenge_hex,
client_challenge_hex,
client_response_hex,
eap_client_hex);
if (p->MsChapV2_UseDoubleMsChapV2 && p->EapClient == NULL)
{
Debug("Double MSCHAPv2 creating EAP client\n");
2020-02-04 23:51:50 +03:00
eap = HubNewEapClient(p->Cedar, hub, client_ip_tmp, id, "L3:PPP");
if (eap)
{
ok = true;
p->EapClient = eap;
}
else
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
}
else if (p->Ipc == NULL)
{
Debug("MSCHAPv2 creating IPC\n");
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, hub, id, password,
&error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort,
2020-02-04 23:51:50 +03:00
p->ClientHostname, p->CryptName, false, p->AdjustMss, p->EapClient, NULL,
+ IPC_LAYER_3);
if (ipc != NULL)
{
p->Ipc = ipc;
2014-01-04 17:00:08 +04:00
// Setting user timeouts
p->PacketRecvTimeout = (UINT64)p->Ipc->Policy->TimeOut * 1000 * 3 / 4; // setting to 3/4 of the user timeout
p->DataTimeout = (UINT64)p->Ipc->Policy->TimeOut * 1000;
p->UserConnectionTimeout = (UINT64)p->Ipc->Policy->AutoDisconnect * 1000;
p->UserConnectionTick = Tick64();
Copy(p->MsChapV2_ServerResponse, ipc->MsChapV2_ServerResponse, 20);
2014-01-04 17:00:08 +04:00
ok = true;
p->AuthOk = true;
}
}
else
{
Debug("Got weird packet when we already have an active IPC! Ipc = 0x%x, AuthOk = 0x%x, Status = 0x%x\n", p->Ipc, p->AuthOk, p->PPPStatus);
ok = p->AuthOk;
}
2014-01-04 17:00:08 +04:00
}
FreeBuf(b);
}
else
{
Debug("Got invalid MSCHAPv2 packet\n");
}
2014-01-04 17:00:08 +04:00
return ok;
2014-01-04 17:00:08 +04:00
}
// Packet building utilities
2014-01-04 17:00:08 +04:00
// Build a PPP packet data
BUF *BuildPPPPacketData(PPP_PACKET *pp)
{
BUF *ret;
UCHAR c;
USHORT us;
// Validate arguments
if (pp == NULL)
{
return NULL;
}
ret = NewBuf();
// Address
c = 0xff;
WriteBuf(ret, &c, 1);
// Control
c = 0x03;
WriteBuf(ret, &c, 1);
// Protocol
us = Endian16(pp->Protocol);
WriteBuf(ret, &us, 2);
if (pp->IsControl)
{
// LCP
BUF *b = BuildLCPData(pp->Lcp);
WriteBufBuf(ret, b);
FreeBuf(b);
}
else
{
// Data
WriteBuf(ret, pp->Data, pp->DataSize);
}
SeekBuf(ret, 0, 0);
return ret;
}
// Build the LCP packet data
BUF *BuildLCPData(PPP_LCP *c)
{
BUF *b;
UCHAR zero = 0;
UINT i;
// Validate arguments
if (c == NULL)
{
return NULL;
}
b = NewBuf();
// Code
WriteBuf(b, &c->Code, 1);
// ID
WriteBuf(b, &c->Id, 1);
// Length (to be updated later)
zero = 0;
WriteBuf(b, &zero, 1);
WriteBuf(b, &zero, 1);
if (c->Data == NULL)
{
// Option List
for (i = 0; i < LIST_NUM(c->OptionList); i++)
2014-01-04 17:00:08 +04:00
{
PPP_OPTION *o = LIST_DATA(c->OptionList, i);
UCHAR sz = o->DataSize + 2;
WriteBuf(b, &o->Type, 1);
WriteBuf(b, &sz, 1);
WriteBuf(b, o->Data, o->DataSize);
}
}
else
{
// Data
WriteBuf(b, c->Data, c->DataSize);
}
SeekBuf(b, 0, 0);
// Update Length
WRITE_USHORT(((UCHAR *)b->Buf) + 2, b->Size);
return b;
}
// Build the MS CHAP v2 challenge packet
PPP_LCP *BuildMSCHAP2ChallengePacket(PPP_SESSION* p)
2014-01-04 17:00:08 +04:00
{
PPP_LCP *lcp;
BUF *b;
char machine_name[MAX_SIZE];
UINT64 now = Tick64();
2014-01-04 17:00:08 +04:00
// Generate a Server Challenge packet of MS-CHAP v2
GetMachineHostName(machine_name, sizeof(machine_name));
2014-01-04 17:00:08 +04:00
if (p->EapClient == NULL)
2014-01-04 17:00:08 +04:00
{
MsChapV2Server_GenerateChallenge(p->MsChapV2_ServerChallenge);
2014-01-04 17:00:08 +04:00
}
else
2014-01-04 17:00:08 +04:00
{
Copy(p->MsChapV2_ServerChallenge, p->EapClient->MsChapV2Challenge.Chap_ChallengeValue, 16);
2014-01-04 17:00:08 +04:00
}
p->MsChapV2_PacketId = p->NextId++;
lcp = NewPPPLCP(PPP_CHAP_CODE_CHALLENGE, p->MsChapV2_PacketId);
b = NewBuf();
WriteBufChar(b, 16);
WriteBuf(b, p->MsChapV2_ServerChallenge, sizeof(p->MsChapV2_ServerChallenge));
WriteBuf(b, machine_name, StrLen(machine_name));
lcp->Data = Clone(b->Buf, b->Size);
lcp->DataSize = b->Size;
FreeBuf(b);
Debug("Building MS-CHAP v2 Challenge\n");
return lcp;
}
// IPCP packet utilities
// Set the IP options of PPP to LCP
bool PPPSetIPOptionToLCP(PPP_IPOPTION *o, PPP_LCP *c, bool only_modify)
{
bool ret = false;
// Validate arguments
if (c == NULL || o == NULL)
2014-01-04 17:00:08 +04:00
{
return false;
2014-01-04 17:00:08 +04:00
}
ret = PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_IP, &o->IpAddress, only_modify);
PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_DNS1, &o->DnsServer1, only_modify);
PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_DNS2, &o->DnsServer2, only_modify);
PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_WINS1, &o->WinsServer1, only_modify);
PPPSetIPAddressValueToLCP(c, PPP_IPCP_OPTION_WINS2, &o->WinsServer2, only_modify);
return ret;
}
// Get the IP options of PPP from LCP
bool PPPGetIPOptionFromLCP(PPP_IPOPTION *o, PPP_LCP *c)
{
bool ret;
// Validate arguments
if (c == NULL || o == NULL)
2014-01-04 17:00:08 +04:00
{
return false;
2014-01-04 17:00:08 +04:00
}
Zero(o, sizeof(PPP_IPOPTION));
2014-01-04 17:00:08 +04:00
ret = PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_IP, &o->IpAddress);
PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_DNS1, &o->DnsServer1);
PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_DNS2, &o->DnsServer2);
PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_WINS1, &o->WinsServer1);
PPPGetIPAddressValueFromLCP(c, PPP_IPCP_OPTION_WINS2, &o->WinsServer2);
return ret;
}
// Set the IP address data to the option list of the LCP
bool PPPSetIPAddressValueToLCP(PPP_LCP *c, UINT type, IP *ip, bool only_modify)
{
IP ip2;
UINT ui;
// Validate arguments
if (c == NULL || ip == NULL)
2014-01-04 17:00:08 +04:00
{
return false;
2014-01-04 17:00:08 +04:00
}
ui = IPToUINT(ip);
2014-01-04 17:00:08 +04:00
if (PPPGetIPAddressValueFromLCP(c, type, &ip2))
{
PPP_OPTION *opt;
opt = PPPGetOptionValue(c, type);
2014-01-04 17:00:08 +04:00
if (opt != NULL)
{
if (IsZeroIP(ip) == false)
2014-01-04 17:00:08 +04:00
{
if (CmpIpAddr(&ip2, ip) == 0)
{
// No change
opt->IsAccepted = true;
opt->IsSupported = true;
}
else
{
// Changed
opt->IsAccepted = false;
opt->IsSupported = true;
opt->AltDataSize = 4;
Copy(opt->AltData, &ui, 4);
}
2014-01-04 17:00:08 +04:00
}
else
2014-01-04 17:00:08 +04:00
{
// The parameter itself is not supported
// (if the IP address is 0.0.0.0)
opt->IsSupported = false;
opt->IsAccepted = false;
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
return true;
}
else
{
if (IsZeroIP(ip) == false)
{
// Add as a new item
if (only_modify != false)
2014-01-04 17:00:08 +04:00
{
return false;
2014-01-04 17:00:08 +04:00
}
else
{
PPP_OPTION *opt2 = NewPPPOption(type, &ui, 4);
UCHAR ipstr[MAX_SIZE];
opt2->IsAccepted = true;
opt2->IsSupported = true;
Copy(opt2->AltData, opt2->Data, opt2->DataSize);
opt2->AltDataSize = opt2->DataSize;
2014-01-04 17:00:08 +04:00
Add(c->OptionList, opt2);
IPToStr(ipstr, MAX_SIZE, ip);
return true;
}
}
else
{
return false;
2014-01-04 17:00:08 +04:00
}
}
}
2014-01-04 17:00:08 +04:00
// Get the IP address data from the option list of the LCP
bool PPPGetIPAddressValueFromLCP(PPP_LCP *c, UINT type, IP *ip)
{
PPP_OPTION *opt;
UINT ui;
// Validate arguments
if (c == NULL || ip == NULL)
{
return false;
}
2014-01-04 17:00:08 +04:00
opt = PPPGetOptionValue(c, type);
if (opt == NULL)
{
return false;
}
if (opt->DataSize != 4)
{
return false;
}
opt->IsSupported = true;
ui = *((UINT *)opt->Data);
UINTToIP(ip, ui);
return true;
2014-01-04 17:00:08 +04:00
}
// EAP packet utilities
bool PPPProcessEAPTlsResponse(PPP_SESSION* p, PPP_EAP* eap_packet, UINT eapTlsSize)
{
UCHAR* dataBuffer;
UINT dataSize;
UINT tlsLength = 0;
bool isFragmented = false;
PPP_LCP* lcp;
PPP_EAP* eap;
UCHAR flags;
UINT64 sizeLeft = 0;
2020-05-02 10:11:01 +03:00
Debug("Got EAP-TLS size=%i\n", eapTlsSize);
if (eapTlsSize == 1)
{
// This is an EAP-TLS message ACK
2020-05-02 10:11:01 +03:00
if (p->Eap_TlsCtx.cachedBufferSend != NULL)
{
// We got an ACK to transmit the next fragmented message
dataSize = p->Mru1 - 8 - 1 - 1; // Calculating the maximum payload size (without TlsLength)
2020-05-02 10:11:01 +03:00
sizeLeft = GetMemSize(p->Eap_TlsCtx.cachedBufferSend);
sizeLeft -= p->Eap_TlsCtx.cachedBufferSendPntr - p->Eap_TlsCtx.cachedBufferSend;
2020-05-02 10:11:01 +03:00
flags = PPP_EAP_TLS_FLAG_FRAGMENTED; // M flag
if (dataSize > sizeLeft)
{
dataSize = sizeLeft;
2020-05-02 10:11:01 +03:00
flags = PPP_EAP_TLS_FLAG_NONE; // Clearing the M flag because it is the last packet
}
lcp = BuildEAPTlsRequest(p->Eap_PacketId++, dataSize, flags);
eap = lcp->Data;
2020-05-02 10:11:01 +03:00
Copy(eap->Tls.TlsDataWithoutLength, p->Eap_TlsCtx.cachedBufferSendPntr, dataSize);
p->Eap_TlsCtx.cachedBufferSendPntr += dataSize;
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
2020-05-02 10:11:01 +03:00
Debug("Sent EAP-TLS size=%i type=%i flag=%i\n", lcp->DataSize, eap->Type, eap->Tls.Flags);
2020-05-02 10:11:01 +03:00
if (flags == PPP_EAP_TLS_FLAG_NONE)
{
// As it is the latest message, we need to cleanup
2020-05-02 10:11:01 +03:00
Free(p->Eap_TlsCtx.cachedBufferSend);
p->Eap_TlsCtx.cachedBufferSend = NULL;
p->Eap_TlsCtx.cachedBufferSendPntr = NULL;
}
}
else
{
// It probably should be the final ACK on closed SSL pipe
SyncSslPipe(p->Eap_TlsCtx.SslPipe);
if (p->Eap_TlsCtx.clientCert.X != NULL)
{
IPC* ipc;
ETHERIP_ID d;
UINT error_code;
PPPParseUsername(p->Cedar, p->Eap_Identity, &d);
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, d.HubName, d.UserName, "",
&error_code, &p->ClientIP, p->ClientPort, &p->ServerIP, p->ServerPort,
p->ClientHostname, p->CryptName, false, p->AdjustMss, NULL, p->Eap_TlsCtx.clientCert.X,
IPC_LAYER_3);
if (ipc != NULL)
{
p->Ipc = ipc;
PPPSetStatus(p, PPP_STATUS_AUTH_SUCCESS);
// Just send an EAP-Success
lcp = NewPPPLCP(PPP_EAP_CODE_SUCCESS, p->Eap_PacketId++);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
2020-05-02 10:11:01 +03:00
Debug("Sent EAP-TLS size=%i SUCCESS\n", lcp->DataSize);
return true;
}
else
{
PPPSetStatus(p, PPP_STATUS_AUTH_FAIL);
lcp = NewPPPLCP(PPP_EAP_CODE_FAILURE, p->Eap_PacketId++);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
2020-05-02 10:11:01 +03:00
Debug("Sent EAP-TLS size=%i FAILURE\n", lcp->DataSize);
return false;
}
}
else
{
Debug("Weird ACK for no reason at all...\n");
return false;
}
}
return true;
}
dataBuffer = eap_packet->Tls.TlsDataWithoutLength;
dataSize = eapTlsSize - 1;
2020-05-02 10:11:01 +03:00
if (eap_packet->Tls.Flags & PPP_EAP_TLS_FLAG_TLS_LENGTH)
{
dataBuffer = eap_packet->Tls.TlsDataWithLength.Data;
dataSize -= 4;
2020-05-02 10:11:01 +03:00
tlsLength = Endian32(eap_packet->Tls.TlsDataWithLength.TlsLength);
}
2020-05-02 10:11:01 +03:00
if (eap_packet->Tls.Flags & PPP_EAP_TLS_FLAG_FRAGMENTED)
{
isFragmented = true;
}
if (p->PPPStatus == PPP_STATUS_AUTHENTICATING)
{
// First we initialize the SslPipe if it is not already inited
if (p->Eap_TlsCtx.SslPipe == NULL)
{
p->Eap_TlsCtx.Dh = DhNewFromBits(DH_PARAM_BITS_DEFAULT);
p->Eap_TlsCtx.SslPipe = NewSslPipeEx(true, p->Cedar->ServerX, p->Cedar->ServerK, p->Eap_TlsCtx.Dh, true, &(p->Eap_TlsCtx.clientCert));
}
// If the current frame is fragmented, or it is a possible last of a fragmented series, bufferize it
2020-05-02 10:11:01 +03:00
if (isFragmented || p->Eap_TlsCtx.cachedBufferRecv != NULL)
{
2020-05-02 10:11:01 +03:00
if (p->Eap_TlsCtx.cachedBufferRecv == NULL && tlsLength > 0)
{
2020-05-02 10:11:01 +03:00
p->Eap_TlsCtx.cachedBufferRecv = ZeroMalloc(MAX(dataSize, tlsLength));
p->Eap_TlsCtx.cachedBufferRecvPntr = p->Eap_TlsCtx.cachedBufferRecv;
}
else
{
Debug("We didn't get the tlsLength size! We're probably corrupt.\n");
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
2020-05-02 10:11:01 +03:00
sizeLeft = GetMemSize(p->Eap_TlsCtx.cachedBufferRecv);
sizeLeft -= p->Eap_TlsCtx.cachedBufferRecvPntr - p->Eap_TlsCtx.cachedBufferRecv;
2020-05-02 10:11:01 +03:00
Copy(p->Eap_TlsCtx.cachedBufferRecvPntr, dataBuffer, MIN(dataSize, sizeLeft));
2020-05-02 10:11:01 +03:00
p->Eap_TlsCtx.cachedBufferRecvPntr += MIN(dataSize, sizeLeft);
}
// If we got a cached buffer, we should feed the FIFOs via it
2020-05-02 10:11:01 +03:00
if (p->Eap_TlsCtx.cachedBufferRecv != NULL)
{
2020-05-02 10:11:01 +03:00
dataBuffer = p->Eap_TlsCtx.cachedBufferRecv;
dataSize = GetMemSize(p->Eap_TlsCtx.cachedBufferRecv);
}
// Just acknoweldge that we buffered the fragmented data
if (isFragmented)
{
PPP_LCP* lcp = BuildEAPPacketEx(PPP_EAP_CODE_REQUEST, p->Eap_PacketId++, PPP_EAP_TYPE_TLS, 0);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
2020-05-02 10:11:01 +03:00
Debug("Sent EAP-TLS size=%i\n", lcp->DataSize);
}
else
{
WriteFifo(p->Eap_TlsCtx.SslPipe->RawIn->SendFifo, dataBuffer, dataSize);
SyncSslPipe(p->Eap_TlsCtx.SslPipe);
// Delete the cached buffer after we fed it into the pipe
2020-05-02 10:11:01 +03:00
if (p->Eap_TlsCtx.cachedBufferRecv != NULL)
{
2020-05-02 10:11:01 +03:00
Free(p->Eap_TlsCtx.cachedBufferRecv);
p->Eap_TlsCtx.cachedBufferRecv = NULL;
p->Eap_TlsCtx.cachedBufferRecvPntr = NULL;
}
if (p->Eap_TlsCtx.SslPipe->IsDisconnected == false)
{
dataSize = FifoSize(p->Eap_TlsCtx.SslPipe->RawOut->RecvFifo);
// Do we need to send a fragmented packet?
if (dataSize > p->Mru1 - 8 - 1 - 1)
{
2020-05-02 10:11:01 +03:00
if (p->Eap_TlsCtx.cachedBufferSend == NULL)
{
2020-05-02 10:11:01 +03:00
p->Eap_TlsCtx.cachedBufferSend = ZeroMalloc(dataSize);
p->Eap_TlsCtx.cachedBufferSendPntr = p->Eap_TlsCtx.cachedBufferSend;
}
2020-05-02 10:11:01 +03:00
ReadFifo(p->Eap_TlsCtx.SslPipe->RawOut->RecvFifo, p->Eap_TlsCtx.cachedBufferSend, dataSize);
// Now send data from the cached buffer with set fragmentation flag and also total TLS Size
tlsLength = dataSize;
dataSize = p->Mru1 - 8 - 1 - 1 - 4; // Calculating the maximum payload size (adjusting for including TlsLength)
2020-05-02 10:11:01 +03:00
flags = PPP_EAP_TLS_FLAG_TLS_LENGTH; // L flag
flags |= PPP_EAP_TLS_FLAG_FRAGMENTED; // M flag
lcp = BuildEAPTlsRequest(p->Eap_PacketId++, dataSize, flags);
eap = lcp->Data;
2020-05-02 10:11:01 +03:00
eap->Tls.TlsDataWithLength.TlsLength = Endian32(tlsLength);
Copy(eap->Tls.TlsDataWithLength.Data, p->Eap_TlsCtx.cachedBufferSend, dataSize);
p->Eap_TlsCtx.cachedBufferSendPntr += dataSize;
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
2020-05-02 10:11:01 +03:00
Debug("Sent EAP-TLS size=%i type=%i flag=%i\n", lcp->DataSize, eap->Type, eap->Tls.Flags);
}
else
{
lcp = BuildEAPTlsRequest(p->Eap_PacketId++, dataSize, 0);
eap = lcp->Data;
ReadFifo(p->Eap_TlsCtx.SslPipe->RawOut->RecvFifo, &(eap->Tls.TlsDataWithoutLength), dataSize);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
2020-05-02 10:11:01 +03:00
Debug("Sent EAP-TLS size=%i type=%i flag=%i\n", lcp->DataSize, eap->Type, eap->Tls.Flags);
}
}
}
}
else
{
Debug("Got an EAP_TLS packet when not authenticating, ignoring...\n");
}
return false;
}
PPP_LCP *BuildEAPPacketEx(UCHAR code, UCHAR id, UCHAR type, UINT datasize)
{
PPP_EAP* eap_packet;
PPP_LCP* lcp_packet;
UINT lcpDatasize;
lcpDatasize = datasize + sizeof(UCHAR);
eap_packet = ZeroMalloc(lcpDatasize);
eap_packet->Type = type;
lcp_packet = NewPPPLCP(code, id);
lcp_packet->Data = eap_packet;
lcp_packet->DataSize = lcpDatasize;
return lcp_packet;
}
PPP_LCP *BuildEAPTlsPacketEx(UCHAR code, UCHAR id, UCHAR type, UINT datasize, UCHAR flags)
{
PPP_LCP* lcp_packet;
PPP_EAP* eap_packet;
UINT tls_datasize = datasize + sizeof(UCHAR);
2020-05-02 10:11:01 +03:00
if (flags & PPP_EAP_TLS_FLAG_TLS_LENGTH)
{
tls_datasize += sizeof(UINT32);
}
lcp_packet = BuildEAPPacketEx(code, id, type, tls_datasize);
eap_packet = lcp_packet->Data;
eap_packet->Tls.Flags = flags;
return lcp_packet;
}
PPP_LCP* BuildEAPTlsRequest(UCHAR id, UINT datasize, UCHAR flags)
{
return BuildEAPTlsPacketEx(PPP_EAP_CODE_REQUEST, id, PPP_EAP_TYPE_TLS, datasize, flags);
}
// Other packet utilities
// Get the option value
PPP_OPTION *PPPGetOptionValue(PPP_LCP *c, UCHAR type)
2014-01-04 17:00:08 +04:00
{
UINT i;
// Validate arguments
if (c == NULL)
{
return NULL;
}
for (i = 0; i < LIST_NUM(c->OptionList); i++)
{
PPP_OPTION *t = LIST_DATA(c->OptionList, i);
if (t->Type == type)
{
return t;
}
}
return NULL;
2014-01-04 17:00:08 +04:00
}
// Check whether the Virtual HUB with the specified name exist?
bool IsHubExistsWithLock(CEDAR *cedar, char *hubname)
2014-01-04 17:00:08 +04:00
{
bool ret = false;
2014-01-04 17:00:08 +04:00
// Validate arguments
if (cedar == NULL || hubname == NULL)
2014-01-04 17:00:08 +04:00
{
return false;
2014-01-04 17:00:08 +04:00
}
LockList(cedar->HubList);
{
ret = IsHub(cedar, hubname);
}
UnlockList(cedar->HubList);
2014-01-04 17:00:08 +04:00
return ret;
}
2014-01-04 17:00:08 +04:00
// Sets the PPP status without overwriting the FAIL status
void PPPSetStatus(PPP_SESSION *p, UINT status)
{
if (status == PPP_STATUS_FAIL)
2014-01-04 17:00:08 +04:00
{
Debug("SETTING PPP_STATUS_FAIL!!!\n");
}
if (!PPP_STATUS_IS_UNAVAILABLE(p->PPPStatus) || PPP_STATUS_IS_UNAVAILABLE(status))
{
p->PPPStatus = status;
2014-01-04 17:00:08 +04:00
}
}
// Memory freeing functions
2014-01-04 17:00:08 +04:00
// Release the PPP session
void FreePPPSession(PPP_SESSION *p)
{
UINT i;
2014-01-04 17:00:08 +04:00
// Validate arguments
if (p == NULL)
{
return;
}
// Release the memory
for (i = 0; i < LIST_NUM(p->RecvPacketList); i++)
{
PPP_PACKET *pp = LIST_DATA(p->RecvPacketList, i);
FreePPPPacket(pp);
}
ReleaseList(p->RecvPacketList);
for (i = 0; i < LIST_NUM(p->SentReqPacketList); i++)
{
PPP_REQUEST_RESEND *t = LIST_DATA(p->SentReqPacketList, i);
FreePPPPacket(t->Packet);
Free(t);
}
ReleaseList(p->SentReqPacketList);
for (i = 0; i < LIST_NUM(p->DelayedPackets); i++)
{
PPP_DELAYED_PACKET *t = LIST_DATA(p->DelayedPackets, i);
FreePPPPacket(t->Packet);
Free(t);
}
ReleaseList(p->DelayedPackets);
if (p->CurrentPacket != NULL)
{
FreePPPPacket(p->CurrentPacket);
}
2014-01-04 17:00:08 +04:00
if (p->TubeRecv != NULL)
{
// Record the PPP disconnect reason code for L2TP
p->TubeRecv->IntParam1 = p->DisconnectCauseCode;
p->TubeRecv->IntParam2 = p->DisconnectCauseDirection;
}
// Freeing EAP-TLS context
2020-05-02 10:11:01 +03:00
if (p->Eap_TlsCtx.cachedBufferRecv != NULL)
{
Free(p->Eap_TlsCtx.cachedBufferRecv);
}
if (p->Eap_TlsCtx.cachedBufferSend != NULL)
{
2020-05-02 10:11:01 +03:00
Free(p->Eap_TlsCtx.cachedBufferRecv);
}
if (p->Eap_TlsCtx.SslPipe != NULL)
{
FreeSslPipe(p->Eap_TlsCtx.SslPipe);
}
if (p->Eap_TlsCtx.clientCert.X != NULL)
{
FreeX(p->Eap_TlsCtx.clientCert.X);
}
if (p->Eap_TlsCtx.Dh != NULL)
{
DhFree(p->Eap_TlsCtx.Dh);
}
2014-01-04 17:00:08 +04:00
FreeTubeFlushList(p->FlushList);
TubeDisconnect(p->TubeRecv);
TubeDisconnect(p->TubeSend);
ReleaseCedar(p->Cedar);
ReleaseTube(p->TubeRecv);
ReleaseTube(p->TubeSend);
if (p->Ipc != NULL)
{
FreeIPC(p->Ipc);
}
2015-10-06 14:18:00 +03:00
PPPFreeEapClient(p);
2014-01-04 17:00:08 +04:00
Free(p);
}
// Release the LCP
void FreePPPLCP(PPP_LCP *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
FreePPPOptionList(c->OptionList);
Free(c->Data);
Free(c);
}
// Release the PPP options list
void FreePPPOptionList(LIST *o)
{
UINT i;
// Validate arguments
if (o == NULL)
{
return;
}
for (i = 0; i < LIST_NUM(o); i++)
2014-01-04 17:00:08 +04:00
{
PPP_OPTION *t = LIST_DATA(o, i);
Free(t);
}
ReleaseList(o);
}
// Release the PPP packet
void FreePPPPacket(PPP_PACKET *pp)
{
FreePPPPacketEx(pp, false);
}
void FreePPPPacketEx(PPP_PACKET *pp, bool no_free_struct)
2014-01-04 17:00:08 +04:00
{
// Validate arguments
if (pp == NULL)
2014-01-04 17:00:08 +04:00
{
return;
2014-01-04 17:00:08 +04:00
}
FreePPPLCP(pp->Lcp);
Free(pp->Data);
if (no_free_struct == false)
2014-01-04 17:00:08 +04:00
{
Free(pp);
2014-01-04 17:00:08 +04:00
}
}
// Free the associated EAP client
void PPPFreeEapClient(PPP_SESSION *p)
{
if (p == NULL)
2014-01-04 17:00:08 +04:00
{
return;
2014-01-04 17:00:08 +04:00
}
if (p->EapClient != NULL)
2014-01-04 17:00:08 +04:00
{
ReleaseEapClient(p->EapClient);
p->EapClient = NULL;
2014-01-04 17:00:08 +04:00
}
}
2014-01-04 17:00:08 +04:00
// Utility functions used not only in PPP stack
2014-01-04 17:00:08 +04:00
// Separate into the user name and the Virtual HUB name by analyzing the string
bool PPPParseUsername(CEDAR *cedar, char *src_username, ETHERIP_ID *dst)
{
UINT i, len, last_at, first_en;
char token1[MAX_SIZE]; // username
char token2[MAX_SIZE]; // hub_name
char src[MAX_SIZE];
// Validate arguments
Zero(dst, sizeof(ETHERIP_ID));
if (cedar == NULL || src == NULL || dst == NULL)
{
return false;
}
2014-01-04 17:00:08 +04:00
StrCpy(src, sizeof(src), src_username);
Trim(src);
2014-01-04 17:00:08 +04:00
// Search for the first "\\" in the string
len = StrLen(src);
2014-01-04 17:00:08 +04:00
first_en = SearchStrEx(src, "\\", 0, true);
2014-01-04 17:00:08 +04:00
if (first_en != INFINITE && first_en >= 1 && (first_en < (len - 1)))
{
StrCpy(token1, sizeof(token1), src + first_en + 1);
StrCpy(token2, sizeof(token2), src);
token2[first_en] = 0;
2014-01-04 17:00:08 +04:00
// Confirm whether the hubname exists if the virtual HUB name is
// specified like as hubname\username
if (IsHubExistsWithLock(cedar, token2) == false)
{
// If the hubname does not exist, restore to the original name
StrCpy(token1, sizeof(token1), src);
ClearStr(token2, sizeof(token2));
}
}
else
{
2020-02-04 23:51:50 +03:00
// Search for the separator character's last position in the string
len = StrLen(src);
last_at = INFINITE;
for (i = 0; i < len; i++)
{
char c = src[i];
2014-01-04 17:00:08 +04:00
2020-02-04 23:51:50 +03:00
if (c == cedar->UsernameHubSeparator)
{
last_at = i;
}
}
2014-01-04 17:00:08 +04:00
Zero(token1, sizeof(token1));
Zero(token2, sizeof(token2));
2014-01-04 17:00:08 +04:00
if (last_at == INFINITE)
{
2020-02-04 23:51:50 +03:00
// The separator character is not specifiedd
StrCpy(token1, sizeof(token1), src);
}
else
{
StrCpy(token1, sizeof(token1), src);
token1[last_at] = 0;
StrCpy(token2, sizeof(token2), src + last_at + 1);
}
// Check whether such Virtual HUB exists If the virtual HUB name is specified
if (IsEmptyStr(token2) == false)
{
if (IsHubExistsWithLock(cedar, token2) == false)
{
// Because the specified virtual HUB name doesn't exist, it's considered to be a part of the user name
StrCpy(token1, sizeof(token1), src);
ClearStr(token2, sizeof(token2));
}
}
2014-01-04 17:00:08 +04:00
}
if (IsEmptyStr(token2))
2014-01-04 17:00:08 +04:00
{
// Select the default Virtual HUB if the Virtual HUB name is not specified
StrCpy(token2, sizeof(token2), SERVER_DEFAULT_HUB_NAME);
if (cedar->Server != NULL && cedar->Server->IPsecServer != NULL)
{
Lock(cedar->Server->IPsecServer->LockSettings);
{
IPsecNormalizeServiceSetting(cedar->Server->IPsecServer);
2014-01-04 17:00:08 +04:00
StrCpy(token2, sizeof(token2), cedar->Server->IPsecServer->Services.L2TP_DefaultHub);
}
Unlock(cedar->Server->IPsecServer->LockSettings);
}
2014-01-04 17:00:08 +04:00
}
2014-01-04 17:00:08 +04:00
// Return the results
StrCpy(dst->HubName, sizeof(dst->HubName), token2);
StrCpy(dst->UserName, sizeof(dst->UserName), token1);
return true;
2014-01-04 17:00:08 +04:00
}
// Generate the NT hash of the password
void GenerateNtPasswordHash(UCHAR *dst, char *password)
{
UCHAR *tmp;
UINT tmp_size;
UINT i, len;
// Validate arguments
if (dst == NULL || password == NULL)
{
return;
}
// Generate a Unicode password
len = StrLen(password);
tmp_size = len * 2;
tmp = ZeroMalloc(tmp_size);
for (i = 0;i < len;i++)
{
tmp[i * 2] = password[i];
}
// Hashing
HashMd4(dst, tmp, tmp_size);
Free(tmp);
}
// Generate the MS-CHAPv2 server-side challenge
void MsChapV2Server_GenerateChallenge(UCHAR *dst)
{
// Validate arguments
if (dst == NULL)
{
return;
}
Rand(dst, 16);
}
// Generate the MS-CHAPv2 client-side challenge
void MsChapV2Client_GenerateChallenge(UCHAR *dst)
{
// Validate arguments
if (dst == NULL)
{
return;
}
Rand(dst, 16);
}
// Generate a 8 bytes challenge
void MsChapV2_GenerateChallenge8(UCHAR *dst, UCHAR *client_challenge, UCHAR *server_challenge, char *username)
{
BUF *b;
UCHAR hash[SHA1_SIZE];
char username2[MAX_SIZE];
char domainname2[MAX_SIZE];
// Validate arguments
if (dst == NULL || client_challenge == NULL || server_challenge == NULL)
{
return;
}
b = NewBuf();
WriteBuf(b, client_challenge, 16);
WriteBuf(b, server_challenge, 16);
ParseNtUsername(username, username2, sizeof(username2), domainname2, sizeof(domainname2), true);
if (IsEmptyStr(username2) == false)
{
WriteBuf(b, username2, StrLen(username2));
}
Sha1(hash, b->Buf, b->Size);
2014-01-04 17:00:08 +04:00
FreeBuf(b);
Copy(dst, hash, 8);
}
// Generate the MS-CHAPv2 client response
void MsChapV2Client_GenerateResponse(UCHAR *dst, UCHAR *challenge8, UCHAR *nt_password_hash)
{
UCHAR password_hash_2[21];
UCHAR key1[8], key2[8], key3[8];
// Validate arguments
if (dst == NULL || challenge8 == NULL || nt_password_hash == NULL)
{
return;
}
Zero(password_hash_2, sizeof(password_hash_2));
Copy(password_hash_2, nt_password_hash, 16);
Zero(key1, sizeof(key1));
Zero(key2, sizeof(key2));
Zero(key3, sizeof(key3));
Copy(key1, password_hash_2 + 0, 7);
Copy(key2, password_hash_2 + 7, 7);
Copy(key3, password_hash_2 + 14, 7);
DesEcbEncrypt(dst + 0, challenge8, key1);
DesEcbEncrypt(dst + 8, challenge8, key2);
DesEcbEncrypt(dst + 16, challenge8, key3);
}
// Generate a hash of the hash of the NT password
void GenerateNtPasswordHashHash(UCHAR *dst_hash, UCHAR *src_hash)
{
// Validate arguments
if (dst_hash == NULL || src_hash == NULL)
{
return;
}
HashMd4(dst_hash, src_hash, 16);
}
// Generate the MS-CHAPv2 server response
void MsChapV2Server_GenerateResponse(UCHAR *dst, UCHAR *nt_password_hash_hash, UCHAR *client_response, UCHAR *challenge8)
{
UCHAR digest[SHA1_SIZE];
BUF *b;
char *magic1 = "Magic server to client signing constant";
char *magic2 = "Pad to make it do more than one iteration";
// Validate arguments
if (dst == NULL || nt_password_hash_hash == NULL || client_response == NULL || challenge8 == NULL)
{
return;
}
b = NewBuf();
WriteBuf(b, nt_password_hash_hash, 16);
WriteBuf(b, client_response, 24);
WriteBuf(b, magic1, StrLen(magic1));
Sha1(digest, b->Buf, b->Size);
2014-01-04 17:00:08 +04:00
FreeBuf(b);
b = NewBuf();
WriteBuf(b, digest, sizeof(digest));
WriteBuf(b, challenge8, 8);
WriteBuf(b, magic2, StrLen(magic2));
Sha1(dst, b->Buf, b->Size);
2014-01-04 17:00:08 +04:00
FreeBuf(b);
}
// Verify whether the password matches one that is specified by the user in the MS-CHAPv2
bool MsChapV2VerityPassword(IPC_MSCHAP_V2_AUTHINFO *d, char *password)
{
UCHAR ntlm_hash[MD5_SIZE];
UCHAR challenge8[8];
UCHAR client_response[24];
// Validate arguments
if (d == NULL || password == NULL)
{
return false;
}
GenerateNtPasswordHash(ntlm_hash, password);
MsChapV2_GenerateChallenge8(challenge8, d->MsChapV2_ClientChallenge, d->MsChapV2_ServerChallenge, d->MsChapV2_PPPUsername);
MsChapV2Client_GenerateResponse(client_response, challenge8, ntlm_hash);
if (Cmp(client_response, d->MsChapV2_ClientResponse, 24) != 0)
{
return false;
}
return true;
}
// Estimate the password in the brute force for the request packet of MS-CHAPv2
char *MsChapV2DoBruteForce(IPC_MSCHAP_V2_AUTHINFO *d, LIST *password_list)
{
UINT i;
// Validate arguments
if (d == NULL || password_list == NULL)
{
return NULL;
}
for (i = 0;i < LIST_NUM(password_list);i++)
{
char *s = LIST_DATA(password_list, i);
char tmp[MAX_SIZE];
UINT j, max;
UINT len;
StrCpy(tmp, sizeof(tmp), s);
len = StrLen(tmp);
max = Power(2, MIN(len, 9));
for (j = 0;j < max;j++)
{
SetStrCaseAccordingToBits(tmp, j);
if (MsChapV2VerityPassword(d, tmp))
{
return CopyStr(tmp);
}
}
}
return NULL;
}