From 7bcd78f93b84ad0478985f7f9ca4b8156f12db45 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 10 Oct 2017 12:19:01 +0200 Subject: [PATCH] CONC-286: - Force TLS/SSL connection if finger print options were specified - Allow hex finger prints with colon separated 2 digit numbers --- libmariadb/ma_tls.c | 49 ++++++++++++++++++++++++++++-------- libmariadb/mariadb_lib.c | 2 ++ libmariadb/secure/gnutls.c | 3 +++ unittest/libmariadb/ssl.c.in | 46 +++++++++++++++++++++++++++++++-- 4 files changed, 87 insertions(+), 13 deletions(-) diff --git a/libmariadb/ma_tls.c b/libmariadb/ma_tls.c index f1177e0f..d5550ddc 100644 --- a/libmariadb/ma_tls.c +++ b/libmariadb/ma_tls.c @@ -119,21 +119,48 @@ my_bool ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_versio return ma_tls_get_protocol_version(ctls, version); } -static my_bool ma_pvio_tls_compare_fp(const char *fp1, unsigned int fp1_len, - const char *fp2, unsigned int fp2_len) +static char ma_hex2int(char c) { - char hexstr[64]; + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + return -1; +} - fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len); - if (fp1_len != fp2_len) +static my_bool ma_pvio_tls_compare_fp(const char *cert_fp, + unsigned int cert_fp_len, + const char *fp, unsigned int fp_len) +{ + char *p= (char *)fp, + *c; + + /* check length */ + if (cert_fp_len != 20) return 1; -#ifdef WIN32 - if (strnicmp(hexstr, fp2, fp1_len) != 0) -#else - if (strncasecmp(hexstr, fp2, fp1_len) != 0) -#endif - 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; + + for(c= (char *)cert_fp; c < cert_fp + cert_fp_len; c++) + { + char d1, d2; + if (*p == ':') + p++; + if (p - fp > fp_len -1) + return 1; + if ((d1 = ma_hex2int(*p)) == - 1 || + (d2 = ma_hex2int(*(p+1))) == -1 || + (char)(d1 * 16 + d2) != *c) + return 1; + p+= 2; + } return 0; } diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 43323c71..9d3a60d3 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -2983,10 +2983,12 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) case MARIADB_OPT_SSL_FP: case MARIADB_OPT_TLS_PEER_FP: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_fp, (char *)arg1); + mysql->options.use_ssl= 1; break; case MARIADB_OPT_SSL_FP_LIST: case MARIADB_OPT_TLS_PEER_FP_LIST: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_fp_list, (char *)arg1); + mysql->options.use_ssl= 1; break; case MARIADB_OPT_TLS_PASSPHRASE: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_pw, (char *)arg1); diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index e657cf6d..25b2582d 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -1347,6 +1347,9 @@ static int my_verify_callback(gnutls_session_t ssl) if (status & GNUTLS_CERT_INVALID) { + char errbuf[100]; + snprintf(errbuf, 99, "CA Verification failed (Status: %d)", status); + pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, errbuf); return GNUTLS_E_CERTIFICATE_ERROR; } /* Up to here the process is the same for X.509 certificates and diff --git a/unittest/libmariadb/ssl.c.in b/unittest/libmariadb/ssl.c.in index e1db4bfa..274eb35c 100644 --- a/unittest/libmariadb/ssl.c.in +++ b/unittest/libmariadb/ssl.c.in @@ -50,8 +50,12 @@ int check_skip_ssl() } if (!(ssldir= getenv("SECURE_LOAD_PATH"))) { - diag("certificate directory not found"); - return 1; + ssldir= "@CERT_PATH@"; + if (!strlen(ssldir)) + { + diag("certificate directory not found"); + return 1; + } } snprintf(sslcert, FNLEN - 1, "%s/%s", ssldir, "client-cert.pem"); snprintf(sslkey, FNLEN - 1, "%s/%s", ssldir, "client-key.pem"); @@ -744,6 +748,12 @@ static int test_ssl_fp(MYSQL *unused __attribute__((unused))) if (check_skip_ssl()) return SKIP; + if (!ssl_cert_finger_print[0]) + { + diag("No fingerprint available"); + return SKIP; + } + my= mysql_init(NULL); FAIL_IF(!my, "mysql_init() failed"); @@ -780,6 +790,11 @@ static int test_ssl_fp_list(MYSQL *unused __attribute__((unused))) if (check_skip_ssl()) return SKIP; + if (!ssl_cert_finger_print[0]) + { + diag("No fingerprint available"); + return SKIP; + } my= mysql_init(NULL); FAIL_IF(!my, "mysql_init() failed"); @@ -1091,8 +1106,35 @@ static int drop_ssl_user(MYSQL *mysql) return OK; } +static int test_conc286(MYSQL *unused __attribute__((unused))) +{ + MYSQL *my; + + if (check_skip_ssl()) + return SKIP; + + if (!ssl_cert_finger_print[0]) + { + diag("No fingerprint available"); + return SKIP; + } + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + mysql_options(my, MARIADB_OPT_SSL_FP, ssl_cert_finger_print); + + FAIL_IF(!mysql_real_connect(my, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); + + mysql_close(my); + return OK; +} + struct my_tests_st my_tests[] = { {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc286", test_conc286, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl_timeout", test_ssl_timeout, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_openssl_1", test_openssl_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, #ifndef HAVE_SCHANNEL