1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-09-19 18:20:40 +03:00

Merge pull request #1109 from Evengard/ppp-eap-tls

Implementation of EAP-TLS for PPP
This commit is contained in:
Ilya Shipitsin 2020-05-04 17:13:15 +05:00 committed by GitHub
commit b41c17f45a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 789 additions and 109 deletions

View File

@ -500,6 +500,8 @@ typedef struct PPP_IPOPTION PPP_IPOPTION;
typedef struct PPP_IPV6OPTION PPP_IPV6OPTION; typedef struct PPP_IPV6OPTION PPP_IPV6OPTION;
typedef struct PPP_REQUEST_RESEND PPP_REQUEST_RESEND; typedef struct PPP_REQUEST_RESEND PPP_REQUEST_RESEND;
typedef struct PPP_DELAYED_PACKET PPP_DELAYED_PACKET; typedef struct PPP_DELAYED_PACKET PPP_DELAYED_PACKET;
typedef struct PPP_EAP PPP_EAP;
typedef struct PPP_EAP_TLS_CONTEXT PPP_EAP_TLS_CONTEXT;
// ============================================================== // ==============================================================

View File

@ -1995,6 +1995,7 @@ UINT CalcL2TPMss(L2TP_SERVER *l2tp, L2TP_TUNNEL *t, L2TP_SESSION *s)
// Start the L2TP thread // Start the L2TP thread
void StartL2TPThread(L2TP_SERVER *l2tp, L2TP_TUNNEL *t, L2TP_SESSION *s) void StartL2TPThread(L2TP_SERVER *l2tp, L2TP_TUNNEL *t, L2TP_SESSION *s)
{ {
PPP_SESSION* underlyingSession;
// Validate arguments // Validate arguments
if (l2tp == NULL || t == NULL || s == NULL) if (l2tp == NULL || t == NULL || s == NULL)
{ {
@ -2023,9 +2024,11 @@ void StartL2TPThread(L2TP_SERVER *l2tp, L2TP_TUNNEL *t, L2TP_SESSION *s)
} }
// Create a PPP thread // Create a PPP thread
s->Thread = NewPPPSession(l2tp->Cedar, &t->ClientIp, t->ClientPort, &t->ServerIp, t->ServerPort, underlyingSession = NewPPPSession(l2tp->Cedar, &t->ClientIp, t->ClientPort, &t->ServerIp, t->ServerPort,
s->TubeSend, s->TubeRecv, L2TP_IPC_POSTFIX, tmp, t->HostName, l2tp->CryptName, s->TubeSend, s->TubeRecv, L2TP_IPC_POSTFIX, tmp, t->HostName, l2tp->CryptName,
CalcL2TPMss(l2tp, t, s)); CalcL2TPMss(l2tp, t, s));
s->Thread = underlyingSession->SessionThread;
s->PPPSession = underlyingSession;
} }
} }
@ -2122,8 +2125,21 @@ void L2TPProcessInterrupts(L2TP_SERVER *l2tp)
{ {
L2TP_TUNNEL *t = LIST_DATA(l2tp->TunnelList, i); L2TP_TUNNEL *t = LIST_DATA(l2tp->TunnelList, i);
LIST *delete_session_list = NULL; LIST *delete_session_list = NULL;
UINT64 l2tpTimeout = L2TP_TUNNEL_TIMEOUT;
if ((l2tp->Now >= (t->LastRecvTick + (UINT64)L2TP_TUNNEL_TIMEOUT)) && t->Timedout == false) // If we got on ANY session a higher timeout than the default L2TP tunnel timeout, increase it
for (i = 0; i < LIST_NUM(t->SessionList); i++)
{
L2TP_SESSION* s = LIST_DATA(t->SessionList, i);
if (s->PPPSession != NULL && s->PPPSession->DataTimeout > l2tpTimeout)
{
l2tpTimeout = s->PPPSession->DataTimeout;
}
}
if ((l2tp->Now >= (t->LastRecvTick + (UINT64)l2tpTimeout)) && t->Timedout == false)
{ {
// Disconnect the tunnel forcibly if data can not be received for a certain period of time // Disconnect the tunnel forcibly if data can not be received for a certain period of time
t->Timedout = true; t->Timedout = true;

View File

@ -169,6 +169,7 @@ struct L2TP_SESSION
UINT64 DisconnectTimeout; // Disconnection completion time-out UINT64 DisconnectTimeout; // Disconnection completion time-out
bool HasThread; // Whether have a thread bool HasThread; // Whether have a thread
THREAD *Thread; // Thread THREAD *Thread; // Thread
PPP_SESSION* PPPSession; // Underlying PPP session
TUBE *TubeSend; // Tube of PPP to L2TP direction TUBE *TubeSend; // Tube of PPP to L2TP direction
TUBE *TubeRecv; // Tube of L2TP to PPP direction TUBE *TubeRecv; // Tube of L2TP to PPP direction
UINT PseudowireType; // Type of L2TPv3 virtual line UINT PseudowireType; // Type of L2TPv3 virtual line

View File

@ -35,6 +35,8 @@ void PPPThread(THREAD *thread, void *param)
p->IPv4_State = PPP_PROTO_STATUS_CLOSED; p->IPv4_State = PPP_PROTO_STATUS_CLOSED;
p->IPv6_State = PPP_PROTO_STATUS_CLOSED; p->IPv6_State = PPP_PROTO_STATUS_CLOSED;
p->Eap_Protocol = PPP_UNSPECIFIED;
p->Mru1 = p->Mru2 = PPP_MRU_DEFAULT; p->Mru1 = p->Mru2 = PPP_MRU_DEFAULT;
p->RecvPacketList = NewList(NULL); p->RecvPacketList = NewList(NULL);
p->SentReqPacketList = NewList(NULL); p->SentReqPacketList = NewList(NULL);
@ -229,6 +231,45 @@ void PPPThread(THREAD *thread, void *param)
Debug("Unprocessed and unrejected packet, protocol = 0x%x\n", p->CurrentPacket->Protocol); Debug("Unprocessed and unrejected packet, protocol = 0x%x\n", p->CurrentPacket->Protocol);
} }
} }
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!";
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...
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) else if (p->PPPStatus == PPP_STATUS_BEFORE_AUTH && p->AuthProtocol == PPP_PROTOCOL_CHAP)
{ {
// We got to start CHAP when we got no LCP packets from the client on previous iteration // We got to start CHAP when we got no LCP packets from the client on previous iteration
@ -247,14 +288,12 @@ void PPPThread(THREAD *thread, void *param)
if (p->PPPStatus == PPP_STATUS_CONNECTED && !authReqSent) if (p->PPPStatus == PPP_STATUS_CONNECTED && !authReqSent)
{ {
// MSCHAPv2 code // EAP code
PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
UCHAR ms_chap_v2_code[3]; USHORT eap_code = Endian16(PPP_LCP_AUTH_EAP);
WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP);
ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2;
Debug("Request MSCHAPv2\n"); Debug("Request EAP\n");
Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, ms_chap_v2_code, sizeof(ms_chap_v2_code))); Add(c->OptionList, NewPPPOption(PPP_LCP_OPTION_AUTH, &eap_code, sizeof(eap_code)));
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c)) if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_LCP, c))
{ {
PPPSetStatus(p, PPP_STATUS_FAIL); PPPSetStatus(p, PPP_STATUS_FAIL);
@ -265,7 +304,7 @@ void PPPThread(THREAD *thread, void *param)
if (p->PPPStatus == PPP_STATUS_AUTHENTICATING) if (p->PPPStatus == PPP_STATUS_AUTHENTICATING)
{ {
Debug("Tick waiting for auth...\n"); //Debug("Tick waiting for auth...\n");
} }
if (p->PPPStatus == PPP_STATUS_AUTH_FAIL) if (p->PPPStatus == PPP_STATUS_AUTH_FAIL)
@ -368,7 +407,7 @@ void PPPThread(THREAD *thread, void *param)
} }
else else
{ {
WaitForTubes(tubes, 1, 100); WaitForTubes(tubes, 1, 300); // Increasing timeout to make the ticks a bit slower
} }
if (IsTubeConnected(p->TubeRecv) == false || IsTubeConnected(p->TubeSend) == false) if (IsTubeConnected(p->TubeRecv) == false || IsTubeConnected(p->TubeSend) == false)
@ -386,13 +425,22 @@ void PPPThread(THREAD *thread, void *param)
} }
// Time-out inspection // Time-out inspection
if ((p->LastRecvTime + (UINT64)PPP_DATA_TIMEOUT) <= now) if ((p->LastRecvTime + (UINT64)p->DataTimeout) <= now)
{ {
// Communication time-out occurs // Communication time-out occurs
PPPLog(p, "LP_DATA_TIMEOUT"); PPPLog(p, "LP_DATA_TIMEOUT");
break; 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 // Terminate if the PPP disconnected
if (p->IsTerminateReceived) if (p->IsTerminateReceived)
{ {
@ -444,7 +492,7 @@ void PPPThread(THREAD *thread, void *param)
// Entry point // Entry point
// Create a new PPP session // Create a new PPP session
THREAD *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) 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)
{ {
PPP_SESSION *p; PPP_SESSION *p;
THREAD *t; THREAD *t;
@ -470,10 +518,14 @@ THREAD *NewPPPSession(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_
p = ZeroMalloc(sizeof(PPP_SESSION)); p = ZeroMalloc(sizeof(PPP_SESSION));
p->EnableMSCHAPv2 = true; p->EnableMSCHAPv2 = true;
p->AuthProtocol = NULL; p->AuthProtocol = PPP_UNSPECIFIED;
p->MsChapV2_ErrorCode = 691; p->MsChapV2_ErrorCode = 691;
p->EapClient = NULL; p->EapClient = NULL;
p->DataTimeout = PPP_DATA_TIMEOUT;
p->PacketRecvTimeout = PPP_PACKET_RECV_TIMEOUT;
p->UserConnectionTimeout = 0;
p->Cedar = cedar; p->Cedar = cedar;
AddRef(cedar->ref); AddRef(cedar->ref);
@ -510,7 +562,9 @@ THREAD *NewPPPSession(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_
// Thread creation // Thread creation
t = NewThread(PPPThread, p); t = NewThread(PPPThread, p);
return t; p->SessionThread = t;
return p;
} }
// PPP processing functions // PPP processing functions
@ -691,6 +745,9 @@ bool PPPProcessResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req)
case PPP_PROTOCOL_IPV6CP: case PPP_PROTOCOL_IPV6CP:
Debug("IPv6CP to be implemented\n"); Debug("IPv6CP to be implemented\n");
break; break;
case PPP_PROTOCOL_EAP:
return PPPProcessEAPResponsePacket(p, pp, req);
break;
default: 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); 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); PPPSetStatus(p, PPP_STATUS_FAIL);
@ -729,11 +786,11 @@ bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req
USHORT *protocol = pp->Lcp->Data; USHORT *protocol = pp->Lcp->Data;
if (*protocol == PPP_PROTOCOL_IPCP || *protocol == PPP_PROTOCOL_IP) if (*protocol == PPP_PROTOCOL_IPCP || *protocol == PPP_PROTOCOL_IP)
{ {
p->IPv4_State == PPP_PROTO_STATUS_REJECTED; p->IPv4_State = PPP_PROTO_STATUS_REJECTED;
} }
if (*protocol == PPP_PROTOCOL_IPV6CP || *protocol == PPP_PROTOCOL_IPV6) if (*protocol == PPP_PROTOCOL_IPV6CP || *protocol == PPP_PROTOCOL_IPV6)
{ {
p->IPv6_State == PPP_PROTO_STATUS_REJECTED; p->IPv6_State = PPP_PROTO_STATUS_REJECTED;
} }
} }
} }
@ -807,7 +864,37 @@ bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req
WHERE; WHERE;
return false; return false;
} }
if (opt->DataSize == sizeof(ms_chap_v2_code) && Cmp(opt->Data, ms_chap_v2_code, opt->DataSize) == 0) 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 // Try to request PAP then
if (!isAccepted || !p->EnableMSCHAPv2) if (!isAccepted || !p->EnableMSCHAPv2)
@ -826,7 +913,7 @@ bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req
return false; return false;
} }
} }
else if (p->AuthProtocol == NULL) else if (p->AuthProtocol == PPP_UNSPECIFIED)
{ {
p->AuthProtocol = PPP_PROTOCOL_CHAP; p->AuthProtocol = PPP_PROTOCOL_CHAP;
Debug("Setting BEFORE_AUTH from ACK on LCP response parse on CHAP accept\n"); Debug("Setting BEFORE_AUTH from ACK on LCP response parse on CHAP accept\n");
@ -848,7 +935,7 @@ bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req
WHERE; WHERE;
return false; return false;
} }
else if (p->AuthProtocol == NULL) else if (p->AuthProtocol == PPP_UNSPECIFIED)
{ {
p->AuthProtocol = PPP_PROTOCOL_PAP; p->AuthProtocol = PPP_PROTOCOL_PAP;
Debug("Setting BEFORE_AUTH from ACK on LCP response parse on PAP accept\n"); Debug("Setting BEFORE_AUTH from ACK on LCP response parse on PAP accept\n");
@ -1016,7 +1103,7 @@ bool PPPProcessIPCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *re
return true; return true;
} }
p->IPv4_State == PPP_PROTO_STATUS_CONFIG; p->IPv4_State = PPP_PROTO_STATUS_CONFIG;
PPPGetIPAddressValueFromLCP(req->Lcp, PPP_IPCP_OPTION_IP, &prevAddrStruct); PPPGetIPAddressValueFromLCP(req->Lcp, PPP_IPCP_OPTION_IP, &prevAddrStruct);
prevAddr = IPToUINT(&prevAddrStruct); prevAddr = IPToUINT(&prevAddrStruct);
@ -1046,6 +1133,78 @@ bool PPPProcessIPCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *re
return false; return false;
} }
// 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
{
PPP_EAP *eap;
Debug("We got a CODE=%i ID=%i from client with zero size EAP structure, that shouldn't be happening!\n", pp->Lcp->Code, pp->Lcp->Id);
eap = req->Lcp->Data;
if (eap->Type == PPP_EAP_TYPE_TLS)
{
PPP_LCP *lcp = BuildEAPTlsRequest(p->Eap_PacketId++, 0, PPP_EAP_TLS_FLAG_NONE);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
}
}
return false;
}
// Processes request packets // Processes request packets
bool PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *pp) bool PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *pp)
@ -1071,6 +1230,9 @@ bool PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *pp)
PPPRejectUnsupportedPacketEx(p, pp, true); PPPRejectUnsupportedPacketEx(p, pp, true);
Debug("IPv6CP to be implemented\n"); Debug("IPv6CP to be implemented\n");
break; break;
case PPP_PROTOCOL_EAP:
return PPPProcessEAPRequestPacket(p, pp);
break;
default: default:
Debug("Unsupported protocols should be already filtered out! protocol = 0x%x, code = 0x%x\n", pp->Protocol, pp->Lcp->Code); Debug("Unsupported protocols should be already filtered out! protocol = 0x%x, code = 0x%x\n", pp->Protocol, pp->Lcp->Code);
return false; return false;
@ -1088,6 +1250,7 @@ bool PPPProcessLCPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp)
USHORT NegotiatedMRU = PPP_UNSPECIFIED; USHORT NegotiatedMRU = PPP_UNSPECIFIED;
// MSCHAPv2 code // MSCHAPv2 code
UCHAR ms_chap_v2_code[3]; UCHAR ms_chap_v2_code[3];
USHORT eap_code = PPP_LCP_AUTH_EAP;
WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP); WRITE_USHORT(ms_chap_v2_code, PPP_LCP_AUTH_CHAP);
ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2; ms_chap_v2_code[2] = PPP_CHAP_ALG_MS_CHAP_V2;
@ -1102,22 +1265,27 @@ bool PPPProcessLCPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp)
{ {
case PPP_LCP_OPTION_AUTH: case PPP_LCP_OPTION_AUTH:
t->IsSupported = true; t->IsSupported = true;
if (t->DataSize == sizeof(USHORT) && *((USHORT*)t->Data) == Endian16(PPP_LCP_AUTH_PAP) && p->AuthProtocol != PPP_PROTOCOL_CHAP) if (t->DataSize == sizeof(USHORT) && *((USHORT*)t->Data) == PPP_LCP_AUTH_EAP && p->AuthProtocol == PPP_UNSPECIFIED)
{
t->IsAccepted = true;
NegotiatedAuthProto = PPP_PROTOCOL_EAP;
}
else if (t->DataSize == sizeof(USHORT) && *((USHORT*)t->Data) == PPP_LCP_AUTH_PAP && p->AuthProtocol == PPP_UNSPECIFIED)
{ {
t->IsAccepted = true; t->IsAccepted = true;
NegotiatedAuthProto = PPP_PROTOCOL_PAP; NegotiatedAuthProto = PPP_PROTOCOL_PAP;
} }
else if (t->DataSize == sizeof(ms_chap_v2_code) && Cmp(t->Data, ms_chap_v2_code, t->DataSize) == 0) 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; t->IsAccepted = true;
NegotiatedAuthProto = PPP_PROTOCOL_CHAP; NegotiatedAuthProto = PPP_PROTOCOL_CHAP;
} }
else else
{ {
// We're recommending MSCHAPv2 by default as a more secure algo // We're recommending EAP by default as a more secure algo
t->IsAccepted = false; t->IsAccepted = false;
t->AltDataSize = sizeof(ms_chap_v2_code); t->AltDataSize = sizeof(eap_code);
Copy(t->AltData, ms_chap_v2_code, sizeof(ms_chap_v2_code)); Copy(t->AltData, &eap_code, sizeof(eap_code));
} }
break; break;
case PPP_LCP_OPTION_MRU: case PPP_LCP_OPTION_MRU:
@ -1173,7 +1341,7 @@ bool PPPProcessLCPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp)
if (NegotiatedAuthProto != PPP_UNSPECIFIED) if (NegotiatedAuthProto != PPP_UNSPECIFIED)
{ {
if (p->AuthProtocol == NULL) if (p->AuthProtocol == PPP_UNSPECIFIED)
{ {
p->AuthProtocol = NegotiatedAuthProto; p->AuthProtocol = NegotiatedAuthProto;
PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH); PPPSetStatus(p, PPP_STATUS_BEFORE_AUTH);
@ -1292,6 +1460,13 @@ bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp)
if (ipc != NULL) if (ipc != NULL)
{ {
p->Ipc = ipc; 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; p->AuthOk = true;
} }
else else
@ -1354,7 +1529,7 @@ bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp)
return false; return false;
} }
return p->AuthOk;
} }
@ -1648,7 +1823,7 @@ bool PPPProcessIPCPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp)
// We will delay this packet ACK and send the server IP first, then wait for a reparse // 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) // 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 != NULL && ok) if (p->IPv4_State == PPP_PROTO_STATUS_CLOSED && p->ClientAddressOption.ServerAddress != 0 && ok)
{ {
PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0); PPP_LCP *c = NewPPPLCP(PPP_LCP_CODE_REQ, 0);
UINT ui = p->ClientAddressOption.ServerAddress; UINT ui = p->ClientAddressOption.ServerAddress;
@ -1689,6 +1864,13 @@ bool PPPProcessIPCPRequestPacket(PPP_SESSION *p, PPP_PACKET* pp)
return ok; return ok;
} }
// 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 // LCP option based packets utility
bool PPPRejectLCPOptions(PPP_SESSION *p, PPP_PACKET *pp) bool PPPRejectLCPOptions(PPP_SESSION *p, PPP_PACKET *pp)
{ {
@ -1889,7 +2071,7 @@ bool PPPSendAndRetransmitRequest(PPP_SESSION *p, USHORT protocol, PPP_LCP *c)
resend->Id = pp->Lcp->Id; resend->Id = pp->Lcp->Id;
resend->Packet = pp; resend->Packet = pp;
resend->ResendTime = now + PPP_PACKET_RESEND_INTERVAL; resend->ResendTime = now + PPP_PACKET_RESEND_INTERVAL;
resend->TimeoutTime = now + PPP_PACKET_RECV_TIMEOUT; resend->TimeoutTime = now + p->PacketRecvTimeout;
Add(p->SentReqPacketList, resend); Add(p->SentReqPacketList, resend);
@ -1946,7 +2128,7 @@ LABEL_LOOP:
if (async == false) if (async == false)
{ {
d = TubeRecvSync(p->TubeRecv, PPP_PACKET_RECV_TIMEOUT); d = TubeRecvSync(p->TubeRecv, p->PacketRecvTimeout);
} }
else else
{ {
@ -2248,7 +2430,7 @@ PPP_PACKET *ParsePPPPacket(void *data, UINT size)
size -= 2; size -= 2;
buf += 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) 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->IsControl = true;
} }
@ -2499,9 +2681,17 @@ bool PPPParseMSCHAP2ResponsePacket(PPP_SESSION* p, PPP_PACKET* pp)
{ {
p->Ipc = ipc; 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();
Copy(p->MsChapV2_ServerResponse, ipc->MsChapV2_ServerResponse, 20); Copy(p->MsChapV2_ServerResponse, ipc->MsChapV2_ServerResponse, 20);
ok = true; ok = true;
p->AuthOk = true;
} }
} }
else else
@ -2813,6 +3003,332 @@ bool PPPGetIPAddressValueFromLCP(PPP_LCP *c, UINT type, IP *ip)
return true; return true;
} }
// EAP packet utilities
bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapTlsSize)
{
UCHAR *dataBuffer;
UINT dataSize;
UINT tlsLength = 0;
UINT i;
bool isFragmented = false;
PPP_LCP *lcp;
PPP_EAP *eap;
UCHAR flags = PPP_EAP_TLS_FLAG_NONE;
UINT64 sizeLeft = 0;
Debug("Got EAP-TLS size=%i\n", eapTlsSize);
if (eapTlsSize == 1)
{
// This is an EAP-TLS message ACK
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)
sizeLeft = GetMemSize(p->Eap_TlsCtx.CachedBufferSend);
sizeLeft -= p->Eap_TlsCtx.CachedBufferSendPntr - p->Eap_TlsCtx.CachedBufferSend;
flags = PPP_EAP_TLS_FLAG_FRAGMENTED; // M flag
if (dataSize > sizeLeft)
{
dataSize = sizeLeft;
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;
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;
}
Debug("Sent EAP-TLS size=%i type=%i flag=%i\n", lcp->DataSize, eap->Type, eap->Tls.Flags);
if (flags == PPP_EAP_TLS_FLAG_NONE)
{
// As it is the latest message, we need to cleanup
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;
/*if (!p->Eap_TlsCtx.SslPipe->IsDisconnected)
{
dataSize = FifoSize(p->Eap_TlsCtx.SslPipe->RawOut->RecvFifo);
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;
}
Debug("Sent EAP-TLS size=%i type=%i flag=%i\n", lcp->DataSize, eap->Type, eap->Tls.Flags);
return true;
}*/
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)
{
PPP_PACKET *pack;
UINT identificator = p->Eap_PacketId - 1; // THIS IS A HACK TO SUPPORT VPN Client Pro on Android!!!
p->Ipc = ipc;
PPPSetStatus(p, PPP_STATUS_AUTH_SUCCESS);
// Just send an EAP-Success
pack = ZeroMalloc(sizeof(PPP_PACKET));
pack->IsControl = true;
pack->Protocol = PPP_PROTOCOL_EAP;
lcp = NewPPPLCP(PPP_EAP_CODE_SUCCESS, identificator);
pack->Lcp = lcp;
Debug("Sent EAP-TLS size=%i SUCCESS\n", lcp->DataSize);
if (!PPPSendPacketAndFree(p, pack))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return true;
}
else
{
PPP_PACKET *pack;
UINT identificator = p->Eap_PacketId - 1; // THIS IS A HACK TO SUPPORT VPN Client Pro on Android!!!
PPPSetStatus(p, PPP_STATUS_AUTH_FAIL);
pack = ZeroMalloc(sizeof(PPP_PACKET));
pack->IsControl = true;
pack->Protocol = PPP_PROTOCOL_EAP;
lcp = NewPPPLCP(PPP_EAP_CODE_FAILURE, identificator);
pack->Lcp = lcp;
Debug("Sent EAP-TLS size=%i FAILURE\n", lcp->DataSize);
if (!PPPSendPacketAndFree(p, pack))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return false;
}
}
else
{
// Some clients needs a little help it seems - namely VPN Client Pro on Android
flags |= PPP_EAP_TLS_FLAG_SSLSTARTED;
lcp = BuildEAPTlsRequest(p->Eap_PacketId++, 0, flags);
PPPSetStatus(p, PPP_STATUS_AUTHENTICATING);
if (!PPPSendAndRetransmitRequest(p, PPP_PROTOCOL_EAP, lcp))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
Debug("Sent EAP-TLS size=%i\n", lcp->DataSize);
return true;
}
}
return true;
}
dataBuffer = eap_packet->Tls.TlsDataWithoutLength;
dataSize = eapTlsSize - 1;
if (eap_packet->Tls.Flags & PPP_EAP_TLS_FLAG_TLS_LENGTH)
{
dataBuffer = eap_packet->Tls.TlsDataWithLength.Data;
dataSize -= 4;
tlsLength = Endian32(eap_packet->Tls.TlsDataWithLength.TlsLength);
}
/*Debug("=======RECV EAP-TLS PACKET DUMP=======\n");
for (i = 0; i < dataSize; i++)
{
if (i > 0) printf(" ");
Debug("%02X", dataBuffer[i]);
}
Debug("\n=======RECV EAP-TLS PACKET DUMP END=======\n");*/
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
if (isFragmented || p->Eap_TlsCtx.CachedBufferRecv != NULL)
{
if (p->Eap_TlsCtx.CachedBufferRecv == NULL && tlsLength > 0)
{
p->Eap_TlsCtx.CachedBufferRecv = ZeroMalloc(MAX(dataSize, tlsLength));
p->Eap_TlsCtx.CachedBufferRecvPntr = p->Eap_TlsCtx.CachedBufferRecv;
}
else if (p->Eap_TlsCtx.CachedBufferRecv == NULL)
{
p->Eap_TlsCtx.CachedBufferRecv = ZeroMalloc(MAX(dataSize, PPP_MRU_MAX * 10)); // 10 MRUs should be enough
p->Eap_TlsCtx.CachedBufferRecvPntr = p->Eap_TlsCtx.CachedBufferRecv;
}
sizeLeft = GetMemSize(p->Eap_TlsCtx.CachedBufferRecv);
sizeLeft -= p->Eap_TlsCtx.CachedBufferRecvPntr - p->Eap_TlsCtx.CachedBufferRecv;
Copy(p->Eap_TlsCtx.CachedBufferRecvPntr, dataBuffer, MIN(sizeLeft, dataSize));
p->Eap_TlsCtx.CachedBufferRecvPntr += MIN(sizeLeft, dataSize);
}
// If we got a cached buffer, we should feed the FIFOs via it
if (p->Eap_TlsCtx.CachedBufferRecv != NULL)
{
dataBuffer = p->Eap_TlsCtx.CachedBufferRecv;
dataSize = GetMemSize(p->Eap_TlsCtx.CachedBufferRecv);
if (dataSize == MAX_BUFFERING_PACKET_SIZE)
{
dataSize = p->Eap_TlsCtx.CachedBufferRecvPntr - 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;
}
Debug("Sent EAP-TLS size=%i\n", lcp->DataSize);
}
else
{
/*Debug("=======RECV EAP-TLS FIFO DUMP=======\n");
for (i = 0; i < dataSize; i++)
{
if (i > 0) printf(" ");
Debug("%02X", dataBuffer[i]);
}
Debug("\n=======RECV EAP-TLS PACKET FIFO END=======\n");*/
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
if (p->Eap_TlsCtx.CachedBufferRecv != NULL)
{
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)
{
if (p->Eap_TlsCtx.CachedBufferSend == NULL)
{
p->Eap_TlsCtx.CachedBufferSend = ZeroMalloc(dataSize);
p->Eap_TlsCtx.CachedBufferSendPntr = p->Eap_TlsCtx.CachedBufferSend;
}
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)
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;
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;
}
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;
}
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);
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 // Other packet utilities
// Get the option value // Get the option value
@ -2924,6 +3440,29 @@ void FreePPPSession(PPP_SESSION *p)
p->TubeRecv->IntParam2 = p->DisconnectCauseDirection; p->TubeRecv->IntParam2 = p->DisconnectCauseDirection;
} }
// Freeing EAP-TLS context
if (p->Eap_TlsCtx.CachedBufferRecv != NULL)
{
Free(p->Eap_TlsCtx.CachedBufferRecv);
}
if (p->Eap_TlsCtx.CachedBufferSend != NULL)
{
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);
}
FreeTubeFlushList(p->FlushList); FreeTubeFlushList(p->FlushList);
TubeDisconnect(p->TubeRecv); TubeDisconnect(p->TubeRecv);
@ -3337,3 +3876,5 @@ char *MsChapV2DoBruteForce(IPC_MSCHAP_V2_AUTHINFO *d, LIST *password_list)
return NULL; return NULL;
} }

