TLS fingerprint

Beside SHA1 fingerprint hash, Connector/C now also supports
SHA224 (OpenSSL and GnuTLS only), SHA256, SHA384 and SHA512
fingerprint hashes.
This commit is contained in:
Georg Richter
2023-08-31 08:21:13 +02:00
committed by Sergei Golubchik
parent 395641549a
commit 9aa15e72a7
12 changed files with 191 additions and 65 deletions

View File

@ -382,7 +382,7 @@ CONFIGURE_FILE(${CC_SOURCE_DIR}/include/mariadb_version.h.in
INCLUDE_DIRECTORIES(${CC_BINARY_DIR}/include)
IF(WIN32)
SET(SYSTEM_LIBS ws2_32 advapi32 kernel32 shlwapi crypt32 ${LIBZ})
SET(SYSTEM_LIBS ws2_32 advapi32 kernel32 shlwapi crypt32 bcrypt ${LIBZ})
ELSE()
SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${LIBPTHREAD} ${CMAKE_DL_LIBS} ${LIBM})
IF(ICONV_EXTERNAL)

View File

@ -17,34 +17,20 @@
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*/
#ifndef _ma_hash_h_
#define _ma_hash_h_
#ifndef _ma_crypt_h_
#define _ma_crypt_h_
#include <ma_hash.h>
#include <stddef.h>
#include <stdarg.h>
/*! Hash algorithms */
#define MA_HASH_MD5 1
#define MA_HASH_SHA1 2
#define MA_HASH_SHA224 3
#define MA_HASH_SHA256 4
#define MA_HASH_SHA384 5
#define MA_HASH_SHA512 6
#define MA_HASH_RIPEMD160 7
#define MA_HASH_MAX 8
/*! Hash digest sizes */
#define MA_MD5_HASH_SIZE 16
#define MA_SHA1_HASH_SIZE 20
#define MA_SHA224_HASH_SIZE 28
#define MA_SHA256_HASH_SIZE 32
#define MA_SHA384_HASH_SIZE 48
#define MA_SHA512_HASH_SIZE 64
#define MA_RIPEMD160_HASH_SIZE 20
#define MA_MAX_HASH_SIZE 64
/** \typedef MRL hash context */
#if defined(HAVE_WINCRYPT)
typedef void MA_HASH_CTX;
#elif defined(HAVE_OPENSSL)
@ -123,8 +109,6 @@ static inline size_t ma_hash_digest_size(unsigned int hash_alg)
return MA_SHA384_HASH_SIZE;
case MA_HASH_SHA512:
return MA_SHA512_HASH_SIZE;
case MA_HASH_RIPEMD160:
return MA_RIPEMD160_HASH_SIZE;
default:
return 0;
}
@ -152,4 +136,4 @@ static inline void ma_hash(unsigned int algorithm,
ma_hash_free(ctx);
}
#endif /* _ma_hash_h_ */
#endif /* _ma_crypt_h_ */

22
include/ma_hash.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef _ma_hash_h_
#define _ma_hash_h_
/*! Hash algorithms */
#define MA_HASH_MD5 1
#define MA_HASH_SHA1 2
#define MA_HASH_SHA224 3
#define MA_HASH_SHA256 4
#define MA_HASH_SHA384 5
#define MA_HASH_SHA512 6
/*! Hash digest sizes */
#define MA_MD5_HASH_SIZE 16
#define MA_SHA1_HASH_SIZE 20
#define MA_SHA224_HASH_SIZE 28
#define MA_SHA256_HASH_SIZE 32
#define MA_SHA384_HASH_SIZE 48
#define MA_SHA512_HASH_SIZE 64
#define MA_MAX_HASH_SIZE 64
#endif

View File

