1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00

Update sslsocket.cpp

This commit is contained in:
Victor Stewart 2020-03-28 18:11:00 -04:00 committed by GitHub
parent 065f89b60c
commit 7261f686c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -42,6 +42,52 @@ bool USSLSocket::ocsp_nonce;
USSLSocket::stapling USSLSocket::staple;
#endif
// OPENSSL_VERSION_NUMBER is a numeric release version identifier:
// MNNFFPPS: major minor fix patch status
// 0x000906000 == 0.9.6 dev
// 0x000906023 == 0.9.6b beta 3
// 0x00090605f == 0.9.6e release
// 0x10101000L == 1.1.1
// 0x10100000L == 1.1.0
// 0x10002000L == 1.0.2
static char* ossl_err_as_string (void)
{
BIO *bio = BIO_new (BIO_s_mem ());
ERR_print_errors (bio);
char *buf = NULL;
size_t len = BIO_get_mem_data (bio, &buf);
char *ret = (char *) calloc (1, 1 + len);
if (ret)
memcpy (ret, buf, len);
BIO_free (bio);
return ret;
}
// static void print_certificate(X509* cert)
// {
// #define MAX_LENGTH 1024
// char subj[MAX_LENGTH+1];
// char issuer[MAX_LENGTH+1];
// X509_NAME_oneline(X509_get_subject_name(cert), subj, MAX_LENGTH);
// X509_NAME_oneline(X509_get_issuer_name(cert), issuer, MAX_LENGTH);
// printf("certificate: %s\n", subj);
// printf("\tissuer: %s\n\n", issuer);
// }
// static void print_stack(STACK_OF(X509)* sk)
// {
// unsigned len = sk_X509_num(sk);
// printf("Begin Certificate Stack:\n");
// for(unsigned i=0; i<len; i++) {
// X509 *cert = sk_X509_value(sk, i);
// print_certificate(cert);
// }
// printf("End Certificate Stack\n");
// }
/**
* The OpenSSL ssl library implements the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) protocols.
* It provides a rich API. At first the library must be initialized; see SSL_library_init(3). Then an SSL_CTX object is created
@ -120,6 +166,13 @@ void USSLSocket::info_callback(const SSL* ssl, int where, int ret)
{
U_TRACE(0, "USSLSocket::info_callback(%p,%d,%d)", ssl, where, ret)
#ifdef DEBUG
U_WARNING("USSLSocket::info_callback -> SSL_state_string_long = %s", SSL_state_string_long(ssl));
const char *error = ossl_err_as_string();
if (strlen(error)) U_WARNING("USSLSocket::info_callback -> SSL error = %s", error);
#endif
if ((where & SSL_CB_HANDSHAKE_START) != 0)
{
U_INTERNAL_DUMP("SSL_CB_HANDSHAKE_START")
@ -368,21 +421,40 @@ bool USSLSocket::useDHFile(const char* dh_file)
}
else
{
/**
* The concept of forward secrecy is simple: client and server negotiate a key that never hits the wire,
* and is destroyed at the end of the session. The RSA private from the server is used to sign a Diffie-Hellman
* key exchange between the client and the server. The pre-master key obtained from the Diffie-Hellman handshake
* is then used for encryption. Since the pre-master key is specific to a connection between a client and a server,
* and used only for a limited amount of time, it is called Ephemeral. With Forward Secrecy, if an attacker gets a
* hold of the server's private key, it will not be able to decrypt past communications. The private key is only
* used to sign the DH handshake, which does not reveal the pre-master key. Diffie-Hellman ensures that the pre-master
* keys never leave the client and the server, and cannot be intercepted by a MITM
*/
# if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH) && defined(NID_X9_62_prime256v1)
// The curve functions were added in OpenSSL 1.0.2. The equivalent group functions were added in OpenSSL 1.1.1.
# if OPENSSL_VERSION_NUMBER >= 0x10101000L
// In TLSv1.3 the client selects a “group” that it will use for key exchange. At the time of writing, OpenSSL only supports ECDHE groups for this. The client then sends “key_share” information to the server for its selected group in the ClientHello.
// The list of supported groups is configurable. It is possible for a client to select a group that the server does not support. In this case the server requests that the client sends a new key_share that it does support. While this means a connection will still be established (assuming a mutually supported group exists), it does introduce an extra server round trip - so this has implications for performance. In the ideal scenario the client will select a group that the server supports in the first instance.
// In practice most clients will use X25519 or P-256 for their initial key_share. For maximum performance it is recommended that servers are configured to support at least those two groups and clients use one of those two for its initial key_share. This is the default case (OpenSSL clients will use X25519).
// The group configuration also controls the allowed groups in TLSv1.2 and below.
// https://www.openssl.org/blog/blog/2018/02/08/tlsv1.3/
(void) U_SYSCALL(SSL_CTX_set1_groups_list, "%p,%s", ctx, "P-384:P-256:X25519:ffdhe2048:P-521");
U_RETURN(true);
# elif OPENSSL_VERSION_NUMBER >= 0x10002000L
(void) U_SYSCALL(SSL_CTX_set1_curves_list, "%p,%s", ctx, "P-384:P-256:X25519:ffdhe2048:P-521");
U_RETURN(true);
/**
* The concept of forward secrecy is simple: client and server negotiate a key that never hits the wire,
* and is destroyed at the end of the session. The RSA private from the server is used to sign a Diffie-Hellman
* key exchange between the client and the server. The pre-master key obtained from the Diffie-Hellman handshake
* is then used for encryption. Since the pre-master key is specific to a connection between a client and a server,
* and used only for a limited amount of time, it is called Ephemeral. With Forward Secrecy, if an attacker gets a
* hold of the server's private key, it will not be able to decrypt past communications. The private key is only
* used to sign the DH handshake, which does not reveal the pre-master key. Diffie-Hellman ensures that the pre-master
* keys never leave the client and the server, and cannot be intercepted by a MITM
*/
# elif OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH) && defined(NID_X9_62_prime256v1)
EC_KEY* ecdh = (EC_KEY*) U_SYSCALL(EC_KEY_new_by_curve_name, "%d", NID_X9_62_prime256v1);
(void) U_SYSCALL(SSL_CTX_set_tmp_ecdh, "%p,%p", ctx, ecdh);
U_SYSCALL_VOID(EC_KEY_free, "%p", ecdh);
U_RETURN(true);
@ -514,20 +586,16 @@ U_NO_EXPORT int USSLSocket::selectProto(SSL* ssl, const unsigned char** out, uns
#endif
bool USSLSocket::setContext(const char* dh_file, const char* cert_file, const char* private_key_file,
const char* passwd, const char* CAfile, const char* CApath, int verify_mode)
const char* passwd, const char* CAfile, const char* CApath, const char* tls_pin, int verify_mode)
{
U_TRACE(1, "USSLSocket::setContext(%S,%S,%S,%S,%S,%S,%d)", dh_file, cert_file, private_key_file, passwd, CAfile, CApath, verify_mode)
U_TRACE(1, "USSLSocket::setContext(%S,%S,%S,%S,%S,%S,%S,%d)", dh_file, cert_file, private_key_file, passwd, CAfile, CApath, tls_pin, verify_mode)
U_INTERNAL_ASSERT_POINTER(ctx)
// These are the bit DH parameters from "Assigned Number for SKIP Protocols"
// See there for how they were generated: http://www.skip-vpn.org/spec/numbers.html
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_CTX_set_ecdh_auto(ctx, 1);
#else
if (useDHFile(dh_file) == false) U_RETURN(false);
#endif
int result = 0;
@ -536,9 +604,13 @@ bool USSLSocket::setContext(const char* dh_file, const char* cert_file, const ch
if ( cert_file &&
*cert_file)
{
result = U_SYSCALL(SSL_CTX_use_certificate_chain_file, "%p,%S", ctx, cert_file);
result = U_SYSCALL(SSL_CTX_use_certificate_chain_file, "%p,%s", ctx, cert_file);
if (result == 0) U_RETURN(false);
if (result == 0)
{
U_WARNING("SSL_CTX_use_certificate_chain_file error = %s", ossl_err_as_string());
U_RETURN(false);
}
# if defined(ENABLE_THREAD) && !defined(OPENSSL_NO_OCSP) && defined(SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB)
UString str(cert_file, u__strlen(cert_file, __PRETTY_FUNCTION__));
@ -595,25 +667,32 @@ bool USSLSocket::setContext(const char* dh_file, const char* cert_file, const ch
# endif
}
if (CAfile && *CAfile == '\0') CAfile = U_NULLPTR;
if (CApath && *CApath == '\0') CApath = U_NULLPTR;
if (tls_pin)
{
U_SYSCALL_VOID(SSL_CTX_set_cert_verify_callback, "%p,%p,%p", ctx, USSLSocket::SPKIPinVerification, UServer_Base::tls_pin);
}
else
{
if (CAfile && *CAfile == '\0') CAfile = U_NULLPTR;
if (CApath && *CApath == '\0') CApath = U_NULLPTR;
if (CAfile ||
CApath)
if (CAfile ||
CApath)
{
if (UServices::setupOpenSSLStore(CAfile, CApath, (verify_mode ? U_STORE_FLAGS : 0)) == false) U_RETURN(false);
if (UServices::setupOpenSSLStore(CAfile, CApath, (verify_mode ? U_STORE_FLAGS : 0)) == false) U_RETURN(false);
U_SYSCALL_VOID(SSL_CTX_set_cert_store, "%p,%p", ctx, UServices::store);
U_SYSCALL_VOID(SSL_CTX_set_cert_store, "%p,%p", ctx, UServices::store);
// Sets the list of CA sent to the client when requesting a client certificate for ctx
// Sets the list of CA sent to the client when requesting a client certificate for ctx
if (CAfile) // Process CA certificate bundle file
if (CAfile) // Process CA certificate bundle file
{
STACK_OF(X509_NAME)* list = (STACK_OF(X509_NAME)*) U_SYSCALL(SSL_load_client_CA_file, "%S", CAfile);
STACK_OF(X509_NAME)* list = (STACK_OF(X509_NAME)*) U_SYSCALL(SSL_load_client_CA_file, "%S", CAfile);
U_SYSCALL_VOID(SSL_CTX_set_client_CA_list, "%p,%p", ctx, list);
U_SYSCALL_VOID(SSL_CTX_set_client_CA_list, "%p,%p", ctx, list);
}
}
}
setVerifyCallback(UServices::X509Callback, verify_mode);
@ -978,6 +1057,48 @@ loop:
U_RETURN(false);
}
int USSLSocket::SPKIPinVerification(X509_STORE_CTX* context, void *arg)
{
U_TRACE_NO_PARAM(0, "USSLSocket::SPKIPinVerification");
UString *tls_pin = (UString *)arg;
STACK_OF(X509) *certChain = X509_STORE_CTX_get0_untrusted(context);
X509 *workingCert = NULL;
bool result = false;
UString spkiPin(64U);
// we pin to root almost always, so start at bottom
for (ssize_t index = sk_X509_num(certChain) - 1; index > -1; index--)
{
workingCert = sk_X509_value(certChain, index);
#if DEBUG
const char *commonName = (const char *)ASN1_STRING_data(X509_NAME_ENTRY_get_data(X509_NAME_get_entry(X509_get_subject_name(workingCert), 5)));
U_WARNING("USSLSocket::SPKIPinVerification -> for cert at index %lu, commonName = %s", index, commonName);
#endif
unsigned char *pkey_buf = NULL;
int pkey_len = i2d_PUBKEY(X509_get_pubkey(workingCert), &pkey_buf);
unsigned int len;
// int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const EVP_MD *type, ENGINE *impl);
// A wrapper around the Digest Init_ex, Update and Final_ex functions. Hashes count bytes of data at data using a digest type from ENGINE impl. The digest value is placed in md and its length is written at size if the pointer is not NULL. At most EVP_MAX_MD_SIZE bytes will be written. If impl is NULL the default implementation of digest type is used.
EVP_Digest(pkey_buf, pkey_len, (unsigned char *)spkiPin.data(), &len, EVP_sha256(), NULL);
spkiPin.size_adjust(len);
result = spkiPin.equal(tls_pin->data(), tls_pin->size());
OPENSSL_free(pkey_buf);
if (result) return true;
}
return result;
}
// server side RE-NEGOTIATE asking for client cert
bool USSLSocket::askForClientCertificate()
@ -1089,36 +1210,28 @@ bool USSLSocket::acceptSSL(USSLSocket* pcNewConnection)
(void) U_SYSCALL(SSL_set_fd, "%p,%d", ssl, fd); // get SSL to use our socket
//SSL_check_chain(ssl, );
loop:
errno = 0;
ret = U_SYSCALL(SSL_accept, "%p", ssl); // get SSL handshake with client
if (ret == 1)
{
SSL_set_app_data(ssl, pcNewConnection);
pcNewConnection->ssl = ssl;
pcNewConnection->ret = SSL_ERROR_NONE;
pcNewConnection->iState = CONNECT;
pcNewConnection->renegotiations = 0;
ssl = U_NULLPTR;
U_RETURN(true);
}
U_INTERNAL_DUMP("errno = %d", errno)
if (errno) pcNewConnection->iState = -errno;
pcNewConnection->ret = U_SYSCALL(SSL_get_error, "%p,%d", ssl, ret);
#ifdef DEBUG
dumpStatus(pcNewConnection->ret, false);
#endif
U_INTERNAL_DUMP("count = %u", count)
if (count++ < 5)
{
if (pcNewConnection->ret == SSL_ERROR_WANT_READ) { if (UNotifier::waitForRead( fd, U_SSL_TIMEOUT_MS) > 0) goto loop; }
@ -1129,7 +1242,6 @@ loop:
U_SYSCALL_VOID(SSL_free, "%p", ssl);
ssl = U_NULLPTR;
pcNewConnection->USocket::_close_socket();
U_INTERNAL_DUMP("pcNewConnection->ret = %d", pcNewConnection->ret)