1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2025-07-06 07:44:57 +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

@ -382,6 +382,7 @@ typedef struct RUDP_SESSION RUDP_SESSION;
typedef struct RUDP_SEGMENT RUDP_SEGMENT;
typedef struct CONNECT_SERIAL_PARAM CONNECT_SERIAL_PARAM;
typedef struct CONNECT_TCP_RUDP_PARAM CONNECT_TCP_RUDP_PARAM;
typedef struct SSL_VERIFY_OPTION SSL_VERIFY_OPTION;
typedef struct TCP_PAIR_HEADER TCP_PAIR_HEADER;
typedef struct NIC_ENTRY NIC_ENTRY;
typedef struct HTTP_VALUE HTTP_VALUE;

View File

@ -54,7 +54,7 @@
#ifdef OS_WIN32
#include <iphlpapi.h>
#include <WS2tcpip.h>
#include <wincrypt.h>
#include <IcmpAPI.h>
struct ROUTE_CHANGE_DATA
@ -11632,11 +11632,17 @@ bool StartSSLEx(SOCK *sock, X *x, K *priv, UINT ssl_timeout, char *sni_hostname)
return StartSSLEx2(sock, x, priv, NULL, ssl_timeout, sni_hostname);
}
bool StartSSLEx2(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char *sni_hostname)
{
return StartSSLEx3(sock, x, priv, chain, ssl_timeout, sni_hostname, NULL, NULL);
}
bool StartSSLEx3(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char *sni_hostname, SSL_VERIFY_OPTION *ssl_option, UINT *ssl_err)
{
X509 *x509;
EVP_PKEY *key;
UINT prev_timeout = 1024;
SSL_CTX *ssl_ctx;
UINT dummy_err = 0;
long ssl_verify_err;
#ifdef UNIX_SOLARIS
SOCKET_TIMEOUT_PARAM *ttparam;
@ -11648,6 +11654,10 @@ bool StartSSLEx2(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char
Debug("StartSSL Error: #0\n");
return false;
}
if (ssl_err == NULL)
{
ssl_err = &dummy_err;
}
if (sock->Connected && sock->Type == SOCK_INPROC && sock->ListenMode == false)
{
sock->SecureMode = true;
@ -11740,6 +11750,55 @@ bool StartSSLEx2(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char
}
Lock(openssl_lock);
}
else
{
// Client mode
if (ssl_option != NULL && ssl_option->VerifyPeer)
{
// Add default trust store
X509_STORE* store = SSL_CTX_get_cert_store(ssl_ctx);
if (ssl_option->AddDefaultCA)
{
#ifdef OS_WIN32
HCERTSTORE hStore = CertOpenSystemStore(0, "ROOT");
if (hStore != NULL)
{
PCCERT_CONTEXT pContext = NULL;
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)))
{
X509 *x509 = d2i_X509(NULL, (const unsigned char**)&pContext->pbCertEncoded, pContext->cbCertEncoded);
if (x509 != NULL)
{
X509_STORE_add_cert(store, x509);
X509_free(x509);
}
}
CertCloseStore(hStore, 0);
}
#else
SSL_CTX_set_default_verify_paths(ssl_ctx);
#endif
}
// Add trust CA specified by user
UINT i;
for (i = 0; i < LIST_NUM(ssl_option->CaList); ++i)
{
X *ca = LIST_DATA(ssl_option->CaList, i);
X509_STORE_add_cert(store, ca->x509);
}
// Allow intermediate CA to be trusted
X509_VERIFY_PARAM *vpm = SSL_CTX_get0_param(ssl_ctx);
X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_PARTIAL_CHAIN);
// Enable hostname verification (by default CN is only checked if SAN is not available)
if (ssl_option->VerifyHostname && IsEmptyStr(sni_hostname) == false)
{
X509_VERIFY_PARAM_set1_host(vpm, sni_hostname, StrLen(sni_hostname));
}
}
}
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
if (sock->SslAcceptSettings.Override_Security_Level)
@ -11880,7 +11939,7 @@ bool StartSSLEx2(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char
Lock(openssl_lock);
{
x509 = SSL_get_peer_certificate(sock->ssl);
ssl_verify_err = SSL_get_verify_result(sock->ssl);
sock->SslVersion = SSL_get_version(sock->ssl);
}
Unlock(openssl_lock);
@ -11896,6 +11955,49 @@ bool StartSSLEx2(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char
sock->RemoteX = X509ToX(x509);
}
// Check verification error
if (ssl_option != NULL && ssl_option->VerifyPeer)
{
if (ssl_verify_err != X509_V_OK)
{
// Clear any error if matching saved certificate and not expired
if (ssl_option->SavedCert != NULL && sock->RemoteX != NULL && CheckXDateNow(sock->RemoteX) && CompareX(ssl_option->SavedCert, sock->RemoteX))
{
ssl_verify_err = X509_V_OK;
}
else
{
Debug("StartSSL: SSL verification error %d\n", ssl_verify_err);
switch (ssl_verify_err)
{
case X509_V_ERR_CERT_HAS_EXPIRED:
*ssl_err = 106; // ERR_SERVER_CERT_EXPIRES
break;
case X509_V_ERR_HOSTNAME_MISMATCH:
*ssl_err = 149; // ERR_HOSTNAME_MISMATCH
break;
default:
*ssl_err = 85; // ERR_CERT_NOT_TRUSTED
}
if (ssl_option->PromptOnVerifyFail == false)
{
// SSL verify failure
Lock(openssl_lock);
{
SSL_free(sock->ssl);
sock->ssl = NULL;
}
Unlock(openssl_lock);
Unlock(sock->ssl_lock);
FreeSSLCtx(ssl_ctx);
return false;
}
}
}
}
// Get the certificate of local host
Lock(openssl_lock);
{
@ -13778,20 +13880,7 @@ void ConnectThreadForTcp(THREAD *thread, void *param)
Unlock(p->CancelLock);
// Start the SSL communication
ssl_ret = StartSSLEx(sock, NULL, NULL, 0, p->Hostname);
if (ssl_ret)
{
// Identify whether the HTTPS server to be connected is a SoftEther VPN
SetTimeout(sock, (10 * 1000));
ssl_ret = DetectIsServerSoftEtherVPN(sock);
SetTimeout(sock, INFINITE);
if (ssl_ret == false)
{
Debug("DetectIsServerSoftEtherVPN Error.\n");
}
}
ssl_ret = StartSSLEx3(sock, NULL, NULL, NULL, 0, p->Hostname, p->SslOption, p->SslErr);
Lock(p->CancelLock);
{
@ -13986,6 +14075,8 @@ void ConnectThreadForIPv4(THREAD *thread, void *param)
p1.CancelFlag = &cancel_flag2;
p1.FinishEvent = finish_event;
p1.Tcp_TryStartSsl = p->Tcp_TryStartSsl;
p1.SslOption = p->SslOption;
p1.SslErr = p->SslErr;
p1.CancelLock = NewLock();
// p2: NAT-T
@ -14411,9 +14502,9 @@ SOCK *ConnectEx3(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
}
SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname, IP *ret_ip)
{
return ConnectEx5(hostname, port, timeout, cancel_flag, nat_t_svc_name, nat_t_error_code, try_start_ssl, no_get_hostname, NULL, ret_ip);
return ConnectEx5(hostname, port, timeout, cancel_flag, nat_t_svc_name, nat_t_error_code, try_start_ssl, no_get_hostname, NULL, NULL, NULL, ret_ip);
}
SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname, char *hint_str, IP *ret_ip)
SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname, SSL_VERIFY_OPTION *ssl_option, UINT *ssl_err, char *hint_str, IP *ret_ip)
{
bool dummy = false;
bool use_natt = false;
@ -14496,9 +14587,9 @@ SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
EVENT *finish_event;
THREAD *t4 = NULL;
THREAD *t6 = NULL;
UINT64 start_tick = Tick64();
bool cancel_flag2 = false;
bool no_delay_flag = false;
IP ret_ip4, ret_ip6;
finish_event = NewEvent();
@ -14517,7 +14608,9 @@ SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
p6.NoDelayFlag = &no_delay_flag;
p6.FinishEvent = finish_event;
p6.Tcp_TryStartSsl = try_start_ssl;
p6.Ret_Ip = ret_ip;
p6.SslOption = ssl_option;
p6.SslErr = ssl_err;
p6.Ret_Ip = &ret_ip6;
p6.RetryDelay = 250;
p6.Delay = 0;
t6 = NewThread(ConnectThreadForIPv6, &p6);
@ -14538,9 +14631,11 @@ SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
StrCpy(p4.NatT_SvcName, sizeof(p4.NatT_SvcName), nat_t_svc_name);
p4.FinishEvent = finish_event;
p4.Tcp_TryStartSsl = try_start_ssl;
p4.SslOption = ssl_option;
p4.SslErr = ssl_err;
p4.Use_NatT = use_natt;
p4.Force_NatT = force_use_natt;
p4.Ret_Ip = ret_ip;
p4.Ret_Ip = &ret_ip4;
p4.RetryDelay = 250;
p4.Delay = 250; // Delay by 250ms to prioritize IPv6 (RFC 6555 recommends 150-250ms, Chrome uses 300ms)
t4 = NewThread(ConnectThreadForIPv4, &p4);
@ -14604,7 +14699,7 @@ SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
{
Disconnect(p4.Sock);
ReleaseSock(p4.Sock);
Copy(ret_ip, &ret_ip6, sizeof(IP));
return p6.Sock;
}
@ -14612,7 +14707,7 @@ SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
{
Disconnect(p6.Sock);
ReleaseSock(p6.Sock);
Copy(ret_ip, &ret_ip4, sizeof(IP));
return p4.Sock;
}

View File

@ -816,6 +816,8 @@ struct CONNECT_SERIAL_PARAM
UINT Delay;
UINT RetryDelay;
bool Tcp_TryStartSsl;
SSL_VERIFY_OPTION *SslOption;
UINT *SslErr;
bool Use_NatT;
bool Force_NatT;
IP *Ret_Ip;
@ -842,11 +844,23 @@ struct CONNECT_TCP_RUDP_PARAM
UINT RUdpProtocol;
UINT Delay;
bool Tcp_TryStartSsl;
SSL_VERIFY_OPTION *SslOption;
UINT *SslErr;
LOCK *CancelLock;
SOCK *CancelDisconnectSock;
bool Tcp_InNegotiation;
};
struct SSL_VERIFY_OPTION
{
bool VerifyPeer; // Whether to verify SSL peer
bool PromptOnVerifyFail; // Prompt on verification failure (Windows)
bool AddDefaultCA; // Use default trust store
bool VerifyHostname; // Verify server hostname
LIST *CaList; // Trusted CA list
X *SavedCert; // Saved server certificate
};
#define SSL_DEFAULT_CONNECT_TIMEOUT (15 * 1000) // SSL default timeout
// Header for TCP Pair
@ -1084,7 +1098,7 @@ SOCK *ConnectEx(char *hostname, UINT port, UINT timeout);
SOCK *ConnectEx2(char *hostname, UINT port, UINT timeout, bool *cancel_flag);
SOCK *ConnectEx3(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname);
SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname, IP *ret_ip);
SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname, char *hint_str, IP *ret_ip);
SOCK *ConnectEx5(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname, SSL_VERIFY_OPTION *ssl_option, UINT *ssl_err, char *hint_str, IP *ret_ip);
SOCKET ConnectTimeoutIPv4(IP *ip, UINT port, UINT timeout, bool *cancel_flag);
bool SetSocketBufferSize(SOCKET s, bool send, UINT size);
UINT SetSocketBufferSizeWithBestEffort(SOCKET s, bool send, UINT size);
@ -1109,6 +1123,7 @@ UINT SecureRecv(SOCK *sock, void *data, UINT size);
bool StartSSL(SOCK *sock, X *x, K *priv);
bool StartSSLEx(SOCK *sock, X *x, K *priv, UINT ssl_timeout, char *sni_hostname);
bool StartSSLEx2(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char *sni_hostname);
bool StartSSLEx3(SOCK *sock, X *x, K *priv, LIST *chain, UINT ssl_timeout, char *sni_hostname, SSL_VERIFY_OPTION *ssl_option, UINT *ssl_err);
bool AddChainSslCert(struct ssl_ctx_st *ctx, X *x);
void AddChainSslCertOnDirectory(struct ssl_ctx_st *ctx);
bool SendAll(SOCK *sock, void *data, UINT size, bool secure);