You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-08 14:02:17 +03:00
CONC-692: Provide X509 peer certificate information
Added a new structure MARIADB_X509_INFO, which contains information about servers certificate. The information can be obtained via mysql_get_infov API function: MARIADB_X509_INFO *info; mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info);
This commit is contained in:
@@ -27,6 +27,7 @@ typedef struct st_ma_pvio_tls {
|
||||
void *data;
|
||||
MARIADB_PVIO *pvio;
|
||||
void *ssl;
|
||||
MARIADB_X509_INFO cert_info;
|
||||
} MARIADB_TLS;
|
||||
|
||||
/* Function prototypes */
|
||||
|
@@ -33,6 +33,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#if !defined (_global_h) && !defined (MY_GLOBAL_INCLUDED) /* If not standard header */
|
||||
#include <sys/types.h>
|
||||
@@ -297,7 +298,8 @@ extern const char *SQLSTATE_UNKNOWN;
|
||||
MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES,
|
||||
MARIADB_CONNECTION_CLIENT_CAPABILITIES,
|
||||
MARIADB_CONNECTION_BYTES_READ,
|
||||
MARIADB_CONNECTION_BYTES_SENT
|
||||
MARIADB_CONNECTION_BYTES_SENT,
|
||||
MARIADB_TLS_PEER_CERT_INFO,
|
||||
};
|
||||
|
||||
enum mysql_status { MYSQL_STATUS_READY,
|
||||
@@ -481,6 +483,27 @@ struct st_mysql_client_plugin
|
||||
MYSQL_CLIENT_PLUGIN_HEADER
|
||||
};
|
||||
|
||||
enum mariadb_tls_verification {
|
||||
MARIADB_VERIFY_NONE = 0,
|
||||
MARIADB_VERIFY_PIPE,
|
||||
MARIADB_VERIFY_UNIXSOCKET,
|
||||
MARIADB_VERIFY_LOCALHOST,
|
||||
MARIADB_VERIFY_FINGERPRINT,
|
||||
MARIADB_VERIFY_PEER_CERT
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int version;
|
||||
char *issuer;
|
||||
char *subject;
|
||||
char fingerprint[65];
|
||||
struct tm not_before;
|
||||
struct tm not_after;
|
||||
enum mariadb_tls_verification verify_mode;
|
||||
} MARIADB_X509_INFO;
|
||||
|
||||
|
||||
struct st_mysql_client_plugin *
|
||||
mysql_load_plugin(struct st_mysql *mysql, const char *name, int type,
|
||||
int argc, ...);
|
||||
|
@@ -549,13 +549,24 @@ static my_bool ignore_self_signed_cert_error(MARIADB_PVIO *pvio)
|
||||
"127.0.0.1", "::1", NULL};
|
||||
int i;
|
||||
if (pvio->type != PVIO_TYPE_SOCKET)
|
||||
{
|
||||
pvio->ctls->cert_info.verify_mode=
|
||||
#ifdef WIN32
|
||||
MARIADB_VERIFY_PIPE;
|
||||
#else
|
||||
MARIADB_VERIFY_UNIXSOCKET;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
if (!hostname)
|
||||
return FALSE;
|
||||
for (i= 0; local_host_names[i]; i++)
|
||||
{
|
||||
if (strcmp(hostname, local_host_names[i]) == 0)
|
||||
{
|
||||
pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_LOCALHOST;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
@@ -586,6 +597,8 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio)
|
||||
!pvio->mysql->net.tls_self_signed_error &&
|
||||
ma_pvio_tls_verify_server_cert(pvio->ctls))
|
||||
return 1;
|
||||
else
|
||||
pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_PEER_CERT;
|
||||
|
||||
if (pvio->mysql->options.extension &&
|
||||
((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) ||
|
||||
@@ -595,6 +608,7 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio)
|
||||
pvio->mysql->options.extension->tls_fp,
|
||||
pvio->mysql->options.extension->tls_fp_list))
|
||||
return 1;
|
||||
pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_FINGERPRINT;
|
||||
reset_tls_self_signed_error(pvio->mysql); // validated
|
||||
}
|
||||
|
||||
|
@@ -4519,6 +4519,11 @@ my_bool mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ...
|
||||
va_start(ap, arg);
|
||||
|
||||
switch(value) {
|
||||
#ifdef HAVE_TLS
|
||||
case MARIADB_TLS_PEER_CERT_INFO:
|
||||
*((MARIADB_X509_INFO **)arg)= mysql->net.pvio->ctls ? (MARIADB_X509_INFO *)&mysql->net.pvio->ctls->cert_info : NULL;
|
||||
break;
|
||||
#endif
|
||||
case MARIADB_MAX_ALLOWED_PACKET:
|
||||
*((size_t *)arg)= (size_t)max_allowed_packet;
|
||||
break;
|
||||
|
@@ -1173,6 +1173,8 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl);
|
||||
MARIADB_PVIO *pvio;
|
||||
int ret;
|
||||
const gnutls_datum_t *cert_list;
|
||||
unsigned int list_size= 0;
|
||||
|
||||
if (!mysql)
|
||||
return 1;
|
||||
@@ -1214,6 +1216,39 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
return 1;
|
||||
}
|
||||
ctls->ssl= (void *)ssl;
|
||||
|
||||
/* retrieve peer certificate information */
|
||||
if ((cert_list= gnutls_certificate_get_peers(ssl, &list_size)))
|
||||
{
|
||||
gnutls_x509_crt_t cert;
|
||||
|
||||
gnutls_x509_crt_init(&cert);
|
||||
memset(&ctls->cert_info, 0, sizeof(MARIADB_X509_INFO));
|
||||
|
||||
if (!gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER))
|
||||
{
|
||||
size_t len= 0;
|
||||
time_t notBefore, notAfter;
|
||||
|
||||
ctls->cert_info.version= gnutls_x509_crt_get_version(cert);
|
||||
|
||||
gnutls_x509_crt_get_issuer_dn(cert, NULL, &len);
|
||||
if ((ctls->cert_info.issuer= (char *)malloc(len)))
|
||||
gnutls_x509_crt_get_issuer_dn(cert, ctls->cert_info.issuer, &len);
|
||||
|
||||
gnutls_x509_crt_get_dn(cert, NULL, &len);
|
||||
if ((ctls->cert_info.subject= (char *)malloc(len)))
|
||||
gnutls_x509_crt_get_dn(cert, ctls->cert_info.subject, &len);
|
||||
|
||||
notBefore= gnutls_x509_crt_get_activation_time(cert);
|
||||
memcpy(&ctls->cert_info.not_before, gmtime(¬Before), sizeof(struct tm));
|
||||
|
||||
notAfter= gnutls_x509_crt_get_expiration_time(cert);
|
||||
memcpy(&ctls->cert_info.not_after, gmtime(¬After), sizeof(struct tm));
|
||||
|
||||
ma_tls_get_finger_print(ctls, MA_HASH_SHA256, (char *)&ctls->cert_info.fingerprint, 32);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1321,6 +1356,8 @@ my_bool ma_tls_close(MARIADB_TLS *ctls)
|
||||
gnutls_certificate_free_ca_names(ctx);
|
||||
gnutls_certificate_free_credentials(ctx);
|
||||
gnutls_deinit((gnutls_session_t )ctls->ssl);
|
||||
free(ctls->cert_info.issuer);
|
||||
free(ctls->cert_info.subject);
|
||||
ctls->ssl= NULL;
|
||||
}
|
||||
return 0;
|
||||
|
@@ -463,6 +463,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
MYSQL *mysql;
|
||||
MARIADB_PVIO *pvio;
|
||||
int rc;
|
||||
X509 *cert;
|
||||
#ifdef OPENSSL_USE_BIOMETHOD
|
||||
BIO_METHOD *bio_method= NULL;
|
||||
BIO *bio;
|
||||
@@ -529,6 +530,23 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
}
|
||||
pvio->ctls->ssl= ctls->ssl= (void *)ssl;
|
||||
|
||||
/* Store peer certificate information */
|
||||
if ((cert= SSL_get_peer_certificate(ssl)))
|
||||
{
|
||||
char fp[33];
|
||||
const ASN1_TIME *not_before= X509_get0_notBefore(cert),
|
||||
*not_after= X509_get0_notAfter(cert);
|
||||
ctls->cert_info.subject= X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
|
||||
ctls->cert_info.issuer= X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
|
||||
ctls->cert_info.version= X509_get_version(cert) + 1;
|
||||
|
||||
ASN1_TIME_to_tm(not_before, (struct tm *)&ctls->cert_info.not_before);
|
||||
ASN1_TIME_to_tm(not_after, (struct tm *)&ctls->cert_info.not_after);
|
||||
|
||||
ma_tls_get_finger_print(ctls, MA_HASH_SHA256, fp, 33);
|
||||
mysql_hex_string(ctls->cert_info.fingerprint, fp, 32);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -654,6 +672,9 @@ my_bool ma_tls_close(MARIADB_TLS *ctls)
|
||||
SSL_free(ssl);
|
||||
ctls->ssl= NULL;
|
||||
|
||||
OPENSSL_free(ctls->cert_info.issuer);
|
||||
OPENSSL_free(ctls->cert_info.subject);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -739,33 +760,30 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp
|
||||
MYSQL *mysql;
|
||||
unsigned int fp_len;
|
||||
const EVP_MD *hash_alg;
|
||||
unsigned int max_len= EVP_MAX_MD_SIZE;
|
||||
|
||||
if (!ctls || !ctls->ssl)
|
||||
return 0;
|
||||
|
||||
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();
|
||||
max_len= 20;
|
||||
break;
|
||||
case MA_HASH_SHA224:
|
||||
hash_alg = EVP_sha224();
|
||||
max_len= 28;
|
||||
break;
|
||||
case MA_HASH_SHA256:
|
||||
hash_alg = EVP_sha256();
|
||||
max_len= 32;
|
||||
break;
|
||||
case MA_HASH_SHA384:
|
||||
hash_alg = EVP_sha384();
|
||||
max_len= 48;
|
||||
break;
|
||||
case MA_HASH_SHA512:
|
||||
hash_alg = EVP_sha512();
|
||||
@@ -777,6 +795,14 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len < max_len)
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Finger print buffer too small");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(cert= SSL_get_peer_certificate(ctls->ssl)))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
|
@@ -32,6 +32,8 @@ char tls_library_version[] = "Schannel";
|
||||
#define PROT_TLS1_2 4
|
||||
#define PROT_TLS1_3 8
|
||||
|
||||
unsigned int ma_set_tls_x509_info(MARIADB_TLS *ctls);
|
||||
|
||||
static struct
|
||||
{
|
||||
DWORD cipher_id;
|
||||
@@ -457,6 +459,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
goto end;
|
||||
}
|
||||
|
||||
ma_set_tls_x509_info(ctls);
|
||||
rc = 0;
|
||||
|
||||
end:
|
||||
@@ -511,6 +514,8 @@ my_bool ma_tls_close(MARIADB_TLS *ctls)
|
||||
DeleteSecurityContext(&sctx->hCtxt);
|
||||
}
|
||||
LocalFree(sctx);
|
||||
LocalFree(ctls->cert_info.issuer);
|
||||
LocalFree(ctls->cert_info.subject);
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -551,6 +556,60 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
|
||||
return cipher_name(&CipherInfo);
|
||||
}
|
||||
|
||||
unsigned char *ma_cert_blob_to_str(PCERT_NAME_BLOB cnblob)
|
||||
{
|
||||
DWORD type= CERT_X500_NAME_STR;
|
||||
DWORD size= CertNameToStrA(X509_ASN_ENCODING, cnblob, type, NULL, 0);
|
||||
char *str= NULL;
|
||||
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
str= (char *)LocalAlloc(LMEM_ZEROINIT,size);
|
||||
CertNameToStrA(X509_ASN_ENCODING, cnblob, type, str, size);
|
||||
return str;
|
||||
}
|
||||
|
||||
static void ma_systime_to_tm(SYSTEMTIME sys_tm, struct tm *tm)
|
||||
{
|
||||
memset(tm, 0, sizeof(struct tm));
|
||||
tm->tm_year= sys_tm.wYear - 1900;
|
||||
tm->tm_mon= sys_tm.wMonth - 1;
|
||||
tm->tm_mday= sys_tm.wDay;
|
||||
tm->tm_hour = sys_tm.wHour;
|
||||
tm->tm_min = sys_tm.wMinute;
|
||||
}
|
||||
|
||||
unsigned int ma_set_tls_x509_info(MARIADB_TLS *ctls)
|
||||
{
|
||||
PCCERT_CONTEXT pCertCtx= NULL;
|
||||
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
|
||||
PCERT_INFO pci= NULL;
|
||||
DWORD size´= 0;
|
||||
SYSTEMTIME tm;
|
||||
char fp[33];
|
||||
|
||||
if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pCertCtx) != SEC_E_OK)
|
||||
return 1;
|
||||
|
||||
pci= pCertCtx->pCertInfo;
|
||||
|
||||
ctls->cert_info.version= pci->dwVersion;
|
||||
ctls->cert_info.subject = ma_cert_blob_to_str(&pci->Subject);
|
||||
ctls->cert_info.issuer = ma_cert_blob_to_str(&pci->Issuer);
|
||||
|
||||
FileTimeToSystemTime(&pci->NotBefore, &tm);
|
||||
ma_systime_to_tm(tm, &ctls->cert_info.not_before);
|
||||
FileTimeToSystemTime(&pci->NotAfter, &tm);
|
||||
ma_systime_to_tm(tm, &ctls->cert_info.not_after);
|
||||
|
||||
ma_tls_get_finger_print(ctls, MA_HASH_SHA256, fp, 33);
|
||||
mysql_hex_string(ctls->cert_info.fingerprint, fp, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp, unsigned int len)
|
||||
{
|
||||
MA_HASH_CTX* hash_ctx;
|
||||
|
@@ -1369,6 +1369,7 @@ static int test_conc276(MYSQL *unused __attribute__((unused)))
|
||||
MYSQL *mysql= mysql_init(NULL);
|
||||
int rc;
|
||||
my_bool val= 1;
|
||||
MARIADB_X509_INFO *info;
|
||||
|
||||
mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &val);
|
||||
mysql_options(mysql, MYSQL_OPT_RECONNECT, &val);
|
||||
@@ -1380,7 +1381,15 @@ static int test_conc276(MYSQL *unused __attribute__((unused)))
|
||||
return FAIL;
|
||||
}
|
||||
diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql));
|
||||
mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info);
|
||||
|
||||
diag("subject: %s", info->subject);
|
||||
diag("issuer: %s", info->issuer);
|
||||
diag("fingerprint: %s", info->fingerprint);
|
||||
diag("not before : %04d.%02d.%02d", info->not_before.tm_year + 1900,
|
||||
info->not_before.tm_mon + 1, info->not_before.tm_mday);
|
||||
diag("not after : %04d.%02d.%02d", info->not_after.tm_year + 1900,
|
||||
info->not_after.tm_mon + 1, info->not_after.tm_mday);
|
||||
rc= mariadb_reconnect(mysql);
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
@@ -2309,7 +2318,49 @@ static int test_conc632(MYSQL *my __attribute__((unused)))
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int test_x509(MYSQL *my __attribute__((unused)))
|
||||
{
|
||||
MYSQL *mysql1, *mysql2;
|
||||
my_bool val= 1;
|
||||
my_bool verify= 0;
|
||||
char fp[65];
|
||||
MARIADB_X509_INFO *info;
|
||||
|
||||
mysql1= mysql_init(NULL);
|
||||
mysql2= mysql_init(NULL);
|
||||
|
||||
mysql_options(mysql1, MYSQL_OPT_SSL_ENFORCE, &val);
|
||||
mysql_options(mysql2, MYSQL_OPT_SSL_ENFORCE, &val);
|
||||
|
||||
mysql_options(mysql1, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify);
|
||||
if (!(my_test_connect(mysql1, hostname, username,
|
||||
password, schema, port,
|
||||
socketname, 0)))
|
||||
{
|
||||
diag("connection failed");
|
||||
return FAIL;
|
||||
}
|
||||
mariadb_get_infov(mysql1, MARIADB_TLS_PEER_CERT_INFO, &info);
|
||||
memset(fp, 0, 65);
|
||||
diag("fingerprint: %s", info->fingerprint);
|
||||
mysql_options(mysql2, MARIADB_OPT_TLS_PEER_FP, info->fingerprint);
|
||||
if (!(my_test_connect(mysql2, hostname, username,
|
||||
password, schema, port,
|
||||
socketname, 0)))
|
||||
{
|
||||
diag("connection failed");
|
||||
return FAIL;
|
||||
}
|
||||
mariadb_get_infov(mysql2, MARIADB_TLS_PEER_CERT_INFO, &info);
|
||||
FAIL_IF(info->verify_mode != MARIADB_VERIFY_FINGERPRINT, "Fingerprint verification expected");
|
||||
|
||||
mysql_close(mysql1);
|
||||
mysql_close(mysql2);
|
||||
return OK;
|
||||
}
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"test_x509", test_x509, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||
{"test_conc632", test_conc632, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||
{"test_status_callback", test_status_callback, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||
{"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||
|
Reference in New Issue
Block a user