@ -1,6 +1,8 @@
#ifndef _ma_tls_h_
#define _ma_tls_h_
#include <ma_hash.h>
enum enum_pvio_tls_type {
SSL_TYPE_DEFAULT=0,
#ifdef _WIN32
@ -128,12 +130,14 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ssl);
returns SHA1 finger print of server certificate
Parameter:
MARIADB_TLS MariaDB SSL container
hash_type hash_type as defined in ma_hash.h
fp buffer for fingerprint
fp_len buffer length
Returns:
actual size of finger print
*/
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int fp_len);
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp, unsigned int fp_len);
/* ma_tls_get_protocol_version
returns protocol version number in use

View File

@ -344,6 +344,10 @@ IF(WIN32)
${CC_SOURCE_DIR}/win-iconv/win_iconv.c
win32_errmsg.c
win32_errmsg.h)
IF(WITH_SSL STREQUAL "SCHANNEL")
SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES}
secure/win_crypt.c)
ENDIF()
ELSE()
IF(ICONV_INCLUDE_DIR)
INCLUDE_DIRECTORIES(BEFORE ${ICONV_INCLUDE_DIR})

View File

@ -41,12 +41,15 @@
#include <ma_tls.h>
#include <mysql/client_plugin.h>
#include <mariadb/ma_io.h>
#include <ma_hash.h>
#ifdef HAVE_NONBLOCK
#include <mariadb_async.h>
#include <ma_context.h>
#endif
#define MAX_FINGERPRINT_LEN 128;
/* Errors should be handled via pvio callback function */
my_bool ma_tls_initialized= FALSE;
unsigned int mariadb_deinitialize_ssl= 1;
@ -141,36 +144,74 @@ static signed char ma_hex2int(char c)
return -1;
}
static my_bool ma_pvio_tls_compare_fp(const char *cert_fp,
unsigned int cert_fp_len,
const char *fp, unsigned int fp_len)
#ifndef EVP_MAX_MD_SIZE
#define EVP_MAX_MD_SIZE 64
#endif
static my_bool ma_pvio_tls_compare_fp(MARIADB_TLS *ctls,
const char *cert_fp,
unsigned int cert_fp_len
)
{
char *p= (char *)fp,
*c;
const char fp[EVP_MAX_MD_SIZE];
unsigned int fp_len= EVP_MAX_MD_SIZE;
unsigned int hash_type;
/* check length */
if (cert_fp_len != 20)
char *p, *c;
uint hash_len;
/* check length without colons */
if (strchr(cert_fp, ':'))
hash_len= (uint)((strlen(cert_fp) + 1) / 3) * 2;
else
hash_len= (uint)strlen(cert_fp);
/* check hash size */
switch (hash_len) {
#ifndef DISABLE_WEAK_HASH
case MA_SHA1_HASH_SIZE * 2:
hash_type = MA_HASH_SHA1;
break;
#endif
case MA_SHA224_HASH_SIZE * 2:
hash_type = MA_HASH_SHA224;
break;
case MA_SHA256_HASH_SIZE * 2:
hash_type = MA_HASH_SHA256;
break;
case MA_SHA384_HASH_SIZE * 2:
hash_type = MA_HASH_SHA384;
break;
case MA_SHA512_HASH_SIZE * 2:
hash_type = MA_HASH_SHA512;
break;
default:
{
MYSQL* mysql = ctls->pvio->mysql;
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Unknown or invalid fingerprint hash size detected");
return 1;
}
}
if (!ma_tls_get_finger_print(ctls, hash_type, (char *)fp, fp_len))
return 1;
/* We support two formats:
2 digits hex numbers, separated by colons (length=59)
20 * 2 digits hex numbers without separators (length = 40)
*/
if (fp_len != (strchr(fp, ':') ? 59 : 40))
return 1;
p= (char *)cert_fp;
c = (char *)fp;
for(c= (char *)cert_fp; c < cert_fp + cert_fp_len; c++)
for (p = (char*)cert_fp; p < cert_fp + cert_fp_len; c++, p += 2)
{
signed char d1, d2;
if (*p == ':')
p++;
if (p - fp > (int)fp_len -1)
if (p - cert_fp > (int)fp_len - 1)
return 1;
if ((d1 = ma_hex2int(*p)) == - 1 ||
(d2 = ma_hex2int(*(p+1))) == -1 ||
(char)(d1 * 16 + d2) != *c)
if ((d1 = ma_hex2int(*p)) == -1 ||
(d2 = ma_hex2int(*(p + 1))) == -1 ||
(char)(d1 * 16 + d2) != *c)
return 1;
p+= 2;
}
return 0;
}
@ -184,10 +225,10 @@ my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_l
cert_fp= (char *)malloc(cert_fp_len);
if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1)
goto end;
if (fp)
rc= ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, fp, (unsigned int)strlen(fp));
{
rc = ma_pvio_tls_compare_fp(ctls, fp, (uint)strlen(fp));
}
else if (fp_list)
{
MA_FILE *fp;
@ -205,7 +246,7 @@ my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_l
if (pos)
*pos= '\0';
if (!ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, buff, (unsigned int)strlen(buff)))
if (!ma_pvio_tls_compare_fp(ctls, cert_fp, cert_fp_len))
{
/* finger print is valid: close file and exit */
ma_close(fp);

View File

@ -1391,18 +1391,43 @@ static int my_verify_callback(gnutls_session_t ssl)
return 0;
}
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp, unsigned int len)
{
MYSQL *mysql;
size_t fp_len= len;
const gnutls_datum_t *cert_list;
unsigned int cert_list_size;
gnutls_digest_algorithm_t hash_alg;
if (!ctls || !ctls->ssl)
return 0;
mysql= (MYSQL *)gnutls_session_get_ptr(ctls->ssl);
switch (hash_type)
{
case MA_HASH_SHA1:
hash_alg = GNUTLS_DIG_SHA1;
break;
case MA_HASH_SHA224:
hash_alg = GNUTLS_DIG_SHA224;
break;
case MA_HASH_SHA256:
hash_alg = GNUTLS_DIG_SHA256;
break;
case MA_HASH_SHA384:
hash_alg = GNUTLS_DIG_SHA384;
break;
case MA_HASH_SHA512:
hash_alg = GNUTLS_DIG_SHA512;
break;
default:
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Cannot detect hash algorithm for fingerprint verification");
return 0;
}
cert_list = gnutls_certificate_get_peers (ctls->ssl, &cert_list_size);
if (cert_list == NULL)
{
@ -1412,7 +1437,7 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l
return 0;
}
if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &cert_list[0], fp, &fp_len) == 0)
if (gnutls_fingerprint(hash_alg, &cert_list[0], fp, &fp_len) == 0)
return fp_len;
else
{

View File

@ -34,8 +34,6 @@ static gnutls_digest_algorithm_t ma_hash_get_algorithm(unsigned int alg)
return GNUTLS_DIG_SHA384;
case MA_HASH_SHA512:
return GNUTLS_DIG_SHA512;
case MA_HASH_RIPEMD160:
return GNUTLS_DIG_RMD160;
default:
return GNUTLS_DIG_UNKNOWN;
}

