mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-07-22 07:00:40 +00:00
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:

committed by
Sergei Golubchik

parent
395641549a
commit
9aa15e72a7
@ -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)
|
||||
|
@ -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
22
include/ma_hash.h
Normal 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
|
@ -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
|
||||
|
@ -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})
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.*/
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)))
|
||||
|
Reference in New Issue
Block a user