View File

@ -21,22 +21,25 @@
#define PPP_CHAP_CODE_IS_REQUEST(c) ((c) == PPP_CHAP_CODE_CHALLENGE || (c) == PPP_CHAP_CODE_SUCCESS || (c) == PPP_CHAP_CODE_FAILURE) #define PPP_CHAP_CODE_IS_REQUEST(c) ((c) == PPP_CHAP_CODE_CHALLENGE || (c) == PPP_CHAP_CODE_SUCCESS || (c) == PPP_CHAP_CODE_FAILURE)
#define PPP_CHAP_CODE_IS_RESPONSE(c) ((c) == PPP_CHAP_CODE_RESPONSE) #define PPP_CHAP_CODE_IS_RESPONSE(c) ((c) == PPP_CHAP_CODE_RESPONSE)
#define PPP_CODE_IS_RESPONSE(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_RESPONSE(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_RESPONSE(c)) || (((protocol) == PPP_PROTOCOL_CHAP) && PPP_CHAP_CODE_IS_RESPONSE(c))) #define PPP_EAP_CODE_IS_REQUEST(c) ((c) == PPP_EAP_CODE_REQUEST)
#define PPP_CODE_IS_REQUEST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_CHAP) && PPP_CHAP_CODE_IS_REQUEST(c))) #define PPP_EAP_CODE_IS_RESPONSE(c) ((c) == PPP_EAP_CODE_RESPONSE || (c) == PPP_EAP_CODE_SUCCESS || (c) == PPP_EAP_CODE_FAILURE)
#define PPP_CODE_IS_RESPONSE(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_RESPONSE(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_RESPONSE(c)) || (((protocol) == PPP_PROTOCOL_CHAP) && PPP_CHAP_CODE_IS_RESPONSE(c)) || (((protocol) == PPP_PROTOCOL_EAP) && PPP_EAP_CODE_IS_RESPONSE(c)))
#define PPP_CODE_IS_REQUEST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_PAP) && PPP_PAP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_CHAP) && PPP_CHAP_CODE_IS_REQUEST(c)) || (((protocol) == PPP_PROTOCOL_EAP) && PPP_EAP_CODE_IS_REQUEST(c)))
#define PPP_CODE_IS_WITH_OPTION_LIST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_WITH_OPTION_LIST(c)) || false) #define PPP_CODE_IS_WITH_OPTION_LIST(protocol, c) ((((protocol) == PPP_PROTOCOL_LCP || (protocol) == PPP_PROTOCOL_IPCP || (protocol) == PPP_PROTOCOL_IPV6CP) && PPP_LCP_CODE_IS_WITH_OPTION_LIST(c)) || false)
#define PPP_IS_SUPPORTED_PROTOCOL(p) ((p) == PPP_PROTOCOL_LCP || (p) == PPP_PROTOCOL_PAP || (p) == PPP_PROTOCOL_CHAP || (p) == PPP_PROTOCOL_IPCP || (p) == PPP_PROTOCOL_IPV6CP || (p) == PPP_PROTOCOL_IP || (p) == PPP_PROTOCOL_IPV6) #define PPP_IS_SUPPORTED_PROTOCOL(p) ((p) == PPP_PROTOCOL_LCP || (p) == PPP_PROTOCOL_PAP || (p) == PPP_PROTOCOL_CHAP || (p) == PPP_PROTOCOL_IPCP || (p) == PPP_PROTOCOL_IPV6CP || (p) == PPP_PROTOCOL_IP || (p) == PPP_PROTOCOL_IPV6 || (p) == PPP_PROTOCOL_EAP )
#define PPP_STATUS_IS_UNAVAILABLE(c) ((c) == PPP_STATUS_FAIL || (c) == PPP_STATUS_AUTH_FAIL || (c) == PPP_STATUS_CLOSING || (c) == PPP_STATUS_CLOSING_WAIT || (c) == PPP_STATUS_CLOSED) #define PPP_STATUS_IS_UNAVAILABLE(c) ((c) == PPP_STATUS_FAIL || (c) == PPP_STATUS_AUTH_FAIL || (c) == PPP_STATUS_CLOSING || (c) == PPP_STATUS_CLOSING_WAIT || (c) == PPP_STATUS_CLOSED)
//// Constants //// Constants
// Time-out value // Time-out value
#define PPP_PACKET_RECV_TIMEOUT (30 * 1000) // Timeout until the next packet is received #define PPP_PACKET_RECV_TIMEOUT (15 * 1000) // Timeout until the next packet is received (3/4 of default policy)
#define PPP_PACKET_RESEND_INTERVAL (5 * 1000) // Retransmission interval of the last packet #define PPP_PACKET_RESEND_INTERVAL (3 * 1000) // Retransmission interval of the last packet
#define PPP_TERMINATE_TIMEOUT 2000 // Timeout value to complete disconnection after requesting to disconnect in the PPP #define PPP_TERMINATE_TIMEOUT 2000 // Timeout value to complete disconnection after requesting to disconnect in the PPP
#define PPP_ECHO_SEND_INTERVAL 4792 // Transmission interval of PPP Echo Request #define PPP_ECHO_SEND_INTERVAL 4792 // Transmission interval of PPP Echo Request
#define PPP_DATA_TIMEOUT (60 * 1000) // Communication time-out #define PPP_DATA_TIMEOUT (20 * 1000) // Communication time-out (from default policy)
// MRU // MRU
#define PPP_MRU_DEFAULT 1500 // Default value #define PPP_MRU_DEFAULT 1500 // Default value
@ -48,6 +51,7 @@
#define PPP_PROTOCOL_PAP 0xc023 #define PPP_PROTOCOL_PAP 0xc023
#define PPP_PROTOCOL_IPCP 0x8021 #define PPP_PROTOCOL_IPCP 0x8021
#define PPP_PROTOCOL_CHAP 0xc223 #define PPP_PROTOCOL_CHAP 0xc223
#define PPP_PROTOCOL_EAP 0xc227
#define PPP_PROTOCOL_IPV6CP 0x8057 #define PPP_PROTOCOL_IPV6CP 0x8057
// PPP protocol (for transfer) // PPP protocol (for transfer)
@ -93,9 +97,28 @@
// IPV6CP option type // IPV6CP option type
#define PPP_IPV6CP_OPTION_IID 1 #define PPP_IPV6CP_OPTION_IID 1
// EAP codes
#define PPP_EAP_CODE_REQUEST 1
#define PPP_EAP_CODE_RESPONSE 2
#define PPP_EAP_CODE_SUCCESS 3
#define PPP_EAP_CODE_FAILURE 4
// EAP types
#define PPP_EAP_TYPE_IDENTITY 1
#define PPP_EAP_TYPE_NOTIFICATION 2
#define PPP_EAP_TYPE_NAK 3
#define PPP_EAP_TYPE_TLS 13
// EAP-TLS Flags
#define PPP_EAP_TLS_FLAG_NONE 0
#define PPP_EAP_TLS_FLAG_TLS_LENGTH 1 << 7
#define PPP_EAP_TLS_FLAG_FRAGMENTED 1 << 6
#define PPP_EAP_TLS_FLAG_SSLSTARTED 1 << 5
// Authentication protocol // Authentication protocol
#define PPP_LCP_AUTH_PAP PPP_PROTOCOL_PAP #define PPP_LCP_AUTH_PAP PPP_PROTOCOL_PAP
#define PPP_LCP_AUTH_CHAP PPP_PROTOCOL_CHAP #define PPP_LCP_AUTH_CHAP PPP_PROTOCOL_CHAP
#define PPP_LCP_AUTH_EAP PPP_PROTOCOL_EAP
// Algorithm of CHAP // Algorithm of CHAP
#define PPP_CHAP_ALG_MS_CHAP_V2 0x81 #define PPP_CHAP_ALG_MS_CHAP_V2 0x81
@ -164,6 +187,53 @@ struct PPP_OPTION
UINT AltDataSize; // Alternate data size UINT AltDataSize; // Alternate data size
}; };
#ifdef OS_WIN32
#pragma pack(push, 1)
#else // OS_WIN32
#pragma pack(1)
#endif
// PPP EAP packet
// EAP is a subset of LCP, sharing Code and Id. The Data field is then mapped to this structure
// We got 8 bytes of size before this structure
struct PPP_EAP
{
UCHAR Type;
union {
UCHAR Data[0];
struct PPP_EAP_TLS
{
UCHAR Flags;
union {
UCHAR TlsDataWithoutLength[0];
struct
{
UINT32 TlsLength;
UCHAR Data[0];
} TlsDataWithLength;
};
} Tls;
};
} GCC_PACKED;
#ifdef OS_WIN32
#pragma pack(pop)
#else // OS_WIN32
#pragma pack()
#endif
struct PPP_EAP_TLS_CONTEXT
{
SSL_PIPE *SslPipe;
DH_CTX *Dh;
struct SslClientCertInfo ClientCert;
UCHAR *CachedBufferRecv;
UCHAR *CachedBufferRecvPntr;
UCHAR *CachedBufferSend;
UCHAR *CachedBufferSendPntr;
};
// PPP request resend // PPP request resend
struct PPP_REQUEST_RESEND struct PPP_REQUEST_RESEND
{ {
@ -234,10 +304,23 @@ struct PPP_SESSION
UINT IPv4_State; UINT IPv4_State;
UINT IPv6_State; UINT IPv6_State;
// EAP contexts
UINT Eap_Protocol; // Current EAP Protocol used
UINT Eap_PacketId; // EAP Packet ID;
UCHAR Eap_Identity[MAX_SIZE]; // Received from client identity
PPP_EAP_TLS_CONTEXT Eap_TlsCtx; // Context information for EAP TLS. May be possibly reused for EAP TTLS?
LIST *SentReqPacketList; // Sent requests list LIST *SentReqPacketList; // Sent requests list
PPP_PACKET *CurrentPacket; PPP_PACKET *CurrentPacket;
LIST *DelayedPackets; LIST *DelayedPackets;
UINT64 PacketRecvTimeout;
UINT64 DataTimeout;
UINT64 UserConnectionTimeout;
UINT64 UserConnectionTick;
THREAD *SessionThread; // Thread of the PPP session
}; };
@ -248,7 +331,7 @@ struct PPP_SESSION
void PPPThread(THREAD *thread, void *param); void PPPThread(THREAD *thread, void *param);
// Entry point // Entry point
THREAD *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); 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);
// PPP processing functions // PPP processing functions
bool PPPRejectUnsupportedPacket(PPP_SESSION *p, PPP_PACKET *pp); bool PPPRejectUnsupportedPacket(PPP_SESSION *p, PPP_PACKET *pp);
@ -260,11 +343,13 @@ bool PPPProcessResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req);
bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req); bool PPPProcessLCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req);
bool PPPProcessCHAPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req); bool PPPProcessCHAPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req);
bool PPPProcessIPCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req); bool PPPProcessIPCPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req);
bool PPPProcessEAPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req);
// Request packets // Request packets
bool PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *pp); bool PPPProcessRequestPacket(PPP_SESSION *p, PPP_PACKET *pp);
bool PPPProcessLCPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp); bool PPPProcessLCPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp);
bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp); bool PPPProcessPAPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp);
bool PPPProcessIPCPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp); bool PPPProcessIPCPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp);
bool PPPProcessEAPRequestPacket(PPP_SESSION *p, PPP_PACKET *pp);
// LCP option based packets utility // LCP option based packets utility
bool PPPRejectLCPOptions(PPP_SESSION *p, PPP_PACKET *pp); bool PPPRejectLCPOptions(PPP_SESSION *p, PPP_PACKET *pp);
@ -274,7 +359,6 @@ bool PPPNackLCPOptionsEx(PPP_SESSION *p, PPP_PACKET* pp, bool simulate);
bool PPPAckLCPOptions(PPP_SESSION *p, PPP_PACKET *pp); bool PPPAckLCPOptions(PPP_SESSION *p, PPP_PACKET *pp);
bool PPPAckLCPOptionsEx(PPP_SESSION *p, PPP_PACKET *pp, bool simulate); bool PPPAckLCPOptionsEx(PPP_SESSION *p, PPP_PACKET *pp, bool simulate);
// PPP networking functions // PPP networking functions
// Send packets // Send packets
bool PPPSendAndRetransmitRequest(PPP_SESSION *p, USHORT protocol, PPP_LCP *c); bool PPPSendAndRetransmitRequest(PPP_SESSION *p, USHORT protocol, PPP_LCP *c);
@ -305,6 +389,11 @@ bool PPPGetIPOptionFromLCP(PPP_IPOPTION *o, PPP_LCP *c);
bool PPPSetIPOptionToLCP(PPP_IPOPTION *o, PPP_LCP *c, bool only_modify); bool PPPSetIPOptionToLCP(PPP_IPOPTION *o, PPP_LCP *c, bool only_modify);
bool PPPGetIPAddressValueFromLCP(PPP_LCP *c, UINT type, IP *ip); bool PPPGetIPAddressValueFromLCP(PPP_LCP *c, UINT type, IP *ip);
bool PPPSetIPAddressValueToLCP(PPP_LCP *c, UINT type, IP *ip, bool only_modify); bool PPPSetIPAddressValueToLCP(PPP_LCP *c, UINT type, IP *ip, bool only_modify);
// EAP packet utilities
bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapTlsSize);
PPP_LCP *BuildEAPPacketEx(UCHAR code, UCHAR id, UCHAR type, UINT datasize);
PPP_LCP *BuildEAPTlsPacketEx(UCHAR code, UCHAR id, UCHAR type, UINT datasize, UCHAR flags);
PPP_LCP *BuildEAPTlsRequest(UCHAR id, UINT datasize, UCHAR flags);
// Other packet utilities // Other packet utilities
PPP_OPTION *PPPGetOptionValue(PPP_LCP *c, UCHAR type); PPP_OPTION *PPPGetOptionValue(PPP_LCP *c, UCHAR type);
bool IsHubExistsWithLock(CEDAR *cedar, char *hubname); bool IsHubExistsWithLock(CEDAR *cedar, char *hubname);