View File

@ -28,6 +28,7 @@
#include <ma_common.h>
#include <ma_pvio.h>
#include <errmsg.h>
#include <ma_hash.h>
#include <wincrypt.h>
@ -35,6 +36,7 @@
#include <security.h>
#include <ma_crypt.h>
#include <schnlsp.h>
#undef SECURITY_WIN32
@ -57,7 +59,7 @@ struct st_schannel {
DWORD IoBufferSize;
SecPkgContext_StreamSizes Sizes;
CtxtHandle hCtxt;
BCRYPT_ALG_HANDLE HashProv[MA_MAX_HASH_SIZE];
/* Cached data from the last read/decrypt call.*/
SecBuffer extraBuf; /* encrypted data read from server. */
SecBuffer dataBuf; /* decrypted but still unread data from server.*/

View File

@ -29,6 +29,7 @@
#include <openssl/err.h> /* error reporting */
#include <openssl/conf.h>
#include <openssl/md4.h>
#include <ma_tls.h>
#if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C)
#include <openssl/applink.c>
@ -729,17 +730,50 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
return SSL_get_cipher_name(ctls->ssl);
}
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp, unsigned int len)
{
X509 *cert= NULL;
MYSQL *mysql;
unsigned int fp_len;
const EVP_MD *hash_alg;
if (!ctls || !ctls->ssl)
return 0;
mysql= SSL_get_app_data(ctls->ssl);
mysql = SSL_get_app_data(ctls->ssl);
if (len < EVP_MAX_MD_SIZE)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Finger print buffer too small");
return 0;
}
switch (hash_type)
{
case MA_HASH_SHA1:
hash_alg = EVP_sha1();
break;
case MA_HASH_SHA224:
hash_alg = EVP_sha224();
break;
case MA_HASH_SHA256:
hash_alg = EVP_sha256();
break;
case MA_HASH_SHA384:
hash_alg = EVP_sha384();
break;
case MA_HASH_SHA512:
hash_alg = EVP_sha512();
break;
default:
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Cannot detect hash algorithm for fingerprint verification");
return 0;
}
if (!(cert= SSL_get_peer_certificate(ctls->ssl)))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
@ -748,14 +782,7 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l
goto end;
}
if (len < EVP_MAX_MD_SIZE)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Finger print buffer too small");
goto end;
}
if (!X509_digest(cert, EVP_sha1(), (unsigned char *)fp, &fp_len))
if (!X509_digest(cert, hash_alg, (unsigned char *)fp, &fp_len))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),

View File

@ -36,8 +36,6 @@ static const EVP_MD *ma_hash_get_algorithm(unsigned int alg)
return EVP_sha384();
case MA_HASH_SHA512:
return EVP_sha512();
case MA_HASH_RIPEMD160:
return EVP_ripemd160();
default:
return NULL;
}

View File

@ -20,6 +20,9 @@
#include "ma_schannel.h"
#include "schannel_certs.h"
#include <string.h>
#include <ma_crypt.h>
#include <wincrypt.h>
#include <bcrypt.h>
extern my_bool ma_tls_initialized;
char tls_library_version[] = "Schannel";
@ -550,15 +553,33 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
return cipher_name(&CipherInfo);
}
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp, unsigned int len)
{
MA_HASH_CTX* hash_ctx;
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
PCCERT_CONTEXT pRemoteCertContext = NULL;
int rc= 0;
if (hash_type == MA_HASH_SHA224)
{
MYSQL *mysql = ctls->pvio->mysql;
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"SHA224 hash for fingerprint verification is not supported in Schannel");
return 0;
}
if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext) != SEC_E_OK)
return 0;
CertGetCertificateContextProperty(pRemoteCertContext, CERT_HASH_PROP_ID, fp, (DWORD *)&len);
hash_ctx = ma_hash_new(hash_type);
ma_hash_input(hash_ctx, pRemoteCertContext->pbCertEncoded, pRemoteCertContext->cbCertEncoded);
ma_hash_result(hash_ctx, fp);
ma_hash_free(hash_ctx);
CertFreeCertificateContext(pRemoteCertContext);
return len;
return (uint)ma_hash_digest_size(hash_type);
}
void ma_tls_set_connection(MYSQL *mysql __attribute__((unused)))