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

Merge pull request #1522 from domosekai/tls

Implement complete server certificate verification
This commit is contained in:
Yihong Wu
2022-05-12 23:38:38 +08:00
committed by GitHub
34 changed files with 1212 additions and 477 deletions

View File

@ -1573,6 +1573,12 @@ bool ServerAccept(CONNECTION *c)
c->CipherName = NULL;
if (c->SslVersion != NULL)
{
Free(c->SslVersion);
}
c->SslVersion = NULL;
if (IsEmptyStr(tmp) == false)
{
c->CipherName = CopyStr(tmp);
@ -1592,11 +1598,22 @@ bool ServerAccept(CONNECTION *c)
}
c->CipherName = NULL;
if (c->SslVersion != NULL)
{
Free(c->SslVersion);
}
c->SslVersion = NULL;
if (c->FirstSock != NULL && IsEmptyStr(c->FirstSock->CipherName) == false)
{
c->CipherName = CopyStr(c->FirstSock->CipherName);
}
if (c->FirstSock != NULL && IsEmptyStr(c->FirstSock->SslVersion) == false)
{
c->SslVersion = CopyStr(c->FirstSock->SslVersion);
}
Format(radius_login_opt.In_VpnProtocolState, sizeof(radius_login_opt.In_VpnProtocolState),
"L%u:%s", IPC_LAYER_2, "SEVPN");
}
@ -4297,7 +4314,6 @@ bool ClientCheckServerCert(CONNECTION *c, bool *expired)
X *x;
CHECK_CERT_THREAD_PROC *p;
THREAD *thread;
CEDAR *cedar;
bool ret;
UINT64 start;
// Validate arguments
@ -4312,31 +4328,10 @@ bool ClientCheckServerCert(CONNECTION *c, bool *expired)
}
auth = c->Session->ClientAuth;
cedar = c->Cedar;
if (auth->CheckCertProc == NULL && c->Session->LinkModeClient == false)
if (auth->CheckCertProc == NULL)
{
// No checking function
return true;
}
if (c->Session->LinkModeClient && c->Session->Link->CheckServerCert == false)
{
// It's in cascade connection mode, but do not check the server certificate
return true;
}
if (c->UseTicket)
{
// Check the certificate of the redirected VPN server
if (CompareX(c->FirstSock->RemoteX, c->ServerX) == false)
{
return false;
}
else
{
return true;
}
return false;
}
x = CloneX(c->FirstSock->RemoteX);
@ -4346,63 +4341,6 @@ bool ClientCheckServerCert(CONNECTION *c, bool *expired)
return false;
}
if (CheckXDateNow(x))
{
// Check whether it is signed by the root certificate to trust
if (c->Session->LinkModeClient == false)
{
// Normal VPN Client mode
if (CheckSignatureByCa(cedar, x))
{
// This certificate can be trusted because it is signed
FreeX(x);
return true;
}
}
else
{
// Cascade connection mode
if (CheckSignatureByCaLinkMode(c->Session, x))
{
// This certificate can be trusted because it is signed
FreeX(x);
return true;
}
}
}
if (c->Session->LinkModeClient)
{
if (CheckXDateNow(x))
{
Lock(c->Session->Link->lock);
{
if (c->Session->Link->ServerCert != NULL)
{
if (CompareX(c->Session->Link->ServerCert, x))
{
Unlock(c->Session->Link->lock);
// Exactly match the certificate that is registered in the cascade configuration
FreeX(x);
return true;
}
}
}
Unlock(c->Session->Link->lock);
}
else
{
if (expired != NULL)
{
*expired = true;
}
}
// Verification failure at this point in the case of cascade connection mode
FreeX(x);
return false;
}
p = ZeroMalloc(sizeof(CHECK_CERT_THREAD_PROC));
p->ServerX = x;
p->CheckCertProc = auth->CheckCertProc;
@ -4420,7 +4358,8 @@ bool ClientCheckServerCert(CONNECTION *c, bool *expired)
{
// Send a NOOP periodically for disconnection prevention
start = Tick64();
ClientUploadNoop(c);
// Do not send because we now ask for user permission before sending signature
//ClientUploadNoop(c);
}
if (p->UserSelected)
{
@ -4479,10 +4418,43 @@ REDIRECTED:
s = ClientConnectToServer(c);
if (s == NULL)
{
// Do not retry if untrusted or hostname mismatched
if (c->Session->LinkModeClient == false && (c->Err == ERR_CERT_NOT_TRUSTED || c->Err == ERR_HOSTNAME_MISMATCH)
&& (c->Session->Account == NULL || ! c->Session->Account->RetryOnServerCert))
{
c->Session->ForceStopFlag = true;
}
PrintStatus(sess, L"free");
return false;
}
PrintStatus(sess, _UU("STATUS_5"));
// Prompt user whether to continue on verification errors
if ((c->Err == ERR_CERT_NOT_TRUSTED || c->Err == ERR_HOSTNAME_MISMATCH || c->Err == ERR_SERVER_CERT_EXPIRES) && ClientCheckServerCert(c, &expired) == false)
{
if (expired)
{
c->Err = ERR_SERVER_CERT_EXPIRES;
}
// Do not retry if untrusted or hostname mismatched
if (c->Session->LinkModeClient == false && (c->Err == ERR_CERT_NOT_TRUSTED || c->Err == ERR_HOSTNAME_MISMATCH)
&& (c->Session->Account == NULL || ! c->Session->Account->RetryOnServerCert))
{
c->Session->ForceStopFlag = true;
}
goto CLEANUP;
}
// Check the certificate of the redirected VPN server
if (c->UseTicket && CompareX(s->RemoteX, c->ServerX) == false)
{
c->Err = ERR_CERT_NOT_TRUSTED;
goto CLEANUP;
}
Copy(&server_ip, &s->RemoteIP, sizeof(IP));
if (c->Halt)
@ -4534,8 +4506,6 @@ REDIRECTED:
goto CLEANUP;
}
PrintStatus(sess, _UU("STATUS_5"));
// Receive a Hello packet
Debug("Downloading Hello...\n");
if (ClientDownloadHello(c, s) == false)
@ -4571,27 +4541,6 @@ REDIRECTED:
// During user authentication
c->Session->ClientStatus = CLIENT_STATUS_AUTH;
// Verify the server certificate by the client
if (ClientCheckServerCert(c, &expired) == false)
{
if (expired == false)
{
c->Err = ERR_CERT_NOT_TRUSTED;
}
else
{
c->Err = ERR_SERVER_CERT_EXPIRES;
}
if (c->Session->LinkModeClient == false && c->Err == ERR_CERT_NOT_TRUSTED
&& (c->Session->Account == NULL || ! c->Session->Account->RetryOnServerCert))
{
c->Session->ForceStopFlag = true;
}
goto CLEANUP;
}
PrintStatus(sess, _UU("STATUS_6"));
// Send the authentication data
@ -5045,6 +4994,13 @@ REDIRECTED:
}
c->CipherName = CopyStr(c->FirstSock->CipherName);
if (c->SslVersion != NULL)
{
Free(c->SslVersion);
}
c->SslVersion = CopyStr(c->FirstSock->SslVersion);
}
Unlock(c->lock);
@ -6220,16 +6176,29 @@ SOCK *ClientConnectToServer(CONNECTION *c)
SetTimeout(s, CONNECTING_TIMEOUT);
// Start the SSL communication
if (StartSSLEx(s, x, k, 0, c->ServerName) == false)
UINT err = 0;
if (StartSSLEx3(s, x, k, NULL, 0, c->ServerName, c->Session->SslOption, &err) == false)
{
// SSL communication start failure
Disconnect(s);
ReleaseSock(s);
c->FirstSock = NULL;
c->Err = ERR_SERVER_IS_NOT_VPN;
if (err != 0)
{
c->Err = err;
}
else
{
c->Err = ERR_SERVER_IS_NOT_VPN;
}
return NULL;
}
if (err != 0)
{
c->Err = err;
}
if (s->RemoteX == NULL)
{
// SSL communication start failure
@ -6240,6 +6209,8 @@ SOCK *ClientConnectToServer(CONNECTION *c)
return NULL;
}
CLog(c->Cedar->Client, "LC_SSL_CONNECTED", c->Session->ClientOption->AccountName, s->SslVersion, s->CipherName);
return s;
}
@ -6299,6 +6270,7 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
if (o->ProxyType == PROXY_DIRECT)
{
UINT ssl_err = 0;
UINT nat_t_err = 0;
wchar_t tmp[MAX_SIZE];
UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), hostname);
@ -6308,9 +6280,10 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
{
// If additional_connect == false, enable trying to NAT-T connection
// If additional_connect == true, follow the IsRUDPSession setting in this session
sock = TcpIpConnectEx(hostname, c->ServerPort,
// In additional connect or redirect we do not need ssl verification as the certificate is always compared with a saved one
sock = TcpIpConnectEx2(hostname, c->ServerPort,
(bool *)cancel_flag, c->hWndForUI, &nat_t_err, (additional_connect ? (!sess->IsRUDPSession) : false),
true, &resolved_ip);
true, ((additional_connect || c->UseTicket) ? NULL : sess->SslOption), &ssl_err, o->HintStr, &resolved_ip);
}
else
{
@ -6333,7 +6306,14 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
// Connection failure
if (nat_t_err != RUDP_ERROR_NAT_T_TWO_OR_MORE)
{
c->Err = ERR_CONNECT_FAILED;
if (ssl_err != 0)
{
c->Err = ssl_err;
}
else
{
c->Err = ERR_CONNECT_FAILED;
}
}
else
{
@ -6342,6 +6322,11 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
return NULL;
}
if (ssl_err != 0)
{
c->Err = ssl_err;
}
}
else
{
@ -6446,23 +6431,31 @@ UINT ProxyCodeToCedar(UINT code)
// TCP connection function
SOCK *TcpConnectEx3(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd, bool no_nat_t, UINT *nat_t_error_code, bool try_start_ssl, IP *ret_ip)
{
return TcpConnectEx4(hostname, port, timeout, cancel_flag, hWnd, no_nat_t, nat_t_error_code, try_start_ssl, NULL, NULL, NULL, ret_ip);
}
SOCK *TcpConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd, bool no_nat_t, UINT *nat_t_error_code, bool try_start_ssl, SSL_VERIFY_OPTION *ssl_option, UINT *ssl_err, char *hint_str, IP *ret_ip)
{
#ifdef OS_WIN32
if (hWnd == NULL)
{
#endif // OS_WIN32
return ConnectEx4(hostname, port, timeout, cancel_flag, (no_nat_t ? NULL : VPN_RUDP_SVC_NAME), nat_t_error_code, try_start_ssl, true, ret_ip);
return ConnectEx5(hostname, port, timeout, cancel_flag, (no_nat_t ? NULL : VPN_RUDP_SVC_NAME), nat_t_error_code, try_start_ssl, true, ssl_option, ssl_err, hint_str, ret_ip);
#ifdef OS_WIN32
}
else
{
return WinConnectEx3((HWND)hWnd, hostname, port, timeout, 0, NULL, NULL, nat_t_error_code, (no_nat_t ? NULL : VPN_RUDP_SVC_NAME), try_start_ssl);
return WinConnectEx4((HWND)hWnd, hostname, port, timeout, 0, NULL, NULL, nat_t_error_code, (no_nat_t ? NULL : VPN_RUDP_SVC_NAME), try_start_ssl, ssl_option, ssl_err, hint_str);
}
#endif // OS_WIN32
}
// Connect with TCP/IP
SOCK *TcpIpConnectEx(char *hostname, UINT port, bool *cancel_flag, void *hWnd, UINT *nat_t_error_code, bool no_nat_t, bool try_start_ssl, IP *ret_ip)
{
return TcpIpConnectEx2(hostname, port, cancel_flag, hWnd, nat_t_error_code, no_nat_t, try_start_ssl, NULL, NULL, NULL, ret_ip);
}
SOCK *TcpIpConnectEx2(char *hostname, UINT port, bool *cancel_flag, void *hWnd, UINT *nat_t_error_code, bool no_nat_t, bool try_start_ssl, SSL_VERIFY_OPTION *ssl_option, UINT *ssl_err, char *hint_str, IP *ret_ip)
{
SOCK *s = NULL;
UINT dummy_int = 0;
@ -6477,7 +6470,7 @@ SOCK *TcpIpConnectEx(char *hostname, UINT port, bool *cancel_flag, void *hWnd, U
return NULL;
}
s = TcpConnectEx3(hostname, port, 0, cancel_flag, hWnd, no_nat_t, nat_t_error_code, try_start_ssl, ret_ip);
s = TcpConnectEx4(hostname, port, 0, cancel_flag, hWnd, no_nat_t, nat_t_error_code, try_start_ssl, ssl_option, ssl_err, hint_str, ret_ip);
if (s == NULL)
{
return NULL;