View File

@ -97,6 +97,8 @@ void SstpProcessControlPacket(SSTP_SERVER *s, SSTP_PACKET *p)
// Process the SSTP received data packet // Process the SSTP received data packet
void SstpProcessDataPacket(SSTP_SERVER *s, SSTP_PACKET *p) void SstpProcessDataPacket(SSTP_SERVER *s, SSTP_PACKET *p)
{ {
PPP_SESSION *underlyingSession;
// Validate arguments // Validate arguments
if (s == NULL || p == NULL || p->IsControl) if (s == NULL || p == NULL || p->IsControl)
{ {
@ -108,9 +110,11 @@ void SstpProcessDataPacket(SSTP_SERVER *s, SSTP_PACKET *p)
if (s->PPPThread == NULL) if (s->PPPThread == NULL)
{ {
// Create a thread to initialize the new PPP module // Create a thread to initialize the new PPP module
s->PPPThread = NewPPPSession(s->Cedar, &s->ClientIp, s->ClientPort, &s->ServerIp, s->ServerPort, underlyingSession = NewPPPSession(s->Cedar, &s->ClientIp, s->ClientPort, &s->ServerIp, s->ServerPort,
s->TubeSend, s->TubeRecv, SSTP_IPC_POSTFIX, SSTP_IPC_CLIENT_NAME, s->TubeSend, s->TubeRecv, SSTP_IPC_POSTFIX, SSTP_IPC_CLIENT_NAME,
s->ClientHostName, s->ClientCipherName, 0); s->ClientHostName, s->ClientCipherName, 0);
s->PPPSession = underlyingSession;
s->PPPThread = underlyingSession->SessionThread;
} }
// Pass the received data to the PPP module // Pass the received data to the PPP module
@ -177,6 +181,7 @@ void SstpSendPacket(SSTP_SERVER *s, SSTP_PACKET *p)
// Process the timer interrupt // Process the timer interrupt
void SstpProcessInterrupt(SSTP_SERVER *s) void SstpProcessInterrupt(SSTP_SERVER *s)
{ {
UINT64 sstpTimeout = SSTP_TIMEOUT;
// Validate arguments // Validate arguments
if (s == NULL) if (s == NULL)
{ {
@ -261,7 +266,12 @@ void SstpProcessInterrupt(SSTP_SERVER *s)
} }
} }
if ((s->LastRecvTick + (UINT64)SSTP_TIMEOUT) <= s->Now) if (s->PPPSession != NULL && s->PPPSession->DataTimeout > sstpTimeout)
{
sstpTimeout = s->PPPSession->DataTimeout;
}
if ((s->LastRecvTick + sstpTimeout) <= s->Now)
{ {
// Disconnect the SSTP because a timeout occurred // Disconnect the SSTP because a timeout occurred
SstpAbort(s); SstpAbort(s);

View File

@ -16,7 +16,7 @@
#define SSTP_IPC_POSTFIX "SSTP" #define SSTP_IPC_POSTFIX "SSTP"
#define SSTP_ECHO_SEND_INTERVAL_MIN 2500 // Transmission interval of Echo Request (minimum) #define SSTP_ECHO_SEND_INTERVAL_MIN 2500 // Transmission interval of Echo Request (minimum)
#define SSTP_ECHO_SEND_INTERVAL_MAX 4792 // Transmission interval of Echo Request (maximum) #define SSTP_ECHO_SEND_INTERVAL_MAX 4792 // Transmission interval of Echo Request (maximum)
#define SSTP_TIMEOUT 10000 // Communication time-out of SSTP #define SSTP_TIMEOUT 20 * 1000 // Communication time-out of SSTP (from default policy)
// SSTP Message Type // SSTP Message Type
#define SSTP_MSG_CALL_CONNECT_REQUEST 0x0001 #define SSTP_MSG_CALL_CONNECT_REQUEST 0x0001
@ -116,6 +116,7 @@ struct SSTP_SERVER
UINT64 LastRecvTick; // Tick when some data has received at the end UINT64 LastRecvTick; // Tick when some data has received at the end
bool FlushRecvTube; // Flag whether to flush the reception tube bool FlushRecvTube; // Flag whether to flush the reception tube
UINT EstablishedCount; // Number of session establishment UINT EstablishedCount; // Number of session establishment
PPP_SESSION *PPPSession; // Underlying PPP Session
}; };

View File

@ -5701,10 +5701,17 @@ int SslCertVerifyCallback(int preverify_ok, X509_STORE_CTX *ctx)
if (cert != NULL) if (cert != NULL)
{ {
X *tmpX = X509ToX(cert); // this only wraps cert, but we need to make a copy X *tmpX = X509ToX(cert); // this only wraps cert, but we need to make a copy
if (!CompareX(tmpX, clientcert->X))
{
X* copyX = CloneX(tmpX); X* copyX = CloneX(tmpX);
if (clientcert->X != NULL)
{
FreeX(clientcert->X);
}
clientcert->X = copyX;
}
tmpX->do_not_free = true; // do not release inner X509 object tmpX->do_not_free = true; // do not release inner X509 object
FreeX(tmpX); FreeX(tmpX);
clientcert->X = copyX;
} }
} }
} }
@ -5729,9 +5736,13 @@ SSL_PIPE *NewSslPipeEx(bool server_mode, X *x, K *k, DH_CTX *dh, bool verify_pee
{ {
if (server_mode) if (server_mode)
{ {
SSL_CTX_set_ssl_version(ssl_ctx, SSLv23_method()); SSL_CTX_set_ssl_version(ssl_ctx, SSLv23_server_method());
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2);
#ifdef SSL_OP_NO_TLSv1_3
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); // For some reason pppd under linux doesn't like it
#endif
AddChainSslCertOnDirectory(ssl_ctx); AddChainSslCertOnDirectory(ssl_ctx);
if (dh != NULL) if (dh != NULL)
@ -11784,8 +11795,17 @@ bool AddChainSslCert(struct ssl_ctx_st *ctx, X *x)
x_copy = CloneX(x); x_copy = CloneX(x);
if (x_copy != NULL) if (x_copy != NULL)
{
if (x_copy->root_cert)
{
X509_STORE* store = SSL_CTX_get_cert_store(ctx);
X509_STORE_add_cert(store, x_copy->x509);
X509_free(x_copy->x509);
}
else
{ {
SSL_CTX_add_extra_chain_cert(ctx, x_copy->x509); SSL_CTX_add_extra_chain_cert(ctx, x_copy->x509);
}
x_copy->do_not_free = true; x_copy->do_not_free = true;
ret = true; ret = true;