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

Implement complete server certificate verification

This commit is contained in:
Yihong Wu
2021-12-17 17:57:23 +08:00
parent 1c1560f6ca
commit f94ac6351e
30 changed files with 868 additions and 411 deletions

View File

@ -4295,7 +4295,6 @@ bool ClientCheckServerCert(CONNECTION *c, bool *expired)
X *x;
CHECK_CERT_THREAD_PROC *p;
THREAD *thread;
CEDAR *cedar;
bool ret;
UINT64 start;
// Validate arguments
@ -4310,31 +4309,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);
@ -4344,63 +4322,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;
@ -4418,7 +4339,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)
{
@ -4477,10 +4399,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)
@ -4532,8 +4487,6 @@ REDIRECTED:
goto CLEANUP;
}
PrintStatus(sess, _UU("STATUS_5"));
// Receive a Hello packet
Debug("Downloading Hello...\n");
if (ClientDownloadHello(c, s) == false)
@ -4569,27 +4522,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
@ -6218,16 +6150,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
@ -6297,6 +6242,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);
@ -6306,9 +6252,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
// 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, o->HintStr, &resolved_ip);
true, ((additional_connect || c->UseTicket) ? NULL : sess->SslOption), &ssl_err, o->HintStr, &resolved_ip);
}
else
{
@ -6331,7 +6278,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
{
@ -6340,6 +6294,11 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
return NULL;
}
if (ssl_err != 0)
{
c->Err = ssl_err;
}
}
else
{
@ -6445,20 +6404,20 @@ 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, 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, char *hint_str, IP *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 ConnectEx5(hostname, port, timeout, cancel_flag, (no_nat_t ? NULL : VPN_RUDP_SVC_NAME), nat_t_error_code, try_start_ssl, true, hint_str, 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 WinConnectEx4((HWND)hWnd, hostname, port, timeout, 0, NULL, NULL, nat_t_error_code, (no_nat_t ? NULL : VPN_RUDP_SVC_NAME), try_start_ssl, hint_str);
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
}
@ -6466,9 +6425,9 @@ SOCK *TcpConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag,
// 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, 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, char *hint_str, IP *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;
@ -6483,7 +6442,7 @@ SOCK *TcpIpConnectEx2(char *hostname, UINT port, bool *cancel_flag, void *hWnd,
return NULL;
}
s = TcpConnectEx4(hostname, port, 0, cancel_flag, hWnd, no_nat_t, nat_t_error_code, try_start_ssl, hint_str, 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;