1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2025-07-07 08:14:58 +03:00

Reworked EAP-TLS 1.3 to account for RFC9190, implemented searching by certificate instead of certificate CN

This commit is contained in:
Evengard
2023-01-31 18:37:16 +03:00
parent 26403c70e3
commit edcdc923ad
14 changed files with 197 additions and 146 deletions

View File

@ -897,21 +897,24 @@ USER *AcGetUser(HUB *h, char *name)
return u;
}
USER* AcGetUserByCert(HUB *h, char *common_name)
USER* AcGetUserByCert(HUB *h, X *cert)
{
int i;
if (cert == NULL)
{
return NULL;
}
for (i = 0; i < LIST_NUM(h->HubDb->UserList); i++)
{
USER* u = LIST_DATA(h->HubDb->UserList, i);
if (u->AuthType == AUTHTYPE_USERCERT)
{
X* cert = ((AUTHUSERCERT*)u->AuthData)->UserX;
if (cert != NULL)
X* ucert = ((AUTHUSERCERT*)u->AuthData)->UserX;
if (ucert != NULL)
{
char cname[MAX_SIZE];
UniToStr(cname, sizeof(cname), cert->subject_name->CommonName);
if (StrCmp(common_name, cname) == 0)
if (CompareX(cert, ucert))
{
AddRef(u->ref);
return u;

View File

@ -177,7 +177,7 @@ void FreeAuthData(UINT authtype, void *authdata);
bool AcAddUser(HUB *h, USER *u);
bool AcAddGroup(HUB *h, USERGROUP *g);
USER *AcGetUser(HUB *h, char *name);
USER* AcGetUserByCert(HUB* h, char *common_name);
USER* AcGetUserByCert(HUB* h, X *cert);
USERGROUP *AcGetGroup(HUB *h, char *name);
bool AcIsUser(HUB *h, char *name);
bool AcIsGroup(HUB *h, char *name);

View File

@ -600,7 +600,7 @@ THREAD *NewPPPSession(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_
p->EapClient = NULL;
Zero(&p->Eap_Identity, sizeof(p->Eap_Identity));
p->Eap_TlsCtx.DisableTls13 = false;
p->Eap_TlsCtx.DisableTls13SessionTickets = false;
p->Eap_TlsCtx.Tls13SessionTicketsCount = 2; // Default count as per hardcoded in OpenSSL
p->DataTimeout = PPP_DATA_TIMEOUT;
p->PacketRecvTimeout = PPP_PACKET_RECV_TIMEOUT;
@ -1280,6 +1280,7 @@ bool PPPProcessEAPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req
case PPP_EAP_TYPE_IDENTITY:
// Parse username
Copy(eapidentitypkt, eap_packet->Data, MIN(MAX_SIZE, eap_datasize));
Zero(&p->Eap_Identity, sizeof(p->Eap_Identity));
PPPParseUsername(p->Cedar, eapidentitypkt, &p->Eap_Identity);
Debug("EAP: username=%s, hubname=%s\n", p->Eap_Identity.UserName, p->Eap_Identity.HubName);
@ -1295,15 +1296,6 @@ bool PPPProcessEAPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req
AcLock(hub);
{
USER *user = AcGetUser(hub, p->Eap_Identity.UserName);
if (user == NULL && hub->Option->AllowEapMatchUserByCert == true)
{
user = AcGetUserByCert(hub, p->Eap_Identity.UserName);
if (user != NULL)
{
// We overwrite the identity by the one we found
StrCpy(p->Eap_Identity.UserName, sizeof(p->Eap_Identity.UserName), user->Name);
}
}
if (user == NULL)
{
user = AcGetUser(hub, "*");
@ -1314,12 +1306,18 @@ bool PPPProcessEAPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req
authtype = user->AuthType;
ReleaseUser(user);
}
else if (hub->Option->AllowEapMatchUserByCert == true)
{
authtype = AUTHTYPE_USERCERT;
Zero(p->Eap_Identity.UserName, sizeof(p->Eap_Identity.UserName));
p->Eap_MatchUserByCert = true;
}
}
AcUnlock(hub);
ReleaseHub(hub);
}
if (found == false)
if (found == false && p->Eap_MatchUserByCert == false)
{
// User not found, fail immediately
PPP_PACKET *pack = ZeroMalloc(sizeof(PPP_PACKET));
@ -1384,7 +1382,7 @@ bool PPPProcessEAPResponsePacket(PPP_SESSION *p, PPP_PACKET *pp, PPP_PACKET *req
// Basically this is just an acknoweldgment that the notification was accepted by the client. Nothing to do here...
break;
case PPP_EAP_TYPE_NAK:
if (p->Eap_Protocol == PPP_EAP_TYPE_TLS)
if (p->Eap_Protocol == PPP_EAP_TYPE_TLS && p->Eap_MatchUserByCert == false)
{
// Propose EAP-MSCHAPv2
p->Eap_Protocol = PPP_EAP_TYPE_MSCHAPV2;
@ -3442,7 +3440,7 @@ bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapSize)
UCHAR flags = PPP_EAP_TLS_FLAG_NONE;
UINT sizeLeft = 0;
Debug("Got EAP-TLS size=%i\n", eapSize);
if (eapSize == 1 && p->Eap_TlsCtx.ClientCert.X == NULL)
if (eapSize == 1 && eap_packet->Tls.Flags == PPP_EAP_TLS_FLAG_NONE)
{
// This is an EAP-TLS message ACK
if (p->Eap_TlsCtx.CachedBufferSend != NULL)
@ -3480,6 +3478,28 @@ bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapSize)
p->Eap_TlsCtx.CachedBufferSendPntr = NULL;
}
}
else if (p->AuthOk == true && p->Ipc != NULL && p->PPPStatus == PPP_STATUS_AUTHENTICATING)
{
// The handshake terminated and we received the final ACK, the auth is successful
// Just send an EAP-Success
PPP_PACKET* pack;
UINT identificator = p->Eap_PacketId;
PPPSetStatus(p, PPP_STATUS_AUTH_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) == false)
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return true;
}
else if (p->Eap_TlsCtx.ClientCert.X == NULL)
{
// Some clients needs a little help it seems - namely VPN Client Pro on Android
@ -3523,7 +3543,7 @@ bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapSize)
if (p->Eap_TlsCtx.SslPipe == NULL)
{
p->Eap_TlsCtx.Dh = DhNewFromBits(DH_PARAM_BITS_DEFAULT);
p->Eap_TlsCtx.SslPipe = NewSslPipeEx3(true, p->Cedar->ServerX, p->Cedar->ServerK, p->Cedar->ServerChain, p->Eap_TlsCtx.Dh, true, &(p->Eap_TlsCtx.ClientCert), p->Eap_TlsCtx.DisableTls13SessionTickets, p->Eap_TlsCtx.DisableTls13);
p->Eap_TlsCtx.SslPipe = NewSslPipeEx3(true, p->Cedar->ServerX, p->Cedar->ServerK, p->Cedar->ServerChain, p->Eap_TlsCtx.Dh, true, &(p->Eap_TlsCtx.ClientCert), p->Eap_TlsCtx.Tls13SessionTicketsCount, p->Eap_TlsCtx.DisableTls13);
}
// If the current frame is fragmented, or it is a possible last of a fragmented series, bufferize it
@ -3593,20 +3613,32 @@ bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapSize)
p->Eap_TlsCtx.CachedBufferRecvPntr = NULL;
}
// Very special case - we attempt to restart without TLS 1.3
if (!syncOk && (p->Eap_TlsCtx.DisableTls13 == false || p->Eap_TlsCtx.DisableTls13SessionTickets == false))
// Special case - we attempt to restart downgrading TLS settings
if (!syncOk && (p->Eap_TlsCtx.DisableTls13 == false || p->Eap_TlsCtx.Tls13SessionTicketsCount == 0))
{
// If we authenticated earlier, deauthenticate back
p->DataTimeout = PPP_DATA_TIMEOUT;
p->PacketRecvTimeout = PPP_PACKET_RECV_TIMEOUT;
p->UserConnectionTimeout = 0;
p->UserConnectionTick = 0;
if (p->Ipc != NULL)
{
FreeIPC(p->Ipc);
p->Ipc = NULL;
p->AuthOk = false;
}
FreeSslPipe(p->Eap_TlsCtx.SslPipe);
DhFree(p->Eap_TlsCtx.Dh);
p->Eap_TlsCtx.SslPipe = NULL;
p->Eap_TlsCtx.Dh = NULL;
if (p->Eap_TlsCtx.DisableTls13SessionTickets == true)
if (p->Eap_TlsCtx.Tls13SessionTicketsCount == 0)
{
p->Eap_TlsCtx.DisableTls13 = true;
}
else
{
p->Eap_TlsCtx.DisableTls13SessionTickets = true;
p->Eap_TlsCtx.Tls13SessionTicketsCount = 0;
}
flags |= PPP_EAP_TLS_FLAG_SSLSTARTED;
p->Eap_PacketId = p->NextId++;
@ -3617,11 +3649,116 @@ bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapSize)
WHERE;
return false;
}
Debug("EAP-TLS: Restarting the handshake! DisableTls13SessionTickets = %d, DisableTls13 = %d\n", p->Eap_TlsCtx.DisableTls13SessionTickets, p->Eap_TlsCtx.DisableTls13);
Debug("EAP-TLS: Restarting the handshake! Tls13SessionTicketsCount = %d, DisableTls13 = %d\n", p->Eap_TlsCtx.Tls13SessionTicketsCount, p->Eap_TlsCtx.DisableTls13);
Debug("Sent EAP-TLS size=%i\n", lcp->DataSize);
return false;
}
// If on the server we have enough data to authenticate, let's do this before we continue with the handshake
// Check if we received the client certificate and the handshake is finished
if (p->Eap_TlsCtx.ClientCert.X != NULL && p->Ipc == NULL)
{
IPC* ipc;
UINT error_code;
if (p->Eap_MatchUserByCert)
{
HUB* hub = GetHub(p->Cedar, p->Eap_Identity.HubName);
bool found = false;
if (hub != NULL)
{
USER* user = AcGetUserByCert(hub, p->Eap_TlsCtx.ClientCert.X);
if (user != NULL)
{
StrCpy(p->Eap_Identity.UserName, sizeof(p->Eap_Identity.UserName), user->Name);
found = true;
ReleaseUser(user);
}
ReleaseHub(hub);
}
if (found == false)
{
PPP_PACKET* pack;
UINT identificator = p->Eap_PacketId;
ReleaseHub(hub);
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) == false)
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return false;
}
}
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, p->Eap_Identity.HubName, p->Eap_Identity.UserName, "", NULL,
&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);
// We use the SAM authentication here, because the handshake can still fail at this point
if (ipc != NULL)
{
// Setting user timeouts
p->Ipc = ipc;
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;
if (p->TubeRecv != NULL)
{
p->TubeRecv->DataTimeout = p->DataTimeout;
}
p->UserConnectionTimeout = (UINT64)p->Ipc->Policy->AutoDisconnect * 1000;
p->UserConnectionTick = Tick64();
p->AuthOk = true;
if (p->Eap_TlsCtx.SslPipe->SslVersion == TLS1_3_VERSION)
{
// Before starting IPC and sending an EAP-Success in case of TLS 1.3 we need to send a 0x00 data packet as per RFC 9190
char zeroPacket[1] = { 0 };
WriteFifo(p->Eap_TlsCtx.SslPipe->SslInOut->SendFifo, zeroPacket, sizeof(zeroPacket));
if (!SyncSslPipe(p->Eap_TlsCtx.SslPipe))
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
}
}
else
{
PPP_PACKET* pack;
UINT identificator = p->Eap_PacketId;
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) == false)
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return false;
}
}
// We continue the TLS handshake
if (p->Eap_TlsCtx.SslPipe->IsDisconnected == false)
{
@ -3673,110 +3810,7 @@ bool PPPProcessEAPTlsResponse(PPP_SESSION *p, PPP_EAP *eap_packet, UINT eapSize)
}
}
// Check if we received the client certificate and the handshake is finished
// If yes, we ignore everything else
if (p->Eap_TlsCtx.ClientCert.X != NULL)
{
IPC* ipc;
UINT error_code;
/*PPP_PACKET* pack;
// Send an ACK that we received the last packet
if (!p->Eap_TlsCtx.SslPipe->IsDisconnected)
{
p->Eap_PacketId = p->NextId++;
lcp = BuildEAPTlsRequest(p->Eap_PacketId, 0, 0);
eap = lcp->Data;
pack = ZeroMalloc(sizeof(PPP_PACKET));
pack->IsControl = true;
pack->Protocol = PPP_PROTOCOL_EAP;
pack->Lcp = lcp;
Debug("Sent EAP-TLS size=%i type=%i flag=%i\n", lcp->DataSize, eap->Type, eap->Tls.Flags);
if (PPPSendPacketAndFree(p, pack) == false)
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
}*/
/*if (!p->Eap_TlsCtx.SslPipe->IsDisconnected)
{
dataSize = FifoSize(p->Eap_TlsCtx.SslPipe->RawOut->RecvFifo);
p->Eap_PacketId = p->NextId++;
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;
}*/
ipc = NewIPC(p->Cedar, p->ClientSoftwareName, p->Postfix, p->Eap_Identity.HubName, p->Eap_Identity.UserName, "", NULL,
&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;
p->Ipc = ipc;
PPPSetStatus(p, PPP_STATUS_AUTH_SUCCESS);
// 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;
if (p->TubeRecv != NULL)
{
p->TubeRecv->DataTimeout = p->DataTimeout;
}
p->UserConnectionTimeout = (UINT64)p->Ipc->Policy->AutoDisconnect * 1000;
p->UserConnectionTick = Tick64();
// 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) == false)
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return true;
}
else
{
PPP_PACKET* pack;
UINT identificator = p->Eap_PacketId;
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) == false)
{
PPPSetStatus(p, PPP_STATUS_FAIL);
WHERE;
return false;
}
return false;
}
}
// If we end up here, we got problems, send an EAP failure
if (p->Eap_TlsCtx.SslPipe->IsDisconnected)

View File

@ -231,7 +231,7 @@ struct PPP_EAP_TLS_CONTEXT
UCHAR *CachedBufferSend;
UCHAR *CachedBufferSendPntr;
bool DisableTls13;
bool DisableTls13SessionTickets;
int Tls13SessionTicketsCount;
};
// PPP request resend
@ -306,6 +306,7 @@ struct PPP_SESSION
UINT Eap_Protocol; // Current EAP Protocol used
UINT Eap_PacketId; // EAP Packet ID;
ETHERIP_ID Eap_Identity; // Received from client identity
bool Eap_MatchUserByCert; // Attempt to match the user from it's certificate during EAP-TLS, ignoring the EAP-identification
PPP_EAP_TLS_CONTEXT Eap_TlsCtx; // Context information for EAP TLS. May be possibly reused for EAP TTLS?
LIST *SentReqPacketList; // Sent requests list