diff --git a/.travis.yml b/.travis.yml index 312384fa..be52066d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,16 @@ cache: directories: - $HOME/docker +before_install: + - |- + if [ -z "$server_branch" ] ; then + case $TRAVIS_OS_NAME in + windows) + choco install python --version=3.12.0 + ;; + esac + fi + env: global: local=0 DB=testc CLEAR_TEXT=0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 03f8e31f..8994119c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ SET(CC_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) SET(CPACK_PACKAGE_VERSION_MAJOR 3) SET(CPACK_PACKAGE_VERSION_MINOR 4) -SET(CPACK_PACKAGE_VERSION_PATCH 0) +SET(CPACK_PACKAGE_VERSION_PATCH 1) SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") MATH(EXPR MARIADB_PACKAGE_VERSION_ID "${CPACK_PACKAGE_VERSION_MAJOR} * 10000 + ${CPACK_PACKAGE_VERSION_MINOR} * 100 + diff --git a/include/ma_common.h b/include/ma_common.h index dfa96621..dc900b0d 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -131,15 +131,9 @@ typedef struct st_mariadb_field_extension MARIADB_CONST_STRING metadata[MARIADB_FIELD_ATTR_LAST+1]; /* 10.5 */ } MA_FIELD_EXTENSION; -#if defined(HAVE_SCHANNEL) || defined(HAVE_GNUTLS) -#define reset_tls_self_signed_error(mysql) \ - do { \ - free((char*)mysql->net.tls_self_signed_error); \ - mysql->net.tls_self_signed_error= 0; \ - } while(0) -#else -#define reset_tls_self_signed_error(mysql) \ - do { \ - mysql->net.tls_self_signed_error= 0; \ +#ifdef HAVE_TLS +#define reset_tls_error(mysql) \ + do { \ + mysql->net.tls_verify_status= 0; \ } while(0) #endif diff --git a/include/ma_tls.h b/include/ma_tls.h index 23f53560..444ea4aa 100644 --- a/include/ma_tls.h +++ b/include/ma_tls.h @@ -21,6 +21,12 @@ enum enum_pvio_tls_type { #define PROTOCOL_MAX PROTOCOL_TLS_1_3 #define TLS_VERSION_LENGTH 64 + +#define have_fingerprint(m) \ +((m)->options.extension) && \ +(((m)->options.extension->tls_fp && (m)->options.extension->tls_fp[0]) ||\ +((m)->options.extension->tls_fp_list && (m)->options.extension->tls_fp_list[0])) + extern char tls_library_version[TLS_VERSION_LENGTH]; typedef struct st_ma_pvio_tls { @@ -111,11 +117,12 @@ my_bool ma_tls_close(MARIADB_TLS *ctls); validation check of server certificate Parameter: MARIADB_TLS MariaDB SSL container + flags verification flags Returns: - ß success + 0 success 1 error */ -int ma_tls_verify_server_cert(MARIADB_TLS *ctls); +int ma_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int flags); /* ma_tls_get_cipher returns cipher for current ssl connection @@ -134,6 +141,7 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ssl); hash_type hash_type as defined in ma_hash.h fp buffer for fingerprint fp_len buffer length + my_bool verify_period Returns: actual size of finger print @@ -150,7 +158,7 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp int ma_tls_get_protocol_version(MARIADB_TLS *ctls); const char *ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls); int ma_pvio_tls_get_protocol_version_id(MARIADB_TLS *ctls); -unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls); +unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls, unsigned int size); void ma_tls_set_connection(MYSQL *mysql); /* Function prototypes */ @@ -159,12 +167,12 @@ my_bool ma_pvio_tls_connect(MARIADB_TLS *ctls); ssize_t ma_pvio_tls_read(MARIADB_TLS *ctls, const uchar *buffer, size_t length); ssize_t ma_pvio_tls_write(MARIADB_TLS *ctls, const uchar *buffer, size_t length); my_bool ma_pvio_tls_close(MARIADB_TLS *ctls); -int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls); +int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int flags); const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls); my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list); my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio); void ma_pvio_tls_set_connection(MYSQL *mysql); void ma_pvio_tls_end(); -unsigned int ma_pvio_tls_get_peer_cert_info(MARIADB_TLS *ctls); +unsigned int ma_pvio_tls_get_peer_cert_info(MARIADB_TLS *ctls, unsigned int size); #endif /* _ma_tls_h_ */ diff --git a/include/mariadb_com.h b/include/mariadb_com.h index 6e7164a3..d1e9f2ba 100644 --- a/include/mariadb_com.h +++ b/include/mariadb_com.h @@ -297,10 +297,10 @@ typedef struct st_net { unsigned char reading_or_writing; char save_char; char unused_1; - my_bool unused_2; + my_bool tls_verify_status; my_bool compress; - my_bool unused_3; - const char *tls_self_signed_error; + my_bool unused_2; + char *unused_3; unsigned int last_errno; unsigned char error; my_bool unused_5; diff --git a/include/mysql.h b/include/mysql.h index 6c84a462..ba92527d 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -300,6 +300,7 @@ extern const char *SQLSTATE_UNKNOWN; MARIADB_CONNECTION_BYTES_READ, MARIADB_CONNECTION_BYTES_SENT, MARIADB_TLS_PEER_CERT_INFO, + MARIADB_TLS_VERIFY_STATUS }; enum mysql_status { MYSQL_STATUS_READY, @@ -448,6 +449,16 @@ typedef struct st_mysql_time #define MYSQL_WAIT_EXCEPT 4 #define MYSQL_WAIT_TIMEOUT 8 +#define MARIADB_TLS_VERIFY_OK 0 +#define MARIADB_TLS_VERIFY_TRUST 1 +#define MARIADB_TLS_VERIFY_HOST 2 +#define MARIADB_TLS_VERIFY_PERIOD 4 +#define MARIADB_TLS_VERIFY_FINGERPRINT 8 +#define MARIADB_TLS_VERIFY_REVOKED 16 +#define MARIADB_TLS_VERIFY_UNKNOWN 32 +#define MARIADB_TLS_VERIFY_ERROR 128 /* last */ + + typedef struct character_set { unsigned int number; /* character set number */ @@ -497,7 +508,7 @@ typedef struct int version; char *issuer; char *subject; - char fingerprint[65]; + char fingerprint[129]; struct tm not_before; struct tm not_after; } MARIADB_X509_INFO; diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index df7e4826..94d05ef6 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -522,47 +522,6 @@ my_bool ma_pvio_has_data(MARIADB_PVIO *pvio, ssize_t *data_len) /* }}} */ #ifdef HAVE_TLS -/** - Checks if self-signed certificate error should be ignored. -*/ -static my_bool ignore_self_signed_cert_error(MARIADB_PVIO *pvio) -{ - const char *hostname= pvio->mysql->host; - const char *local_host_names[]= { -#ifdef _WIN32 - /* - On Unix, we consider TCP connections with "localhost" - an insecure transport, for the single reason to run tests for - insecure transport on CI.This is artificial, but should be ok. - Default client connections use unix sockets anyway, so it - would not hurt much. - - On Windows, the situation is quite different. - Default connections type is TCP, default host name is "localhost", - non-password plugin gssapi is common (every installation) - In this environment, there would be a lot of faux/disruptive - "self-signed certificates" errors there. Thus, "localhost" TCP - needs to be considered secure transport. - */ - "localhost", -#endif - "127.0.0.1", "::1", NULL}; - int i; - if (pvio->type != PVIO_TYPE_SOCKET) - { - return TRUE; - } - if (!hostname) - return FALSE; - for (i= 0; local_host_names[i]; i++) - { - if (strcmp(hostname, local_host_names[i]) == 0) - { - return TRUE; - } - } - return FALSE; -} /* {{{ my_bool ma_pvio_start_ssl */ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) @@ -580,32 +539,6 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) pvio->ctls= NULL; return 1; } - - /* default behaviour: - 1. peer certificate verification - 2. verify CN (requires option ssl_verify_check) - 3. verrify finger print - */ - if (!pvio->mysql->options.extension->tls_allow_invalid_server_cert && - !pvio->mysql->net.tls_self_signed_error && - ma_pvio_tls_verify_server_cert(pvio->ctls)) - return 1; - - if (pvio->mysql->options.extension && - ((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) || - (pvio->mysql->options.extension->tls_fp_list && pvio->mysql->options.extension->tls_fp_list[0]))) - { - if (ma_pvio_tls_check_fp(pvio->ctls, - pvio->mysql->options.extension->tls_fp, - pvio->mysql->options.extension->tls_fp_list)) - return 1; - reset_tls_self_signed_error(pvio->mysql); // validated - return 0; - } - - if (pvio->mysql->net.tls_self_signed_error && ignore_self_signed_cert_error(pvio)) - reset_tls_self_signed_error(pvio->mysql); - return 0; } /* }}} */ diff --git a/libmariadb/ma_tls.c b/libmariadb/ma_tls.c index 1bda7dcf..f6ea6661 100644 --- a/libmariadb/ma_tls.c +++ b/libmariadb/ma_tls.c @@ -103,9 +103,54 @@ my_bool ma_pvio_tls_close(MARIADB_TLS *ctls) return ma_tls_close(ctls); } -int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls) +int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int flags) { - return ma_tls_verify_server_cert(ctls); + MYSQL *mysql; + int rc; + + if (!ctls || !ctls->pvio || !ctls->pvio->mysql) + return 0; + + mysql= ctls->pvio->mysql; + + /* Skip peer certificate verification */ + if (ctls->pvio->mysql->options.extension->tls_allow_invalid_server_cert) + { + return 0; + } + + rc= ma_tls_verify_server_cert(ctls, flags); + + /* Set error messages */ + if (!mysql->net.last_errno) + { + if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_PERIOD) + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Certificate not yet valid or expired"); + else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_FINGERPRINT) + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Fingerprint validation of peer certificate failed"); + else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_REVOKED) + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Certificate revoked"); + else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_HOST) + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Hostname verification failed"); + else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_UNKNOWN) + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Peer certificate verification failed"); + else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_TRUST) + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Peer certificate is not trusted"); + } + + return rc; } const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls) @@ -150,8 +195,7 @@ static signed char ma_hex2int(char c) static my_bool ma_pvio_tls_compare_fp(MARIADB_TLS *ctls, const char *cert_fp, - unsigned int cert_fp_len -) + unsigned int cert_fp_len) { const char fp[EVP_MAX_MD_SIZE]; unsigned int fp_len= EVP_MAX_MD_SIZE; @@ -206,8 +250,6 @@ static my_bool ma_pvio_tls_compare_fp(MARIADB_TLS *ctls, signed char d1, d2; if (*p == ':') p++; - 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) @@ -270,8 +312,8 @@ void ma_pvio_tls_set_connection(MYSQL *mysql) ma_tls_set_connection(mysql); } -unsigned int ma_pvio_tls_get_peer_cert_info(MARIADB_TLS *ctls) +unsigned int ma_pvio_tls_get_peer_cert_info(MARIADB_TLS *ctls, unsigned int size) { - return ma_tls_get_peer_cert_info(ctls); + return ma_tls_get_peer_cert_info(ctls, size); } #endif /* HAVE_TLS */ diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 68f69b5c..78195d44 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -1295,7 +1295,7 @@ mysql_init(MYSQL *mysql) } else { - memset((char*) (mysql), 0, sizeof(*(mysql))); + memset(mysql, 0, sizeof(MYSQL)); mysql->net.pvio= 0; mysql->free_me= 0; mysql->net.extension= 0; @@ -1462,7 +1462,7 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user, if (!mysql->options.extension || !mysql->options.extension->status_callback) mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, NULL, NULL); - reset_tls_self_signed_error(mysql); + reset_tls_error(mysql); /* if host contains a semicolon, we need to parse connection string */ if (host && strchr(host, ';')) @@ -2468,7 +2468,7 @@ mysql_close(MYSQL *mysql) mysql_close_memory(mysql); mysql_close_options(mysql); ma_clear_session_state(mysql); - reset_tls_self_signed_error(mysql); + reset_tls_error(mysql); if (mysql->net.extension) { @@ -4542,12 +4542,18 @@ my_bool mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ... case MARIADB_TLS_PEER_CERT_INFO: if (mysql->net.pvio->ctls) { - if (!ma_pvio_tls_get_peer_cert_info(mysql->net.pvio->ctls)) + unsigned int size; + + size= va_arg(ap, unsigned int); + if (!ma_pvio_tls_get_peer_cert_info(mysql->net.pvio->ctls, size)) *((MARIADB_X509_INFO **)arg)= (MARIADB_X509_INFO *)&mysql->net.pvio->ctls->cert_info; return 0; } *((MARIADB_X509_INFO **)arg)= NULL; break; + case MARIADB_TLS_VERIFY_STATUS: + *((unsigned int *)arg)= (unsigned int)mysql->net.tls_verify_status; + break; #endif case MARIADB_MAX_ALLOWED_PACKET: *((size_t *)arg)= (size_t)max_allowed_packet; diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index 5fcd1e92..0d17a9d5 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -33,6 +33,7 @@ #include #include #include +#include pthread_mutex_t LOCK_gnutls_config; @@ -46,8 +47,6 @@ enum ma_pem_type { MA_TLS_PEM_CRL }; -static int my_verify_callback(gnutls_session_t ssl); - char tls_library_version[TLS_VERSION_LENGTH]; struct st_cipher_map { @@ -1069,7 +1068,9 @@ static int ma_tls_set_certs(MYSQL *mysql, ssl_error= gnutls_certificate_set_x509_crl_file(ctx, mysql->options.extension->ssl_crl, GNUTLS_X509_FMT_PEM); if (ssl_error < 0) + { goto error; + } } if (!mysql->options.ssl_ca && !mysql->options.ssl_capath) @@ -1079,9 +1080,6 @@ static int ma_tls_set_certs(MYSQL *mysql, goto error; } - gnutls_certificate_set_verify_function(ctx, - my_verify_callback); - if (mysql->options.ssl_key || mysql->options.ssl_cert) { char *keyfile= mysql->options.ssl_key; @@ -1242,11 +1240,56 @@ ssize_t ma_tls_write_async(MARIADB_PVIO *pvio, const uchar *buffer, size_t lengt } } -unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls) +static gnutls_x509_crt_t ma_get_cert(MARIADB_TLS *ctls) { + MYSQL *mysql; const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + gnutls_x509_crt_t cert; + + if (!ctls || !ctls->ssl) + return 0; + + mysql= (MYSQL *)gnutls_session_get_ptr(ctls->ssl); + + cert_list = gnutls_certificate_get_peers (ctls->ssl, &cert_list_size); + if (cert_list == NULL) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Unable to get server certificate"); + return NULL; + } + + /* Check expiration */ + gnutls_x509_crt_init(&cert); + + if (!gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)) + return cert; + + return NULL; +} + +unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls, uint hash_size) +{ gnutls_session_t ssl; - unsigned int list_size= 0; + unsigned int hash_alg; + char fp[129]; + + switch (hash_size) { + case 0: + case 256: + hash_alg= MA_HASH_SHA256; + break; + case 384: + hash_alg= MA_HASH_SHA384; + break; + case 512: + hash_alg= MA_HASH_SHA512; + break; + default: + return 1; + } if (!ctls || !ctls->ssl) return 1; @@ -1254,21 +1297,14 @@ unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls) if (!(ssl = (gnutls_session_t)ctls->ssl)) return 1; - if (ctls->cert_info.version) - return 0; /* already loaded */ - /* retrieve peer certificate information */ - if ((cert_list= gnutls_certificate_get_peers(ssl, &list_size))) + if (!ctls->cert_info.version) { 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)) + if ((cert = ma_get_cert(ctls))) { size_t len= 0; - char fp[33]; time_t notBefore, notAfter; ctls->cert_info.version= gnutls_x509_crt_get_version(cert); @@ -1287,13 +1323,12 @@ unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls) 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, fp, sizeof(fp)); - mysql_hex_string(ctls->cert_info.fingerprint, fp, 32); + gnutls_x509_crt_deinit(cert); } - gnutls_x509_crt_deinit(cert); - return 0; } - return 1; + ma_tls_get_finger_print(ctls, hash_alg, fp, sizeof(fp)); + mysql_hex_string(ctls->cert_info.fingerprint, fp, ma_hash_digest_size(hash_alg)); + return 0; } @@ -1383,10 +1418,84 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) return 0; } -int ma_tls_verify_server_cert(MARIADB_TLS *ctls __attribute__((unused))) +static void set_verification_error(MYSQL *mysql, int status) { - /* server verification is already handled before during handshake */ - return 0; + gnutls_session_t ssl; + gnutls_datum_t out; + int type; + + if (!(ssl = (gnutls_session_t)mysql->net.pvio->ctls->ssl)) + return; + + type= gnutls_certificate_type_get(ssl); + gnutls_certificate_verification_status_print(status, type, &out, 0); + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), out.data); + gnutls_free(out.data); +} + +int ma_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int flags) +{ + unsigned int status= 0; + gnutls_session_t ssl; + MYSQL *mysql; + + if (!ctls || !(ssl= ctls->ssl) || !(mysql= (MYSQL *)gnutls_session_get_ptr(ssl))) + return 1; + + CLEAR_CLIENT_ERROR(mysql); + + if (flags & MARIADB_TLS_VERIFY_FINGERPRINT) + { + if (ma_pvio_tls_check_fp(ctls, mysql->options.extension->tls_fp, mysql->options.extension->tls_fp_list)) + { + mysql->net.tls_verify_status= MARIADB_TLS_VERIFY_FINGERPRINT; + goto end; + } + } + + if (gnutls_certificate_verify_peers2(ssl, &status)) + return GNUTLS_E_CERTIFICATE_ERROR; + + if (status) + { + set_verification_error(mysql, status); + if (status & GNUTLS_CERT_REVOKED) + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_REVOKED; + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + if (flags & MARIADB_TLS_VERIFY_TRUST) + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_TRUST; + if ((status & GNUTLS_CERT_NOT_ACTIVATED) || (status & GNUTLS_CERT_EXPIRED)) + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_PERIOD; + } + + if (!status && (flags & MARIADB_TLS_VERIFY_HOST)) + { + gnutls_x509_crt_t cert= ma_get_cert(ctls); + int rc; + + if (!cert) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Can't access peer certificate"); + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + goto end; + } + + rc= gnutls_x509_crt_check_hostname2(cert, mysql->host, flags); + gnutls_x509_crt_deinit(cert); + + if (!rc) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Certificate subject name doesn't match specified hostname"); + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + } + } +end: + return (mysql->net.tls_verify_status > 0); } const char *ma_tls_get_cipher(MARIADB_TLS *ctls) @@ -1404,65 +1513,13 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls) return openssl_cipher_name(kx, cipher, mac); } -static int my_verify_callback(gnutls_session_t ssl) -{ - unsigned int status= 0; - MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl); - - CLEAR_CLIENT_ERROR(mysql); - - if (!mysql->options.extension->tls_allow_invalid_server_cert) - { - const char *hostname= mysql->host; - - if (gnutls_certificate_verify_peers3 (ssl, hostname, &status) < 0) - return GNUTLS_E_CERTIFICATE_ERROR; - } else { - if (gnutls_certificate_verify_peers2 (ssl, &status) < 0) - return GNUTLS_E_CERTIFICATE_ERROR; - } - if (status & GNUTLS_CERT_INVALID) - { - gnutls_datum_t out; - int type; - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - { - /* accept self signed certificates if we don't have to verify server cert */ - if (mysql->options.extension->tls_allow_invalid_server_cert) - return 0; - - /* postpone the error for self signed certificates if CA isn't set */ - if (!mysql->options.ssl_ca && !mysql->options.ssl_capath) - { - type= gnutls_certificate_type_get(ssl); - gnutls_certificate_verification_status_print(status, type, &out, 0); - mysql->net.tls_self_signed_error= (char*)out.data; - return 0; - } - } - - /* gnutls default error message "certificate validation failed" isn't very - descriptive, so we provide more information about the error here */ - type= gnutls_certificate_type_get(ssl); - gnutls_certificate_verification_status_print(status, type, &out, 0); - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), out.data); - gnutls_free(out.data); - - return GNUTLS_E_CERTIFICATE_ERROR; - } - - return 0; -} - 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; + gnutls_digest_algorithm_t hash_alg; const gnutls_datum_t *cert_list; unsigned int cert_list_size; - gnutls_digest_algorithm_t hash_alg; if (!ctls || !ctls->ssl) return 0; @@ -1471,12 +1528,14 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp switch (hash_type) { +#ifdef WEAK_HASH case MA_HASH_SHA1: hash_alg = GNUTLS_DIG_SHA1; break; case MA_HASH_SHA224: hash_alg = GNUTLS_DIG_SHA224; break; +#endif case MA_HASH_SHA256: hash_alg = GNUTLS_DIG_SHA256; break; diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index 10ea3318..1a36abce 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -482,10 +482,10 @@ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, } /* }}} */ #include "win32_errmsg.h" -my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name) +unsigned int ma_schannel_verify_certs(MARIADB_TLS *ctls, unsigned int verify_flags) { SECURITY_STATUS status; - + unsigned int verify_status = MARIADB_TLS_VERIFY_ERROR; MARIADB_PVIO *pvio= ctls->pvio; MYSQL *mysql= pvio->mysql; SC_CTX *sctx = (SC_CTX *)ctls->ssl; @@ -498,6 +498,12 @@ my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name) HCERTSTORE store= NULL; int ret= 0; + if (verify_flags & MARIADB_TLS_VERIFY_FINGERPRINT) + { + if (ma_pvio_tls_check_fp(ctls, mysql->options.extension->tls_fp, mysql->options.extension->tls_fp_list)) + return MARIADB_TLS_VERIFY_FINGERPRINT; + } + status = schannel_create_store(ca_file, ca_path, crl_file, crl_path, &store, errmsg, sizeof(errmsg)); if(status) goto end; @@ -513,33 +519,48 @@ my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name) status = schannel_verify_server_certificate( pServerCert, store, - crl_file != 0 || crl_path != 0, mysql->host, - verify_server_name, + verify_flags, errmsg, sizeof(errmsg)); if (status) goto end; + verify_status= MARIADB_TLS_VERIFY_OK; + ret= 1; end: if (!ret) { - /* postpone the error for self signed certificates if CA isn't set */ - if (status == CERT_E_UNTRUSTEDROOT && !ca_file && !ca_path) - { - mysql->net.tls_self_signed_error= strdup(errmsg); - ret= 1; + switch (status) { + case CERT_E_UNTRUSTEDROOT: + if ((verify_flags & MARIADB_TLS_VERIFY_TRUST)) + { + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_TRUST; + } + break; + case CERT_E_EXPIRED: + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_PERIOD; + break; + case CRYPT_E_REVOKED: + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_REVOKED; + break; + case CERT_E_INVALID_NAME: + case CERT_E_CN_NO_MATCH: + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + break; + default: + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_ERROR; + break; } - else - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, errmsg); + pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, errmsg); } if (pServerCert) CertFreeCertificateContext(pServerCert); if(store) schannel_free_store(store); - return ret; + return verify_status; } diff --git a/libmariadb/secure/ma_schannel.h b/libmariadb/secure/ma_schannel.h index 562dae1f..dca62f45 100644 --- a/libmariadb/secure/ma_schannel.h +++ b/libmariadb/secure/ma_schannel.h @@ -43,6 +43,11 @@ #include #include +#include +#include +#include +#include + #define SC_IO_BUFFER_SIZE 0x4000 @@ -75,7 +80,7 @@ extern my_bool ca_Check, crl_Check; SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls); SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData); -my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name); +unsigned int ma_schannel_verify_certs(MARIADB_TLS *ctls, unsigned int verify_flags); ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, uchar *WriteBuffer, size_t WriteBufferSize); diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 8231a244..b5b573a9 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -33,6 +33,7 @@ #if OPENSSL_VERSION_NUMBER < 0x10100000L #include #endif +#include #if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C) #include @@ -87,6 +88,7 @@ static int ma_bio_write(BIO *h, const char *buf, int size); static BIO_METHOD ma_BIO_method; #endif +static int ma_verification_callback(int preverify_ok, X509_STORE_CTX *ctx); static long ma_tls_version_options(const char *version) { @@ -459,55 +461,70 @@ error: return NULL; } -unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls) +unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls, uint hash_size) { X509 *cert; + unsigned int hash_alg; SSL *ssl; + char fp[129]; + + switch (hash_size) { + case 0: + case 256: + hash_alg= MA_HASH_SHA256; + break; + case 384: + hash_alg= MA_HASH_SHA384; + break; + case 512: + hash_alg= MA_HASH_SHA512; + break; + default: + return 1; + } if (!ctls || !ctls->ssl) return 1; - /* Did we already read peer cert information ? */ - if (ctls->cert_info.version) - return 0; - ssl= (SSL *)ctls->ssl; /* Store peer certificate information */ - if ((cert= SSL_get_peer_certificate(ssl))) + if (!ctls->cert_info.version) { - char fp[33]; -#if OPENSSL_VERSION_NUMBER >= 0x10101000L - const ASN1_TIME *not_before= X509_get0_notBefore(cert), - *not_after= X509_get0_notAfter(cert); - 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); -#else - const ASN1_TIME *not_before= X509_get_notBefore(cert), - *not_after= X509_get_notAfter(cert); - time_t now, from, to; - int pday, psec; - /* ANS1_TIME_diff returns days and seconds between now and the - specified ASN1_TIME */ - time(&now); - ASN1_TIME_diff(&pday, &psec, not_before, NULL); - from= now - (pday * 86400 + psec); - gmtime_r(&from, &ctls->cert_info.not_before); - ASN1_TIME_diff(&pday, &psec, NULL, not_after); - to= now + (pday * 86400 + psec); - gmtime_r(&to, &ctls->cert_info.not_after); -#endif - 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; - - ma_tls_get_finger_print(ctls, MA_HASH_SHA256, fp, 33); - mysql_hex_string(ctls->cert_info.fingerprint, fp, 32); - - X509_free(cert); - return 0; + if ((cert= SSL_get_peer_certificate(ssl))) + { + #if OPENSSL_VERSION_NUMBER >= 0x10101000L + const ASN1_TIME *not_before= X509_get0_notBefore(cert), + *not_after= X509_get0_notAfter(cert); + 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); + #else + const ASN1_TIME *not_before= X509_get_notBefore(cert), + *not_after= X509_get_notAfter(cert); + time_t now, from, to; + int pday, psec; + /* ANS1_TIME_diff returns days and seconds between now and the + specified ASN1_TIME */ + time(&now); + ASN1_TIME_diff(&pday, &psec, not_before, NULL); + from= now - (pday * 86400 + psec); + gmtime_r(&from, &ctls->cert_info.not_before); + ASN1_TIME_diff(&pday, &psec, NULL, not_after); + to= now + (pday * 86400 + psec); + gmtime_r(&to, &ctls->cert_info.not_after); + #endif + 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; + X509_free(cert); + } + else + return 1; } - return 1; + ma_tls_get_finger_print(ctls, hash_alg, fp, sizeof(fp)); + mysql_hex_string(ctls->cert_info.fingerprint, fp, (unsigned long)ma_hash_digest_size(hash_alg)); + + return 0; } my_bool ma_tls_connect(MARIADB_TLS *ctls) @@ -539,6 +556,8 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) #else SSL_set_fd(ssl, (int)mysql_get_socket(mysql)); #endif + if (!mysql->options.extension->tls_allow_invalid_server_cert) + SSL_set_verify(ssl, SSL_VERIFY_PEER, ma_verification_callback); while (try_connect && (rc= SSL_connect(ssl)) == -1) { @@ -556,31 +575,12 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) } } - /* In case handshake failed or if a root certificate (ca) was specified, - we need to check the result code of X509 verification. A detailed check - of the peer certificate (hostname checking will follow later) */ - if (rc != 1 || !mysql->options.extension->tls_allow_invalid_server_cert || - mysql->options.ssl_ca || mysql->options.ssl_capath) + if (rc != 1) { - long x509_err= SSL_get_verify_result(ssl); - if ((x509_err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || - x509_err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && rc == 1 && - !mysql->options.ssl_ca && !mysql->options.ssl_capath) - mysql->net.tls_self_signed_error= X509_verify_cert_error_string(x509_err); - else if (x509_err != X509_V_OK) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(x509_err)); - /* restore blocking mode */ - if (!blocking) - pvio->methods->blocking(pvio, FALSE, 0); - - return 1; - } else if (rc != 1) { - ma_tls_set_error(mysql); - return 1; - } + ma_tls_set_error(mysql); + return 1; } + pvio->ctls->ssl= ctls->ssl= (void *)ssl; return 0; @@ -714,9 +714,40 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) return rc; } -int ma_tls_verify_server_cert(MARIADB_TLS *ctls) +/** Check for possible errors, and store the result in net.tls_verify_status. + verification will happen after handshake by ma_tls_verify_server_cert(). + To retrieve all errors, this callback function returns always true. + (By default OpenSSL stops verification after first error +*/ +static int ma_verification_callback(int preverify_ok __attribute__((unused)), X509_STORE_CTX *ctx) { - X509 *cert; + SSL *ssl; + + int x509_err= X509_STORE_CTX_get_error(ctx); + + if ((ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()))) + { + MYSQL *mysql= (MYSQL *)SSL_get_app_data(ssl); + + if ((x509_err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + x509_err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)) + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_TRUST; + else if (x509_err == X509_V_ERR_CERT_REVOKED) + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_REVOKED; + else if (x509_err == X509_V_ERR_CERT_NOT_YET_VALID || + x509_err == X509_V_ERR_CERT_HAS_EXPIRED) + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_PERIOD; + else if (x509_err != X509_V_OK) + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_UNKNOWN; + } + + /* continue verification */ + return 1; +} + +int ma_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int verify_flags) +{ + X509 *cert= NULL; MYSQL *mysql; SSL *ssl; MARIADB_PVIO *pvio; @@ -734,52 +765,97 @@ int ma_tls_verify_server_cert(MARIADB_TLS *ctls) mysql= (MYSQL *)SSL_get_app_data(ssl); pvio= mysql->net.pvio; - if (!mysql->host) + if (verify_flags & MARIADB_TLS_VERIFY_FINGERPRINT) + { + if (ma_pvio_tls_check_fp(ctls, mysql->options.extension->tls_fp, mysql->options.extension->tls_fp_list)) + { + mysql->net.tls_verify_status |= MARIADB_TLS_VERIFY_FINGERPRINT; + return 1; + } + + /* if certificates are valid and no revocation error occured, + we can return */ + if (!(mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_PERIOD) && + !(mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_REVOKED)) + { + mysql->net.tls_verify_status= MARIADB_TLS_VERIFY_OK; + return 0; + } + } + + if (mysql->net.tls_verify_status & verify_flags) { - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), "Invalid (empty) hostname"); return 1; } - if (!(cert= SSL_get_peer_certificate(ssl))) + if (verify_flags & MARIADB_TLS_VERIFY_HOST) { - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate"); - return 1; + if (!mysql->host) + { + pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), "Invalid (empty) hostname"); + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + return MARIADB_TLS_VERIFY_ERROR; + } + + if (!(cert= SSL_get_peer_certificate(ssl))) + { + pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate"); + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + return MARIADB_TLS_VERIFY_ERROR; + } + + #ifdef HAVE_OPENSSL_CHECK_HOST + if (X509_check_host(cert, mysql->host, strlen(mysql->host), 0, 0) != 1 + && X509_check_ip_asc(cert, mysql->host, 0) != 1) + { + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + goto error; + } + #else + x509sn= X509_get_subject_name(cert); + + if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0) + { + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + goto error; + } + + if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos))) + { + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + goto error; + } + + if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry))) + { + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + goto error; + } + + cn_str = (char *)ASN1_STRING_data(cn_asn1); + + /* Make sure there is no embedded \0 in the CN */ + if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str)) + { + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + goto error; + } + + if (strcmp(cn_str, mysql->host)) + { + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_HOST; + goto error; + } + #endif + X509_free(cert); } -#ifdef HAVE_OPENSSL_CHECK_HOST - if (X509_check_host(cert, mysql->host, strlen(mysql->host), 0, 0) != 1 - && X509_check_ip_asc(cert, mysql->host, 0) != 1) - goto error; -#else - x509sn= X509_get_subject_name(cert); - - if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0) - goto error; - - if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos))) - goto error; - - if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry))) - goto error; - - cn_str = (char *)ASN1_STRING_data(cn_asn1); - - /* Make sure there is no embedded \0 in the CN */ - if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str)) - goto error; - - if (strcmp(cn_str, mysql->host)) - goto error; -#endif - X509_free(cert); - return 0; error: - X509_free(cert); + if (cert) + X509_free(cert); - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), "Validation of SSL server certificate failed"); return 1; } @@ -854,7 +930,7 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp "invalid finger print of server certificate"); goto end; } - + X509_free(cert); return (fp_len); end: diff --git a/libmariadb/secure/schannel.c b/libmariadb/secure/schannel.c index b71d9cf1..78da1f67 100644 --- a/libmariadb/secure/schannel.c +++ b/libmariadb/secure/schannel.c @@ -32,6 +32,8 @@ char tls_library_version[] = "Schannel"; #define PROT_TLS1_2 4 #define PROT_TLS1_3 8 +static int ma_check_peer_cert_time(MARIADB_TLS *ctls); + static struct { DWORD cipher_id; @@ -374,7 +376,6 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) ALG_ID AlgId[MAX_ALG_ID]; size_t i; DWORD protocol = 0; - int verify_certs; const CERT_CONTEXT* cert_context = NULL; if (!ctls) @@ -448,14 +449,6 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) if (ma_schannel_client_handshake(ctls) != SEC_E_OK) goto end; - verify_certs = mysql->options.ssl_ca || mysql->options.ssl_capath || - !mysql->options.extension->tls_allow_invalid_server_cert; - - if (verify_certs) - { - if (!ma_schannel_verify_certs(ctls, !mysql->options.extension->tls_allow_invalid_server_cert)) - goto end; - } rc = 0; end: @@ -516,10 +509,34 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) } /* }}} */ -int ma_tls_verify_server_cert(MARIADB_TLS *ctls) +int ma_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int verify_flags) { - /* Done elsewhere */ - return 0; + MYSQL *mysql; + SC_CTX *sctx; + if (!ctls || !ctls->ssl || !ctls->pvio || !ctls->pvio->mysql) + return 1; + + sctx= (SC_CTX *)ctls->ssl; + mysql= ctls->pvio->mysql; + + if (verify_flags & MARIADB_TLS_VERIFY_PERIOD) + { + if (ma_check_peer_cert_time(ctls)) + { + mysql->net.tls_verify_status|= MARIADB_TLS_VERIFY_PERIOD; + return 1; + } + } + + if (verify_flags & MARIADB_TLS_VERIFY_FINGERPRINT) + { + if (ma_pvio_tls_check_fp(ctls, mysql->options.extension->tls_fp, mysql->options.extension->tls_fp_list)) + { + mysql->net.tls_verify_status |= MARIADB_TLS_VERIFY_FINGERPRINT; + return 1; + } + } + return ma_schannel_verify_certs(ctls, verify_flags); } static const char *cipher_name(const SecPkgContext_CipherInfo *CipherInfo) @@ -566,6 +583,34 @@ unsigned char *ma_cert_blob_to_str(PCERT_NAME_BLOB cnblob) return str; } +static int ma_check_peer_cert_time(MARIADB_TLS *ctls) +{ + PCCERT_CONTEXT pCertCtx= NULL; + SC_CTX *sctx; + PCERT_INFO pci= NULL; + FILETIME ft; + SYSTEMTIME st; + + if (!ctls || !ctls->ssl || !ctls->pvio || !ctls->pvio->mysql) + return 1; + + sctx= (SC_CTX *)ctls->ssl; + + if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pCertCtx) != SEC_E_OK) + return 1; + + pci= pCertCtx->pCertInfo; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + + if (CompareFileTime(&ft, &pci->NotBefore) == -1 || + CompareFileTime(&pci->NotAfter, &ft) == -1) + return 1; + + return 0; +} + static void ma_systime_to_tm(SYSTEMTIME sys_tm, struct tm *tm) { memset(tm, 0, sizeof(struct tm)); @@ -576,38 +621,58 @@ static void ma_systime_to_tm(SYSTEMTIME sys_tm, struct tm *tm) tm->tm_min = sys_tm.wMinute; } -unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls) +unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls, unsigned int hash_size) { PCCERT_CONTEXT pCertCtx= NULL; - SC_CTX *sctx= (SC_CTX *)ctls->ssl; + SC_CTX *sctx; PCERT_INFO pci= NULL; DWORD size´= 0; SYSTEMTIME tm; - char fp[33]; + char fp[129]; + unsigned int hash_alg; - if (!ctls || !sctx) + if (!ctls || !ctls->ssl || !ctls->pvio || !ctls->pvio->mysql) return 1; + sctx= (SC_CTX *)ctls->ssl; + + switch (hash_size) { + case 0: + case 256: + hash_alg= MA_HASH_SHA256; + break; + case 384: + hash_alg= MA_HASH_SHA384; + break; + case 512: + hash_alg= MA_HASH_SHA512; + break; + default: + my_set_error(ctls->pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Cannot detect hash algorithm for fingerprint verification"); + return 1; + } + /* Did we already read peer cert information ? */ - if (ctls->cert_info.version) - return 0; + if (!ctls->cert_info.version) + { + if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pCertCtx) != SEC_E_OK) + return 1; - if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pCertCtx) != SEC_E_OK) - return 1; + pci= pCertCtx->pCertInfo; - 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); - 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); + 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, hash_alg, fp, sizeof(fp)); + mysql_hex_string(ctls->cert_info.fingerprint, fp, (unsigned long)ma_hash_digest_size(hash_alg)); return 0; } diff --git a/libmariadb/secure/schannel_certs.c b/libmariadb/secure/schannel_certs.c index c8ff3753..b167f74d 100644 --- a/libmariadb/secure/schannel_certs.c +++ b/libmariadb/secure/schannel_certs.c @@ -33,6 +33,7 @@ #define _WIN32_WINNT 0x0601 #endif #include "schannel_certs.h" +#include "ma_schannel.h" #include #include #include @@ -594,9 +595,8 @@ Verify server certificate against a wincrypt store SECURITY_STATUS schannel_verify_server_certificate( const CERT_CONTEXT* cert, HCERTSTORE store, - BOOL check_revocation, - const char* server_name, - BOOL check_server_name, + const char *server_name, + unsigned int verify_flags, char* errmsg, size_t errmsg_len) { @@ -605,7 +605,7 @@ SECURITY_STATUS schannel_verify_server_certificate( DWORD dwVerifyFlags; DWORD dwRevocationFlags; - if (check_server_name) + if (verify_flags & MARIADB_TLS_VERIFY_HOST) { int cchServerName = (int)strlen(server_name) + 1; wserver_name = (wchar_t*)LocalAlloc(0,sizeof(wchar_t) * cchServerName); @@ -621,10 +621,14 @@ SECURITY_STATUS schannel_verify_server_certificate( dwVerifyFlags = 0; dwRevocationFlags = 0; - if (check_revocation) + if (verify_flags & MARIADB_TLS_VERIFY_REVOKED) dwRevocationFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; - if (!check_server_name) + if (!(verify_flags & MARIADB_TLS_VERIFY_HOST)) dwVerifyFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID; + /* Period was already checked before */ + dwVerifyFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; + if (!(verify_flags & MARIADB_TLS_VERIFY_TRUST)) + dwVerifyFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA; status = VerifyServerCertificate(cert, store, wserver_name ? wserver_name : L"SERVER_NAME", dwRevocationFlags, dwVerifyFlags, errmsg, errmsg_len); diff --git a/libmariadb/secure/schannel_certs.h b/libmariadb/secure/schannel_certs.h index 67a8d11e..372a8a3d 100644 --- a/libmariadb/secure/schannel_certs.h +++ b/libmariadb/secure/schannel_certs.h @@ -35,9 +35,8 @@ extern SECURITY_STATUS schannel_create_store( extern SECURITY_STATUS schannel_verify_server_certificate( const CERT_CONTEXT* cert, HCERTSTORE store, - BOOL check_revocation, const char* server_name, - BOOL check_server_name, + unsigned int verify_flags, char* errmsg, size_t errmsg_len); diff --git a/plugins/auth/my_auth.c b/plugins/auth/my_auth.c index faea968c..e7429fae 100644 --- a/plugins/auth/my_auth.c +++ b/plugins/auth/my_auth.c @@ -18,14 +18,7 @@ extern unsigned char *mysql_net_store_length(unsigned char *packet, ulonglong le extern const char *disabled_plugins; #define hashing(p) (p->interface_version >= 0x0101 && p->hash_password_bin) - -static int set_error_from_tls_self_signed_error(MYSQL *mysql) -{ - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), mysql->net.tls_self_signed_error); - reset_tls_self_signed_error(mysql); - return 1; -} +#define password_and_hashing(m,p) ((m)->passwd && (m)->passwd[0] && hashing((p))) typedef struct { int (*read_packet)(struct st_plugin_vio *vio, uchar **buf); @@ -67,6 +60,49 @@ auth_plugin_t mysql_native_password_client_plugin= native_password_hash }; +/** + Checks if self-signed certificate error should be ignored. +*/ +static my_bool is_local_connection(MARIADB_PVIO *pvio) +{ + const char *hostname= pvio->mysql->host; + const char *local_host_names[]= { +#ifdef _WIN32 + /* + On Unix, we consider TCP connections with "localhost" + an insecure transport, for the single reason to run tests for + insecure transport on CI.This is artificial, but should be ok. + Default client connections use unix sockets anyway, so it + would not hurt much. + + On Windows, the situation is quite different. + Default connections type is TCP, default host name is "localhost", + non-password plugin gssapi is common (every installation) + In this environment, there would be a lot of faux/disruptive + "self-signed certificates" errors there. Thus, "localhost" TCP + needs to be considered secure transport. + */ + "localhost", +#endif + "127.0.0.1", "::1", NULL}; + int i; + + if (pvio->type != PVIO_TYPE_SOCKET) + { + return TRUE; + } + if (!hostname) + return FALSE; + for (i= 0; local_host_names[i]; i++) + { + if (strcmp(hostname, local_host_names[i]) == 0) + { + return TRUE; + } + } + return FALSE; +} + static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { @@ -370,6 +406,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (mysql->options.use_ssl && (mysql->client_flag & CLIENT_SSL)) { + unsigned int verify_flags= 0; /* Send mysql->client_flag, max_packet_size - unencrypted otherwise the server does not know we want to do SSL @@ -382,14 +419,30 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, errno); goto error; } + mysql->net.tls_verify_status = 0; if (ma_pvio_start_ssl(mysql->net.pvio)) goto error; - if (mysql->net.tls_self_signed_error && - (!mysql->passwd || !mysql->passwd[0] || !hashing(mpvio->plugin))) + + verify_flags= MARIADB_TLS_VERIFY_PERIOD | MARIADB_TLS_VERIFY_REVOKED; + if (have_fingerprint(mysql)) { - /* cannot use auth to validate the cert */ - set_error_from_tls_self_signed_error(mysql); - goto error; + verify_flags|= MARIADB_TLS_VERIFY_FINGERPRINT; + } else { + verify_flags|= MARIADB_TLS_VERIFY_TRUST | MARIADB_TLS_VERIFY_HOST; + } + + if (ma_pvio_tls_verify_server_cert(mysql->net.pvio->ctls, verify_flags) > MARIADB_TLS_VERIFY_OK) + { + if (mysql->net.tls_verify_status > MARIADB_TLS_VERIFY_TRUST || + (mysql->options.ssl_ca || mysql->options.ssl_capath)) + goto error; + + if (is_local_connection(mysql->net.pvio)) + { + CLEAR_CLIENT_ERROR(mysql); + } + else if (!password_and_hashing(mysql, mpvio->plugin)) + goto error; } } #endif /* HAVE_TLS */ @@ -769,8 +822,14 @@ retry: auth_plugin= &dummy_fallback_client_plugin; /* can we use this plugin with this tls server cert ? */ - if (mysql->net.tls_self_signed_error && !hashing(auth_plugin)) - return set_error_from_tls_self_signed_error(mysql); + if ((mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_TRUST) && + !password_and_hashing(mysql, auth_plugin)) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Certificate verification failure: The certificate is NOT trusted."); + return 1; + } goto retry; } /* @@ -782,7 +841,9 @@ retry: if (ma_read_ok_packet(mysql, mysql->net.read_pos + 1, pkt_length)) return -1; - if (!mysql->net.tls_self_signed_error) + if (!mysql->net.tls_verify_status || + ((mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_TRUST) && + is_local_connection(mysql->net.pvio))) return 0; assert(mysql->options.use_ssl); @@ -824,6 +885,9 @@ retry: return 0; /* phew. self-signed certificate is validated! */ } - return set_error_from_tls_self_signed_error(mysql); + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Certificate verification failure: The certificate is NOT trusted."); + return 1; } diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index d5b93716..e84230d3 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -19,6 +19,7 @@ ENDIF() ENABLE_TESTING() +find_package (Python3 COMPONENTS Interpreter) INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include ${CC_BINARY_DIR}/include @@ -26,6 +27,7 @@ INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include ${CC_SOURCE_DIR}/unittest/libmariadb) ADD_DEFINITIONS(-DLIBMARIADB) + SET(API_TESTS "conc336" "bulk1" "performance" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs" "sp" "result" "connection" "misc" "ps_new" "thread" "features-10_2") IF(WITH_DYNCOL) SET(API_TESTS ${API_TESTS} "dyncol") @@ -50,6 +52,15 @@ ENDIF() ADD_LIBRARY(ma_getopt ma_getopt.c) +IF(${Python3_FOUND}) + CONFIGURE_FILE(${CC_SOURCE_DIR}/unittest/libmariadb/tls.c.in + ${CC_BINARY_DIR}/unittest/libmariadb/tls.c) + + ADD_EXECUTABLE(tls ${CC_BINARY_DIR}/unittest/libmariadb/tls.c) + TARGET_LINK_LIBRARIES(tls cctap ma_getopt mariadbclient) + ADD_TEST(tls ${EXECUTABLE_OUTPUT_PATH}/tls) +ENDIF() + FOREACH(API_TEST ${API_TESTS}) IF (NOT TARGET ${API_TEST}) ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c) @@ -63,7 +74,17 @@ FOREACH(API_TEST ${API_TESTS}) ENDIF() ENDFOREACH(API_TEST) + FOREACH(API_TEST ${MANUAL_TESTS}) ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c) TARGET_LINK_LIBRARIES(${API_TEST} cctap ma_getopt mariadbclient) ENDFOREACH() + +SET(ADDITIONAL_FILES "tls_server.py" "certs/server-cert.pem" + "certs/server-cert.crl" "certs/server-key.pem" "certs/cacert.pem") +FOREACH(FILE ${ADDITIONAL_FILES}) + CONFIGURE_FILE(${CC_SOURCE_DIR}/unittest/libmariadb/${FILE} + ${CC_BINARY_DIR}/unittest/libmariadb/${FILE} + COPYONLY) +ENDFOREACH() + diff --git a/unittest/libmariadb/basic-t.c b/unittest/libmariadb/basic-t.c index 40a44962..46440adb 100644 --- a/unittest/libmariadb/basic-t.c +++ b/unittest/libmariadb/basic-t.c @@ -44,7 +44,7 @@ static int test_conc75(MYSQL *my) mysql= mysql_init(NULL); mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); - my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0| CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS); + my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0| CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS, 1); rc= mysql_query(mysql, "DROP TABLE IF EXISTS a"); check_mysql_rc(rc, mysql); @@ -85,7 +85,7 @@ static int test_conc74(MYSQL *unused __attribute__((unused))) mysql= mysql_init(NULL); - if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0| CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS)) + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0| CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS, 1)) { diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -128,7 +128,7 @@ static int test_conc71(MYSQL *my) mysql_options(mysql, MYSQL_INIT_COMMAND, "/*!40101 set @@session.wait_timeout=28800 */"); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0), mysql_error(my)); + port, socketname, 0, 1), mysql_error(my)); diag("kill server"); @@ -161,7 +161,7 @@ static int test_conc70(MYSQL *my) mysql_options(mysql, MYSQL_OPT_COMPRESS, (void *)1); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0), mysql_error(my)); + port, socketname, 0, 1), mysql_error(my)); rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); check_mysql_rc(rc, mysql); @@ -223,7 +223,7 @@ static int test_conc68(MYSQL *my) mysql_query(my, "SET global max_allowed_packet=1024*1024*22"); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0), mysql_error(my)); + port, socketname, 0, 1), mysql_error(my)); rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); check_mysql_rc(rc, mysql); @@ -274,7 +274,7 @@ static int basic_connect(MYSQL *unused __attribute__((unused))) FAIL_IF(!my, "mysql_init() failed"); FAIL_IF(!my_test_connect(my, hostname, username, password, schema, - port, socketname, 0), mysql_error(my)); + port, socketname, 0, 1), mysql_error(my)); rc= mysql_query(my, "SELECT @@version"); check_mysql_rc(rc, my); @@ -671,7 +671,7 @@ static int test_status(MYSQL *mysql) static int bug_conc1(MYSQL *mysql) { my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0); + port, socketname, 0, 1); diag("errno: %d", mysql_errno(mysql)); FAIL_IF(mysql_errno(mysql) != CR_ALREADY_CONNECTED, "Expected errno=CR_ALREADY_CONNECTED"); @@ -688,7 +688,7 @@ static int test_options_initcmd(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_INIT_COMMAND, "INSERT INTO t1 VALUES (1),(2),(3)"); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, port, socketname, - CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS), mysql_error(mysql)); + CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS, 1), mysql_error(mysql)); rc= mysql_query(mysql, "SELECT a FROM t1"); check_mysql_rc(rc, mysql); @@ -734,7 +734,7 @@ static int test_reconnect_maxpackage(MYSQL *unused __attribute__((unused))) FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, port, socketname, - CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS), mysql_error(mysql)); + CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS, 1), mysql_error(mysql)); mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); rc= mysql_query(mysql, "SELECT @@max_allowed_packet"); @@ -792,7 +792,7 @@ static int test_compressed(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_OPT_COMPRESS, (void *)1); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, port, socketname, - CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS), mysql_error(mysql)); + CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS, 1), mysql_error(mysql)); mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); rc= mysql_query(mysql, "SHOW VARIABLES"); diff --git a/unittest/libmariadb/bulk1.c b/unittest/libmariadb/bulk1.c index 56ae20d8..2b00bdde 100644 --- a/unittest/libmariadb/bulk1.c +++ b/unittest/libmariadb/bulk1.c @@ -1106,7 +1106,7 @@ static int bulk_with_unit_result_insert(MYSQL *my) stmt= mysql_stmt_init(mysql); mysql_options(mysql, MARIADB_OPT_BULK_UNIT_RESULTS, &unique_result); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0), mysql_error(mysql)); + port, socketname, 0, 1), mysql_error(mysql)); mysql_get_option(mysql, MARIADB_OPT_BULK_UNIT_RESULTS, &bool_val); FAIL_UNLESS(bool_val, "bool_val != true"); @@ -1223,7 +1223,7 @@ static int bulk_with_unit_result_delete(MYSQL *my) stmt= mysql_stmt_init(mysql); mysql_options(mysql, MARIADB_OPT_BULK_UNIT_RESULTS, &unique_result); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0), mysql_error(mysql)); + port, socketname, 0, 1), mysql_error(mysql)); if (!bulk_enabled) return SKIP; @@ -1331,7 +1331,7 @@ static int bulk_with_unit_result_update(MYSQL *my) mysql_options(mysql, MARIADB_OPT_BULK_UNIT_RESULTS, &unique_result); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0), mysql_error(mysql)); + port, socketname, 0, 1), mysql_error(mysql)); if (!bulk_enabled) return SKIP; diff --git a/unittest/libmariadb/certs/cacert.pem b/unittest/libmariadb/certs/cacert.pem new file mode 100755 index 00000000..16d2d68c --- /dev/null +++ b/unittest/libmariadb/certs/cacert.pem @@ -0,0 +1,121 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 5c:81:fd:b9:82:69:e0:46:8d:99:ed:d0:95:4b:d3:d6:62:8a:69:97 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN = cacert, C = FI, ST = Helsinki, L = Helsinki, O = MariaDB + Validity + Not Before: Mar 3 03:03:03 2020 GMT + Not After : Feb 27 03:03:03 2040 GMT + Subject: CN = cacert, C = FI, ST = Helsinki, L = Helsinki, O = MariaDB + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (4096 bit) + Modulus: + 00:bc:72:01:10:68:c1:df:fa:d1:77:55:7c:09:13: + 19:51:41:cd:ed:4f:51:43:22:e7:97:0e:94:fc:0d: + 1d:a9:c6:f3:17:79:31:6f:12:7a:9b:7f:87:e2:55: + 16:d5:a4:dd:6a:d3:5e:4f:e2:e0:bc:c4:9f:c5:95: + 1a:47:13:a5:cd:6d:f3:76:e2:9a:12:31:76:e4:f1: + 01:94:37:45:10:32:18:9e:cc:d8:7f:04:ca:18:97: + bf:22:2e:51:ba:73:6f:46:50:36:fe:df:82:4c:8a: + 9e:34:be:44:f4:12:61:72:18:b3:81:b5:d9:b2:69: + 8f:7d:f4:8b:41:62:34:59:71:f7:e4:26:a6:cf:55: + 4d:d4:52:a3:37:24:bd:82:21:9f:d1:db:cd:e9:12: + a9:cf:60:48:3d:da:ea:a0:a7:d0:57:16:68:49:e5: + b4:e8:dc:d0:b0:12:b3:c7:1c:f1:b3:b1:eb:12:8b: + c7:3e:3c:02:fe:f6:4c:a8:35:d8:cf:74:64:63:89: + 84:3e:75:88:a0:0b:80:5c:ea:68:b2:9c:bb:e3:ae: + 4f:cd:24:b9:97:89:95:4e:3f:78:48:ba:ad:97:4c: + bf:f8:8b:14:80:f4:78:03:11:4b:a7:e3:99:1c:82: + 59:bf:b6:f3:3e:3e:db:db:38:73:91:8d:f0:6f:e5: + 5d:02:e7:c0:a5:8b:ad:69:f1:17:29:2c:16:41:ad: + ac:0f:64:c8:f7:62:58:da:73:66:f7:91:2c:89:f7: + 80:6b:d7:91:f1:c8:4a:5a:c4:2d:3d:0b:18:83:f1: + e9:74:2d:83:bb:09:46:bd:f0:40:ad:0b:dd:0c:45: + 64:8d:9e:cf:a7:a0:6c:d2:cc:f1:f9:05:15:7a:e3: + 02:15:b0:e6:99:cd:81:5b:dd:52:9b:c0:35:25:fa: + 4c:88:47:1d:16:19:9f:9d:f0:54:01:6e:20:6a:10: + ab:4f:d2:a2:a7:31:db:6d:76:26:0d:35:9d:ac:1f: + 4e:4d:37:87:2c:78:26:d4:1f:f0:87:fc:f4:e2:2a: + af:9a:a4:d6:69:93:16:4f:ed:fb:e0:78:99:36:49: + d2:2e:7d:44:15:94:58:bd:ba:bc:d3:b9:75:5c:58: + 13:69:18:8e:5d:0e:c9:0c:dd:06:c4:7b:fc:34:3e: + df:51:46:8d:5e:33:40:ba:39:75:9a:29:a5:f0:be: + d3:a1:2c:72:88:85:4a:a0:2e:d9:71:15:30:49:57: + f4:1b:73:e4:b3:b1:9e:3e:e6:0b:30:0a:eb:e9:cc: + ec:98:4c:36:ea:58:8a:aa:b1:4b:b9:e1:5e:4a:d0: + 81:65:f0:ba:2c:b0:a6:8e:02:f1:c6:e0:78:b9:49: + eb:8e:79 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 4D:FC:7A:19:F3:2B:0C:7D:F6:C0:7C:4D:F8:72:34:4C:8C:35:52:74 + X509v3 Authority Key Identifier: + keyid:4D:FC:7A:19:F3:2B:0C:7D:F6:C0:7C:4D:F8:72:34:4C:8C:35:52:74 + + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + 3c:e7:3a:e8:b6:d1:be:ca:27:ad:02:00:08:b7:1f:2d:e9:cd: + 81:9f:98:f0:f0:a4:c5:dd:dc:94:a1:a6:bd:31:56:54:05:6a: + da:b1:1b:42:35:ef:f8:75:1c:0d:0f:00:77:24:a7:ef:5f:f1: + ce:38:3a:05:a7:22:a6:40:b6:6c:57:46:31:e8:ba:2f:b5:9e: + fb:7c:07:93:5c:cb:82:59:1a:2f:13:5b:6d:55:8d:d9:dd:ec: + 44:b2:7f:89:26:37:da:fc:e8:aa:af:87:8b:5e:c3:3e:21:c2: + a0:ca:2a:49:78:fa:9d:10:5c:e8:2c:24:ac:e8:f3:61:25:cb: + 86:6c:8f:1d:4c:2f:83:bd:d4:72:dc:54:1b:fa:3d:1a:d4:54: + a9:ac:87:70:ff:cf:53:26:1f:e7:11:bc:b3:89:d0:71:25:09: + d9:80:09:42:b1:5a:8d:01:a9:89:1e:2c:cc:89:e7:f8:c2:43: + 4b:20:cd:ce:9c:2f:67:99:35:4f:e2:ec:00:07:3d:69:6e:9e: + 48:93:d4:56:b1:0b:52:00:d9:86:05:1e:ea:64:68:a1:87:04: + 0f:17:45:82:8e:f5:13:7a:90:79:c8:fd:42:e8:80:df:3b:46: + bc:39:c9:76:14:90:8e:cd:80:98:84:56:43:88:33:1e:ac:4a: + cb:48:8d:ff:35:d4:68:0e:ef:bf:1a:b5:34:17:7a:c8:c2:e8: + f0:18:f6:58:07:9b:09:9f:b5:e2:29:e2:5e:cb:b9:71:ce:13: + 90:6d:b7:4e:23:31:bc:ab:17:b4:3d:96:17:4b:f8:01:49:b9: + 0a:0c:73:1d:54:de:51:79:3a:fb:f0:b5:10:8d:38:ad:87:36: + 0f:36:48:11:f7:1b:d6:2f:ed:a7:1d:73:74:45:19:69:1d:9f: + 86:ec:a3:2b:8e:fd:5a:33:08:4d:17:41:df:57:8a:1e:99:c1: + bc:aa:15:a1:af:2c:23:67:4e:31:45:91:fb:8f:af:1e:19:a6: + 0f:bb:d9:3a:55:cc:b0:a2:41:a5:99:20:cc:64:91:f7:8e:6b: + c4:ad:5a:c8:ca:3d:74:36:91:97:34:f1:f8:c5:22:87:6b:e3: + cf:9c:0a:75:5a:09:30:88:9d:07:99:2d:ef:87:75:45:ed:0f: + 75:8a:88:6c:b0:7e:dc:c3:d4:50:c4:25:0c:de:f7:53:f6:12: + bd:5a:8b:81:3a:30:83:b7:66:5e:7e:8f:d8:65:ab:2a:fa:85: + d4:4b:61:fb:27:98:79:cb:d6:06:07:23:7f:d9:1d:79:3e:71: + a7:0c:c8:e8:0e:e0:40:66:4d:8d:0e:9c:41:c0:d2:2c:91:95: + 63:d5:c4:12:cd:9b:bc:f4 +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIUXIH9uYJp4EaNme3QlUvT1mKKaZcwDQYJKoZIhvcNAQEL +BQAwVjEPMA0GA1UEAwwGY2FjZXJ0MQswCQYDVQQGEwJGSTERMA8GA1UECAwISGVs +c2lua2kxETAPBgNVBAcMCEhlbHNpbmtpMRAwDgYDVQQKDAdNYXJpYURCMB4XDTIw +MDMwMzAzMDMwM1oXDTQwMDIyNzAzMDMwM1owVjEPMA0GA1UEAwwGY2FjZXJ0MQsw +CQYDVQQGEwJGSTERMA8GA1UECAwISGVsc2lua2kxETAPBgNVBAcMCEhlbHNpbmtp +MRAwDgYDVQQKDAdNYXJpYURCMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEAvHIBEGjB3/rRd1V8CRMZUUHN7U9RQyLnlw6U/A0dqcbzF3kxbxJ6m3+H4lUW +1aTdatNeT+LgvMSfxZUaRxOlzW3zduKaEjF25PEBlDdFEDIYnszYfwTKGJe/Ii5R +unNvRlA2/t+CTIqeNL5E9BJhchizgbXZsmmPffSLQWI0WXH35Camz1VN1FKjNyS9 +giGf0dvN6RKpz2BIPdrqoKfQVxZoSeW06NzQsBKzxxzxs7HrEovHPjwC/vZMqDXY +z3RkY4mEPnWIoAuAXOpospy7465PzSS5l4mVTj94SLqtl0y/+IsUgPR4AxFLp+OZ +HIJZv7bzPj7b2zhzkY3wb+VdAufApYutafEXKSwWQa2sD2TI92JY2nNm95EsifeA +a9eR8chKWsQtPQsYg/HpdC2DuwlGvfBArQvdDEVkjZ7Pp6Bs0szx+QUVeuMCFbDm +mc2BW91Sm8A1JfpMiEcdFhmfnfBUAW4gahCrT9KipzHbbXYmDTWdrB9OTTeHLHgm +1B/wh/z04iqvmqTWaZMWT+374HiZNknSLn1EFZRYvbq807l1XFgTaRiOXQ7JDN0G +xHv8ND7fUUaNXjNAujl1miml8L7ToSxyiIVKoC7ZcRUwSVf0G3Pks7GePuYLMArr +6czsmEw26liKqrFLueFeStCBZfC6LLCmjgLxxuB4uUnrjnkCAwEAAaNTMFEwHQYD +VR0OBBYEFE38ehnzKwx99sB8TfhyNEyMNVJ0MB8GA1UdIwQYMBaAFE38ehnzKwx9 +9sB8TfhyNEyMNVJ0MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB +ADznOui20b7KJ60CAAi3Hy3pzYGfmPDwpMXd3JShpr0xVlQFatqxG0I17/h1HA0P +AHckp+9f8c44OgWnIqZAtmxXRjHoui+1nvt8B5Ncy4JZGi8TW21Vjdnd7ESyf4km +N9r86Kqvh4tewz4hwqDKKkl4+p0QXOgsJKzo82Ely4Zsjx1ML4O91HLcVBv6PRrU +VKmsh3D/z1MmH+cRvLOJ0HElCdmACUKxWo0BqYkeLMyJ5/jCQ0sgzc6cL2eZNU/i +7AAHPWlunkiT1FaxC1IA2YYFHupkaKGHBA8XRYKO9RN6kHnI/ULogN87Rrw5yXYU +kI7NgJiEVkOIMx6sSstIjf811GgO778atTQXesjC6PAY9lgHmwmfteIp4l7LuXHO +E5Btt04jMbyrF7Q9lhdL+AFJuQoMcx1U3lF5OvvwtRCNOK2HNg82SBH3G9Yv7acd +c3RFGWkdn4bsoyuO/VozCE0XQd9Xih6ZwbyqFaGvLCNnTjFFkfuPrx4Zpg+72TpV +zLCiQaWZIMxkkfeOa8StWsjKPXQ2kZc08fjFIodr48+cCnVaCTCInQeZLe+HdUXt +D3WKiGywftzD1FDEJQze91P2Er1ai4E6MIO3Zl5+j9hlqyr6hdRLYfsnmHnL1gYH +I3/ZHXk+cacMyOgO4EBmTY0OnEHA0iyRlWPVxBLNm7z0 +-----END CERTIFICATE----- diff --git a/unittest/libmariadb/certs/server-cert.crl b/unittest/libmariadb/certs/server-cert.crl new file mode 100755 index 00000000..0db83397 --- /dev/null +++ b/unittest/libmariadb/certs/server-cert.crl @@ -0,0 +1,17 @@ +-----BEGIN X509 CRL----- +MIICxTCBrgIBATANBgkqhkiG9w0BAQsFADBWMQ8wDQYDVQQDDAZjYWNlcnQxCzAJ +BgNVBAYTAkZJMREwDwYDVQQIDAhIZWxzaW5raTERMA8GA1UEBwwISGVsc2lua2kx +EDAOBgNVBAoMB01hcmlhREIXDTIwMDMwMzAzMDMwM1oXDTQwMDIyNzAzMDMwM1ow +FDASAgEBFw0yMDAzMDMwMzAzMDNaoA4wDDAKBgNVHRQEAwIBATANBgkqhkiG9w0B +AQsFAAOCAgEAFsQCul/9vZ1qSxnYoVVlHPspJ6JFgg0kEK5ZQsiGaAKyvV1d9xpT +uu1FxOEajbeOGdGk9kRqy928KFynPSTpWphl+bJ2E+uey4a9PTgAlxPEcdbnGf2f +1je7i0pOlOximKkeRb8GB4EwNvS9MPKd2pkJnGgIK6eyTtOnKgvmkA3vwXIqB1wJ +dbQMscQlWyCN7lz5A2D6T9xOYY5gKthBAdM73OgzivGKJekoCCirGqVjA7Kurpmt +zV+UbDrHqxsBB4BS+X4sFp7n9K4dqD/acR7aYsjpxKUzc43rEzuTE8flQw1Hzk/h +lGJKg0XfIs2ol7zSeADocZMQR8G0wpeFsz5abm5/oCQ007Rxn+2d/8ZPfOw75u2p +vdcOARoU1q1qF1lGL3x5PFpam693VxB07js3qi6sCqJjsVETAmz08Bvck0+HeHpr +nyQndk7EZPl7W+8Ls68kZZB+77DDNS9Gwb8BQxzazuvN4G6DQsZLYNhWyV6vPeOt +sJF7mdvr4PElbtNG1WQFySt7SJKaA21hG1qVUqto8ZTd+/KU5b7MMnTM7nxFXQ3a +WwHq79xK1xC4f87rrc423yYEjAQkb/1NSseGa/gfqo6wRcxXbRQETq2r9KIsLwAN +sK1gO7Cl0SW9PtrCF4lWNOES9iSZ3BaacOHT7kD8qoBAL83CIlDFgWg= +-----END X509 CRL----- diff --git a/unittest/libmariadb/certs/server-cert.pem b/unittest/libmariadb/certs/server-cert.pem new file mode 100755 index 00000000..b0bb96ae --- /dev/null +++ b/unittest/libmariadb/certs/server-cert.pem @@ -0,0 +1,128 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=cacert, C=FI, ST=Helsinki, L=Helsinki, O=MariaDB + Validity + Not Before: Mar 3 03:03:03 2020 GMT + Not After : Feb 27 03:03:03 2040 GMT + Subject: C=FI, ST=state or province within country, in other certificates in this file it is the same as L, L=location, usually an address but often ambiguously used, O=organization name, typically a company name, OU=organizational unit name, a division name within an organization, CN=localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (4096 bit) + Modulus: + 00:c9:f9:46:27:69:68:4b:5a:26:dd:1f:98:0f:44: + ba:40:83:ca:82:c2:7a:53:cc:b9:30:f1:ca:3e:e2: + 6d:de:3a:11:aa:ce:c5:90:27:e6:f3:4f:3b:e9:af: + 1a:ec:21:d7:ca:14:1f:f1:9b:cb:cd:7e:57:b4:c8: + 5d:6c:cd:5a:54:dd:8a:9a:a9:27:ef:49:d3:6c:ac: + 99:2d:dc:e5:c0:1e:3c:05:9f:c5:04:c7:2d:81:66: + 21:27:16:d6:c3:e4:97:53:db:21:a6:43:50:70:cb: + 2e:95:fb:da:52:55:27:1b:17:ef:19:83:eb:ff:a1: + fc:62:63:ea:2f:fe:53:35:e6:d9:bc:03:2d:e5:c2: + 18:b1:29:91:e4:a4:79:2c:f1:05:dd:d5:3f:ff:b1: + 9e:64:8d:60:29:74:43:f0:3d:31:e7:78:ce:9f:17: + 74:e5:9f:fb:7b:69:a9:45:3b:e8:76:03:c6:ca:52: + 85:84:50:0d:2b:98:6f:ff:d8:41:66:6d:39:f6:1a: + a3:61:e2:82:5f:dc:ec:ca:97:dc:b2:dc:cf:aa:97: + ef:13:10:ea:fb:8f:99:91:bb:d9:e8:61:25:2d:68: + 04:af:2f:89:56:0d:89:90:77:e0:ad:c2:25:eb:3c: + d2:4e:3d:ca:6e:ae:35:c8:f2:94:7a:09:74:d3:8e: + 73:30:e6:39:fe:b6:9c:c7:4d:23:4e:b0:bf:90:97: + 29:b2:b3:30:b2:bb:49:ae:47:09:fe:cd:23:3a:01: + a4:ac:cb:53:25:74:98:27:20:85:6b:18:74:bb:1c: + bf:ff:05:dc:06:7c:02:78:81:1e:96:ad:8f:c6:a2: + 0d:b3:5c:8e:ad:d5:fd:af:c5:8d:8b:9f:31:b1:4f: + a7:1e:9b:cd:57:68:d5:ad:ed:4e:7b:5f:0d:0c:d2: + 47:85:b4:65:4b:23:1c:5b:a5:ec:88:fa:42:80:73: + 84:cb:75:05:a6:39:f1:e6:a9:4e:15:e6:2f:f7:61: + 0b:f3:08:cc:a6:2b:2b:64:6e:04:a1:fc:da:5e:34: + ad:7c:54:be:85:e4:ed:64:74:31:30:2a:ed:ab:3e: + d2:cd:c7:3e:de:18:04:8a:a7:bd:ad:52:74:13:b1: + b0:7d:4d:7e:87:7b:cb:82:1f:29:11:e1:0e:4b:42: + 2b:83:e8:88:7e:92:80:20:eb:ee:da:d4:dd:1e:9c: + 54:5c:67:a7:00:5f:a1:b7:bf:5d:c0:5a:25:2a:c1: + 1e:7c:93:32:dd:17:c8:02:6d:1a:42:26:f6:50:01: + 4b:df:29:7f:72:f1:90:72:80:8a:ba:2f:8c:86:7d: + 56:45:c5:0e:82:16:d8:29:03:57:87:ce:22:1c:7f: + 31:a8:4f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + DB:DF:8B:BC:C2:EE:A9:2A:4E:FD:0E:7A:5F:15:CF:94:A3:0C:CA:CC + X509v3 Authority Key Identifier: + keyid:4D:FC:7A:19:F3:2B:0C:7D:F6:C0:7C:4D:F8:72:34:4C:8C:35:52:74 + + Signature Algorithm: sha256WithRSAEncryption + 09:d1:af:0e:0b:e8:a2:5e:c8:ee:0a:9f:21:c3:2c:da:a4:38: + d5:cc:a6:ca:8e:ef:df:94:ab:32:5a:ec:32:84:01:7a:97:bb: + 38:e0:a8:7d:20:d9:ca:51:3d:a3:74:f9:fe:85:14:26:95:37: + de:2d:74:7e:16:eb:14:14:1b:80:e9:12:54:de:cc:94:cc:38: + ca:df:9a:d0:ce:3e:6c:f1:de:e8:40:f5:3e:6e:c0:ee:05:50: + 5a:38:4b:97:69:3c:7a:1f:a8:11:67:e5:9a:9e:50:2e:62:e8: + f8:bf:1a:54:84:ad:9d:0e:1e:ec:64:22:1b:38:85:87:0b:f3: + c7:47:80:aa:c1:99:72:a5:0d:fd:ce:2c:6e:0d:52:4d:d7:55: + 2f:4e:52:6e:4a:b9:9a:61:34:08:59:d9:30:cc:30:4a:dc:35: + 34:b9:b0:a4:97:a1:b8:d7:ce:ee:63:2d:3a:ad:73:9c:99:49: + 11:0a:04:94:60:97:19:4f:4b:66:d4:fb:bf:14:46:39:27:da: + 01:3f:d8:6a:46:cb:77:12:f2:77:86:3a:45:e1:f7:44:3d:2b: + 3d:e6:26:06:5e:29:20:be:1f:aa:74:43:0c:85:79:e2:14:9c: + 03:bf:49:21:64:7e:c3:4a:7b:a1:60:f6:ce:fb:7c:59:e4:65: + 7c:fb:1e:84:38:53:ec:1f:80:c2:b5:f7:c2:0e:46:19:4b:4d: + a3:32:6e:59:40:32:9b:6b:2c:bb:fa:1a:89:2e:96:22:71:d5: + 71:92:9b:0d:86:0e:60:60:19:ba:34:22:e1:f1:f3:c9:87:5c: + 5c:f5:d3:52:1c:11:0d:d3:91:7a:6b:bd:6f:cc:ba:78:60:e0: + 20:b4:c2:d1:91:70:5f:74:33:a1:bc:aa:db:d1:35:91:b5:cb: + 46:a8:28:7a:26:fc:8c:6c:64:05:4d:73:f1:00:bb:eb:70:87: + fd:9f:04:55:8f:7b:00:b1:c0:50:09:3a:58:44:19:a7:bd:f1: + 34:5b:4d:d9:10:6b:d5:38:fa:64:f5:d3:28:4f:c4:23:14:29: + 98:3e:2f:c2:87:6f:69:a0:89:0e:ee:f7:c4:50:9e:33:b9:0a: + 84:f0:c6:38:45:38:91:10:14:ac:c4:03:8e:4b:e2:61:f9:78: + 85:02:b9:c6:d5:c2:9f:ba:ac:21:1a:3a:4e:1a:f8:a9:12:ae: + 67:37:79:ce:ec:94:54:cf:28:c4:33:3b:45:23:d2:cb:37:3b: + 09:ee:e2:c4:9f:12:dc:e3:8f:06:1d:d5:54:b7:73:2c:34:36: + 97:41:91:81:30:06:2c:90:14:9b:aa:4e:33:2a:38:29:f5:3d: + f4:c7:f2:03:6d:d9:d7:3e +-----BEGIN CERTIFICATE----- +MIIGlTCCBH2gAwIBAgIBATANBgkqhkiG9w0BAQsFADBWMQ8wDQYDVQQDDAZjYWNl +cnQxCzAJBgNVBAYTAkZJMREwDwYDVQQIDAhIZWxzaW5raTERMA8GA1UEBwwISGVs +c2lua2kxEDAOBgNVBAoMB01hcmlhREIwHhcNMjAwMzAzMDMwMzAzWhcNNDAwMjI3 +MDMwMzAzWjCCAUcxCzAJBgNVBAYTAkZJMWEwXwYDVQQIDFhzdGF0ZSBvciBwcm92 +aW5jZSB3aXRoaW4gY291bnRyeSwgaW4gb3RoZXIgY2VydGlmaWNhdGVzIGluIHRo +aXMgZmlsZSBpdCBpcyB0aGUgc2FtZSBhcyBMMUAwPgYDVQQHDDdsb2NhdGlvbiwg +dXN1YWxseSBhbiBhZGRyZXNzIGJ1dCBvZnRlbiBhbWJpZ3VvdXNseSB1c2VkMTQw +MgYDVQQKDCtvcmdhbml6YXRpb24gbmFtZSwgdHlwaWNhbGx5IGEgY29tcGFueSBu +YW1lMUkwRwYDVQQLDEBvcmdhbml6YXRpb25hbCB1bml0IG5hbWUsIGEgZGl2aXNp +b24gbmFtZSB3aXRoaW4gYW4gb3JnYW5pemF0aW9uMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ+UYnaWhLWibdH5gP +RLpAg8qCwnpTzLkw8co+4m3eOhGqzsWQJ+bzTzvprxrsIdfKFB/xm8vNfle0yF1s +zVpU3YqaqSfvSdNsrJkt3OXAHjwFn8UExy2BZiEnFtbD5JdT2yGmQ1Bwyy6V+9pS +VScbF+8Zg+v/ofxiY+ov/lM15tm8Ay3lwhixKZHkpHks8QXd1T//sZ5kjWApdEPw +PTHneM6fF3Tln/t7aalFO+h2A8bKUoWEUA0rmG//2EFmbTn2GqNh4oJf3OzKl9yy +3M+ql+8TEOr7j5mRu9noYSUtaASvL4lWDYmQd+CtwiXrPNJOPcpurjXI8pR6CXTT +jnMw5jn+tpzHTSNOsL+QlymyszCyu0muRwn+zSM6AaSsy1MldJgnIIVrGHS7HL// +BdwGfAJ4gR6WrY/Gog2zXI6t1f2vxY2LnzGxT6cem81XaNWt7U57Xw0M0keFtGVL +IxxbpeyI+kKAc4TLdQWmOfHmqU4V5i/3YQvzCMymKytkbgSh/NpeNK18VL6F5O1k +dDEwKu2rPtLNxz7eGASKp72tUnQTsbB9TX6He8uCHykR4Q5LQiuD6Ih+koAg6+7a +1N0enFRcZ6cAX6G3v13AWiUqwR58kzLdF8gCbRpCJvZQAUvfKX9y8ZBygIq6L4yG +fVZFxQ6CFtgpA1eHziIcfzGoTwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG ++EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU +29+LvMLuqSpO/Q56XxXPlKMMyswwHwYDVR0jBBgwFoAUTfx6GfMrDH32wHxN+HI0 +TIw1UnQwDQYJKoZIhvcNAQELBQADggIBAAnRrw4L6KJeyO4KnyHDLNqkONXMpsqO +79+UqzJa7DKEAXqXuzjgqH0g2cpRPaN0+f6FFCaVN94tdH4W6xQUG4DpElTezJTM +OMrfmtDOPmzx3uhA9T5uwO4FUFo4S5dpPHofqBFn5ZqeUC5i6Pi/GlSErZ0OHuxk +Ihs4hYcL88dHgKrBmXKlDf3OLG4NUk3XVS9OUm5KuZphNAhZ2TDMMErcNTS5sKSX +objXzu5jLTqtc5yZSREKBJRglxlPS2bU+78URjkn2gE/2GpGy3cS8neGOkXh90Q9 +Kz3mJgZeKSC+H6p0QwyFeeIUnAO/SSFkfsNKe6Fg9s77fFnkZXz7HoQ4U+wfgMK1 +98IORhlLTaMybllAMptrLLv6GokuliJx1XGSmw2GDmBgGbo0IuHx88mHXFz101Ic +EQ3TkXprvW/Munhg4CC0wtGRcF90M6G8qtvRNZG1y0aoKHom/IxsZAVNc/EAu+tw +h/2fBFWPewCxwFAJOlhEGae98TRbTdkQa9U4+mT10yhPxCMUKZg+L8KHb2mgiQ7u +98RQnjO5CoTwxjhFOJEQFKzEA45L4mH5eIUCucbVwp+6rCEaOk4a+KkSrmc3ec7s +lFTPKMQzO0Uj0ss3Ownu4sSfEtzjjwYd1VS3cyw0NpdBkYEwBiyQFJuqTjMqOCn1 +PfTH8gNt2dc+ +-----END CERTIFICATE----- diff --git a/unittest/libmariadb/certs/server-key.pem b/unittest/libmariadb/certs/server-key.pem new file mode 100755 index 00000000..20b64cac --- /dev/null +++ b/unittest/libmariadb/certs/server-key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKgIBAAKCAgEAyflGJ2loS1om3R+YD0S6QIPKgsJ6U8y5MPHKPuJt3joRqs7F +kCfm80876a8a7CHXyhQf8ZvLzX5XtMhdbM1aVN2Kmqkn70nTbKyZLdzlwB48BZ/F +BMctgWYhJxbWw+SXU9shpkNQcMsulfvaUlUnGxfvGYPr/6H8YmPqL/5TNebZvAMt +5cIYsSmR5KR5LPEF3dU//7GeZI1gKXRD8D0x53jOnxd05Z/7e2mpRTvodgPGylKF +hFANK5hv/9hBZm059hqjYeKCX9zsypfcstzPqpfvExDq+4+ZkbvZ6GElLWgEry+J +Vg2JkHfgrcIl6zzSTj3Kbq41yPKUegl0045zMOY5/racx00jTrC/kJcpsrMwsrtJ +rkcJ/s0jOgGkrMtTJXSYJyCFaxh0uxy//wXcBnwCeIEelq2PxqINs1yOrdX9r8WN +i58xsU+nHpvNV2jVre1Oe18NDNJHhbRlSyMcW6XsiPpCgHOEy3UFpjnx5qlOFeYv +92EL8wjMpisrZG4EofzaXjStfFS+heTtZHQxMCrtqz7Szcc+3hgEiqe9rVJ0E7Gw +fU1+h3vLgh8pEeEOS0Irg+iIfpKAIOvu2tTdHpxUXGenAF+ht79dwFolKsEefJMy +3RfIAm0aQib2UAFL3yl/cvGQcoCKui+Mhn1WRcUOghbYKQNXh84iHH8xqE8CAwEA +AQKCAgEAtn2LvKqJ+KOhP+R/ETSpEQfDX7h2rObqYWXmtkECwms3cVzYtzLGgwsR +eEimC7tcbZMXdceuMqM4ffkYKOm2970gsjOrCJNs++FLmlvgH2FyHCbK7lFFNYjg +5Z/GN0OA09zIH6Dsuq1rD0t7bS7RYbKTcDt5bgWftArRG8IwzyAhlbZNry5b/x8v +Wiad8lRoV5KJ++8xzWrL+0i45gV05M+L6cVY8u1FXbIPpqXFmXQ6Fq7PQsjNzZkz +gTiVhwWj/FD/VL9Dy3gjX74tRFMtM9eJxXFg0CFkwX+5GTVFUSyOJvfNoDolFKqs +EHO+rTjMULOWB8FSiZldPJL0wv4P5wEAZ+aqQ4mWsDHVDyACRlmLbOvMriiHrCMj +uDOWQwMsWOc/vCt6WPuNv3PlagACqOGQf0Pj62OcWTJGRzHoOXCw1OMgW3nNgg9X +f5UvXE8nn1hfJMePh6u1BXuOmoyOi2zTIng8ubfE8nw4pw35fkvfkY8BqllUw0XO +Kq5ICr1eqrHJNnicL+ITfiFm7yo0qUPlQnkBA7pCvrNDEdh6mLIEUE/NSqZd2XKB +TZeXLh6/0+djK1899sTsjr75yB1mVc6Jgo0OC4Em6T/u+VKw5LptxAoBnY7et2/7 +6oJe8trQDEwBBE0ihwAvlElwG+kUyKS7P01Ow7ctmYzUuDoLgqECggEBAOV6LQ2H +/OfKfgyP/0pM3ngDUC0H49FLIXAKRdY/gLzecPauTleUX1aGOBkfscuHlKjKCllS +qUCHBLyE3bwAK5t0E3aHLtV9gGjNZvi7O6DZHwnMWP5AwN+eW1TASWUKfPVS6uUk +uMzkTkUowDK38EF7iabinSEYmpNxt0G7T4K9pV6O2HgMkKdIHvyxJ0dME3VOgG6m +miYTe2faRDaqRNY2Kn/r1NhSpRKb/iBovFcHLEQpffbtoJs4hzSUa8gk+iTk7WRX +k5a/74nJpB+v1m8ywHEmZ9wFBTCjoVNzBj0f7vZOrs6pP/3niJPEbyi1oK42I1GN +g6J16jgjVv+Q21ECggEBAOFRUVM61Yqz9mt34MqJgp0bqGEShDuFY4kvttAbtHK5 +FLYc2Jxvn657jp1r2ksAb6UsDbp92UXmxxTw5H+PWsQ/nbNxmhPfjbjdvhoB4IHr +l3YxgWL9Ex0bi1vB8i/RYEOZs+Q9yyjB7rJqR6gw2jDJ0CQe/RsZUFh4RdzvrWoz +gqddlFUzfkS8KAjvmUA6rOrAgIdYhBRP85gXDtV+bc331q6NH8wu0hkJ0Q6gj0WZ +l/Nu1wWg8goHMuCU5GCDtrjQaXSECCo+XL4+VheGk8ZkDF+EkD4tW0oRKyao1rex ++aZ7IykGikXpMGtoQ+gUyiQzOzodFD5aycfJ+vO0IZ8CggEBANHXREWAWfM3xsYE +E2XFHxZNqU5UTa5AbqY+rpf5X+bV3iSlRfxuMDQ17iyDQBkmtPkYMBh4L09TaDBf +q0fUPAwePAICIgCVkAvF8Gh5BlDS2sAh/isZ2YVjEI9SeosL6TKIjUXWq6qpBy3U +0tROQUQQDNLvnNH75rX3oaVY/J9IfmUWaLp2evyNRdC6ynk3BwAZNfZ5ILK60/km +rYPzdZkQ1RI+/FaIVGzM+rh2LevDImZ+LrLTny7xpSmeo2TU820zbiV4s/yBLTEp +k4xqRcNPfIy7mvOmH5XXu5lMsKLKhcD0OIPAX0T2KX0+fouScwl7dhaIOpwgCXsm +TdLLitECggEAcJMYduUmXC6eKAO1JHyf7a8r6ZQ+zR5QJPLZ/BBbkBY46uRutXpv +5dWF49FHN8H4BiaElXDbZblwLl5NTA5r4zGFsWpI+TTwsjTYDlZxvXfgLQV/B945 +9okUj7vPLWUHG79nydm17581i57ePoJqAYZToDh7bVawdgNhpIl7s1wZI1X2Druz +ktQPjKdpglXOn1ue3AC1vRBVPOAIYVLRud7iPEP2ZyXdgvUMpqpB6xxadx4iAIXx +aGFAYkUB9dbZLG20wqpVCgYugD7U9NwgaTcDl5W8G9S9j7wUOzSQUydw/GT5pD3I +SDXCI2fsxb/dX0jZhqeQvbbOqiuYXrrZMQKCAQEAqE3ZWDqhx4obUCkAiSnADnmh +v4/yzeHByCXH71uhou/C+y1f3RQLIZN8oAgnSKpFeAdue3Gwk6BA8FYBjSF2hhrM +LaYkzZ0fhFKhTbNmJ2LVJgFtOO8C/95UWY9tKuToRfndXv+99c1d1o85C97KE7C1 +zREnTgIf990e5gx2KQCA5irELpKltu0g1wDfbD6hCxJhuvsg869Q0JcXcktg8c0D +sbJQWZrlr9i7TG6TEs7bWxH77hOdrpPyaRUiELt8ZfJJrrctYaY4ogd8NDctDbma +WJWdco8kyiWauoRBJuJJwRtTTMQ+JBiomOhDb1moQd19lNQiBjW5pTfr92NSVg== +-----END RSA PRIVATE KEY----- diff --git a/unittest/libmariadb/charset.c b/unittest/libmariadb/charset.c index 35ae716f..64bc07f2 100644 --- a/unittest/libmariadb/charset.c +++ b/unittest/libmariadb/charset.c @@ -757,7 +757,7 @@ static int charset_auto(MYSQL *my __attribute__((unused))) mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "auto"); FAIL_IF(!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0), + password, schema, port, socketname, 0, 1), mysql_error(mysql)); csname1= mysql_character_set_name(mysql); diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index 7886432b..5a29aa5d 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -65,7 +65,7 @@ static int test_conc66(MYSQL *my) rc= mysql_query(my, "FLUSH PRIVILEGES"); check_mysql_rc(rc, my); if (!my_test_connect(mysql, hostname, NULL, - NULL, schema, port, socketname, 0)) + NULL, schema, port, socketname, 0, 1)) { diag("user: %s", mysql->options.user); diag("Error: %s", mysql_error(mysql)); @@ -463,7 +463,7 @@ static int test_bug31669(MYSQL *mysql) static int test_bug33831(MYSQL *mysql) { FAIL_IF(my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0), + password, schema, port, socketname, 0, 1), "Error expected"); return OK; @@ -493,7 +493,7 @@ static int test_opt_reconnect(MYSQL *mysql) if (!(my_test_connect(mysql, hostname, username, password, schema, port, - socketname, 0))) + socketname, 0, 1))) { diag("connection failed"); mysql_close(mysql); @@ -513,7 +513,7 @@ static int test_opt_reconnect(MYSQL *mysql) if (!(my_test_connect(mysql, hostname, username, password, schema, port, - socketname, 0))) + socketname, 0, 1))) { diag("connection failed"); mysql_close(mysql); @@ -544,7 +544,7 @@ static int test_compress(MYSQL *mysql) if (!(my_test_connect(mysql, hostname, username, password, schema, port, - socketname, 0))) + socketname, 0, 1))) { diag("connection failed"); return FAIL; @@ -583,7 +583,7 @@ static int test_reconnect(MYSQL *mysql) if (!(my_test_connect(mysql1, hostname, username, password, schema, port, - socketname, 0))) + socketname, 0, 1))) { diag("connection failed"); return FAIL; @@ -647,14 +647,14 @@ int test_conc26(MYSQL *unused __attribute__((unused))) MYSQL *mysql= mysql_init(NULL); mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "ascii"); - FAIL_IF(my_test_connect(mysql, hostname, "notexistinguser", "password", schema, port, socketname, CLIENT_REMEMBER_OPTIONS), + FAIL_IF(my_test_connect(mysql, hostname, "notexistinguser", "password", schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1), "Error expected"); FAIL_IF(!mysql->options.charset_name || strcmp(mysql->options.charset_name, "ascii") != 0, "expected charsetname=ascii"); mysql_close(mysql); mysql= mysql_init(NULL); - FAIL_IF(my_test_connect(mysql, hostname, "notexistinguser", "password", schema, port, socketname, 0), + FAIL_IF(my_test_connect(mysql, hostname, "notexistinguser", "password", schema, port, socketname, 0, 1), "Error expected"); FAIL_IF(mysql->options.charset_name, "Error: options not freed"); mysql_close(mysql); @@ -669,7 +669,7 @@ int test_connection_timeout(MYSQL *unused __attribute__((unused))) MYSQL *mysql= mysql_init(NULL); mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); start= time(NULL); - if (my_test_connect(mysql, "192.168.1.101", "notexistinguser", "password", schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + if (my_test_connect(mysql, "192.168.1.101", "notexistinguser", "password", schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { diag("Error expected - maybe you have to change hostname"); return FAIL; @@ -697,7 +697,7 @@ int test_connection_timeout2(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_INIT_COMMAND, "set @a:=SLEEP(7)"); mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &no); start= time(NULL); - if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { elapsed= time(NULL) - start; diag("elapsed: %lu", (unsigned long)elapsed); @@ -723,7 +723,7 @@ int test_connection_timeout3(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_OPT_WRITE_TIMEOUT, (unsigned int *)&read_write_timeout); mysql_options(mysql, MYSQL_INIT_COMMAND, "set @a:=SLEEP(6)"); start= time(NULL); - if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { diag("timeout error expected"); elapsed= time(NULL) - start; @@ -740,7 +740,7 @@ int test_connection_timeout3(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (unsigned int *)&read_write_timeout); mysql_options(mysql, MYSQL_OPT_WRITE_TIMEOUT, (unsigned int *)&read_write_timeout); - if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { diag("Error: %s", mysql_error(mysql)); return FAIL; @@ -801,7 +801,7 @@ static int test_wrong_bind_address(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_OPT_BIND, bind_addr); if (my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error expected"); mysql_close(mysql); @@ -848,7 +848,7 @@ static int test_bind_address(MYSQL *my) mysql_options(mysql, MYSQL_OPT_BIND, bind_addr); if (!my_test_connect(mysql, bind_addr, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s\n", mysql_error(mysql)); mysql_close(mysql); @@ -1081,7 +1081,7 @@ static int test_unix_socket_close(MYSQL *unused __attribute__((unused))) for (i=0; i < 10000; i++) { - my_test_connect(mysql, "localhost", "user", "passwd", NULL, 0, "./dummy_sock", 0); + my_test_connect(mysql, "localhost", "user", "passwd", NULL, 0, "./dummy_sock", 0, 1); /* check if we run out of sockets */ if (mysql_errno(mysql) == 2001) { @@ -1194,7 +1194,7 @@ static int test_auth256(MYSQL *my) rc= mysql_query(my, query); check_mysql_rc(rc, my); - if (!my_test_connect(mysql, hostname, "sha256user", "foo", NULL, port, socketname, 0)) + if (!my_test_connect(mysql, hostname, "sha256user", "foo", NULL, port, socketname, 0, 1)) { diag("error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -1204,7 +1204,7 @@ static int test_auth256(MYSQL *my) mysql= mysql_init(NULL); mysql_options(mysql, MYSQL_SERVER_PUBLIC_KEY, "rsa_public_key.pem"); - if (!my_test_connect(mysql, hostname, "sha256user", "foo", NULL, port, socketname, 0)) + if (!my_test_connect(mysql, hostname, "sha256user", "foo", NULL, port, socketname, 0, 1)) { diag("error: %s", mysql_error(mysql)); diag("host: %s", this_host); @@ -1240,7 +1240,7 @@ static int test_mdev13100(MYSQL *my __attribute__((unused))) check_mysql_rc(rc, mysql); if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); return FAIL; @@ -1265,7 +1265,7 @@ static int test_mdev13100(MYSQL *my __attribute__((unused))) check_mysql_rc(rc, mysql); if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); return FAIL; @@ -1292,7 +1292,7 @@ if (!(fp= fopen("./mdev13100.cnf", "w"))) check_mysql_rc(rc, mysql); if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); return FAIL; @@ -1322,7 +1322,7 @@ if (!(fp= fopen("./mdev13100.cnf", "w"))) check_mysql_rc(rc, mysql); if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); return FAIL; @@ -1350,7 +1350,7 @@ if (!(fp= fopen("./mdev13100.cnf", "w"))) check_mysql_rc(rc, mysql); if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); return FAIL; @@ -1373,7 +1373,7 @@ static int test_conc276(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(mysql, MYSQL_OPT_RECONNECT, &val); - if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0)) + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0, 1)) { diag("Connection failed. Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -1423,7 +1423,7 @@ static int test_expired_pw(MYSQL *my) mysql= mysql_init(NULL); my_test_connect(mysql, hostname, "foo", "foo", schema, - port, socketname, 0); + port, socketname, 0, 1); FAIL_IF(!mysql_errno(mysql), "Error expected"); mysql_close(mysql); @@ -1432,7 +1432,7 @@ static int test_expired_pw(MYSQL *my) mysql_optionsv(mysql, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, &expire); my_test_connect(mysql, hostname, "foo", "foo", schema, - port, socketname, 0); + port, socketname, 0, 1); /* we should be in sandbox mode now, only set commands should be allowed */ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); @@ -1503,7 +1503,7 @@ static int test_conc317(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); my_test_connect(mysql, hostname, username, password, - schema, port, socketname, 0); + schema, port, socketname, 0, 1); remove(cnf_file1); @@ -1551,7 +1551,7 @@ static int test_conc327(MYSQL *unused __attribute__((unused))) mysql= mysql_init(NULL); mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); my_test_connect(mysql, hostname, username, password, - schema, port, socketname, 0); + schema, port, socketname, 0, 1); remove(cnf_file1); remove(cnf_file2); @@ -1575,7 +1575,7 @@ static int test_conc327(MYSQL *unused __attribute__((unused))) mysql= mysql_init(NULL); mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, cnf_file2); my_test_connect(mysql, hostname, username, password, - schema, port, socketname, 0); + schema, port, socketname, 0, 1); remove(cnf_file1); remove(cnf_file2); @@ -1601,7 +1601,7 @@ static int test_conc332(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4"); my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0); + port, socketname, 0, 1); FAIL_IF(mysql_errno(mysql), "Error during connect"); @@ -1640,7 +1640,7 @@ static int test_conc351(MYSQL *unused __attribute__((unused))) ulong capabilities= 0; my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0); + port, socketname, 0, 1); FAIL_IF(mysql_errno(mysql), "Error during connect"); @@ -1690,7 +1690,7 @@ static int test_conc312(MYSQL *my) check_mysql_rc(rc, my); mysql= mysql_init(NULL); - if (!my_test_connect(mysql, hostname, "foo", "foo", schema, port, socketname, 0)) + if (!my_test_connect(mysql, hostname, "foo", "foo", schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); return FAIL; @@ -1749,7 +1749,7 @@ static int test_conc366(MYSQL *mysql) my= mysql_init(NULL); if (plugindir) mysql_options(my, MYSQL_PLUGIN_DIR, plugindir); - if (!my_test_connect(my, hostname, "ede", "MySup8%rPassw@ord", schema, port, socketname, 0)) + if (!my_test_connect(my, hostname, "ede", "MySup8%rPassw@ord", schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(my)); return FAIL; @@ -1811,7 +1811,7 @@ static int test_conc443(MYSQL *my __attribute__((unused))) mysql_options(mysql, MYSQL_INIT_COMMAND, "set @a:=3"); mysql_options(mysql, MYSQL_OPT_RECONNECT, &x); - if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { diag("Connection failed. Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -1857,7 +1857,7 @@ static int test_default_auth(MYSQL *my __attribute__((unused))) mysql= mysql_init(NULL); mysql_options(mysql, MYSQL_DEFAULT_AUTH, "mysql_clear_password"); - if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { diag("Connection failed. Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -1868,7 +1868,7 @@ static int test_default_auth(MYSQL *my __attribute__((unused))) mysql= mysql_init(NULL); mysql_options(mysql, MYSQL_DEFAULT_AUTH, "caching_sha2_password"); - if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { diag("Connection failed. Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -1926,7 +1926,7 @@ static int test_conc490(MYSQL *my __attribute__((unused))) MYSQL *mysql= mysql_init(NULL); if (!my_test_connect(mysql, hostname, username, - password, NULL, port, socketname, CLIENT_CONNECT_WITH_DB)) + password, NULL, port, socketname, CLIENT_CONNECT_WITH_DB, 1)) { diag("error: %s\n", mysql_error(mysql)); return FAIL; @@ -1961,7 +1961,7 @@ static int test_conc544(MYSQL *mysql) check_mysql_rc(rc, mysql); if (my_test_connect(my, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("error expected (restricted auth)"); return FAIL; @@ -1984,7 +1984,7 @@ static int test_conc544(MYSQL *mysql) if (plugindir) mysql_optionsv(my, MYSQL_PLUGIN_DIR, plugindir); mysql_optionsv(my, MARIADB_OPT_RESTRICTED_AUTH, "client_ed25519, mysql_native_password"); - if (!my_test_connect(my, hostname, "ede", "MySup8%rPassw@ord", schema, port, socketname, 0)) + if (!my_test_connect(my, hostname, "ede", "MySup8%rPassw@ord", schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(my)); return FAIL; @@ -2059,7 +2059,7 @@ static int test_conn_str_1(MYSQL *my __attribute__((unused))) rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); check_mysql_rc(rc, mysql); - if (!my_test_connect(mysql, NULL, NULL, NULL, NULL, 0, NULL, 0)) + if (!my_test_connect(mysql, NULL, NULL, NULL, NULL, 0, NULL, 0, 1)) { diag("Error: %s", mysql_error(mysql)); remove("./conc274.cnf"); @@ -2102,7 +2102,7 @@ static int test_conc365(MYSQL *my __attribute__((unused))) mysql= mysql_init(NULL); snprintf(tmp, sizeof(tmp) -1, "127.0.0.1:3300,%s:%d", hostname ? hostname : "localhost", port); if (!my_test_connect(mysql, tmp, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); rc= FAIL; @@ -2116,7 +2116,7 @@ static int test_conc365(MYSQL *my __attribute__((unused))) mysql= mysql_init(NULL); mysql_options(mysql, MARIADB_OPT_HOST, tmp); if (!my_test_connect(mysql, NULL, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); rc= FAIL; @@ -2151,13 +2151,14 @@ static int test_conc365_reconnect(MYSQL *my) } if (!my_test_connect(mysql, tmp, username, - password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { diag("Error: %s", mysql_error(mysql)); rc= FAIL; } sprintf(tmp, "KILL %ld", mysql_thread_id(mysql)); + diag("KILL %ld", mysql_thread_id(mysql)); rc= mysql_query(my, tmp); check_mysql_rc(rc, my); @@ -2232,7 +2233,7 @@ static int test_status_callback(MYSQL *my __attribute__((unused))) rc= mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, my_status_callback, &data); if (!my_test_connect(mysql, hostname, username, - password, NULL, port, socketname, 0)) + password, NULL, port, socketname, 0, 1)) { diag("error1: %s", mysql_error(mysql)); return FAIL; @@ -2278,7 +2279,7 @@ static int test_conc632(MYSQL *my __attribute__((unused))) MYSQL *mysql= mysql_init(NULL); int rc; - if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) { diag("Connection failed. Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -2329,7 +2330,7 @@ static int test_x509(MYSQL *my __attribute__((unused))) mysql_options(mysql1, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); if (!(my_test_connect(mysql1, hostname, username, password, schema, port, - socketname, 0))) + socketname, 0, 1))) { diag("connection failed"); return FAIL; @@ -2339,7 +2340,7 @@ static int test_x509(MYSQL *my __attribute__((unused))) mysql_options(mysql2, MARIADB_OPT_TLS_PEER_FP, info->fingerprint); if (!(my_test_connect(mysql2, hostname, username, password, schema, port, - socketname, 0))) + socketname, 0, 1))) { diag("connection failed"); return FAIL; @@ -2363,7 +2364,7 @@ static int test_conc505(MYSQL *my __attribute__((unused))) #define CLIENT_DEPRECATE_EOF (1ULL << 24) - if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_DEPRECATE_EOF)) + if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_DEPRECATE_EOF, 1)) { diag("Error expected: Invalid client flag"); mysql_close(mysql); @@ -2371,7 +2372,7 @@ static int test_conc505(MYSQL *my __attribute__((unused))) } diag("Error (expected): %s", mysql_error(mysql)); FAIL_IF(mysql_errno(mysql) != CR_INVALID_CLIENT_FLAG, "Wrong error number"); - if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS)) + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS, 1)) { diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); diff --git a/unittest/libmariadb/misc.c b/unittest/libmariadb/misc.c index 16609c4e..98a51c1f 100644 --- a/unittest/libmariadb/misc.c +++ b/unittest/libmariadb/misc.c @@ -991,7 +991,7 @@ static int test_connect_attrs(MYSQL *my) mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo2", "bar2"); FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0), mysql_error(my)); + port, socketname, 0, 1), mysql_error(my)); if (!(mysql->server_capabilities & CLIENT_CONNECT_ATTRS)) { @@ -1041,7 +1041,7 @@ static int test_conc117(MYSQL *unused __attribute__((unused))) MYSQL *my= mysql_init(NULL); SKIP_MAXSCALE; FAIL_IF(!my_test_connect(my, hostname, username, password, schema, - port, socketname, 0), mysql_error(my)); + port, socketname, 0, 1), mysql_error(my)); mysql_kill(my, mysql_thread_id(my)); @@ -1060,7 +1060,7 @@ static int test_read_timeout(MYSQL *unused __attribute__((unused))) SKIP_MAXSCALE; mysql_options(my, MYSQL_OPT_READ_TIMEOUT, &timeout); FAIL_IF(!my_test_connect(my, hostname, username, password, schema, - port, socketname, 0), mysql_error(my)); + port, socketname, 0, 1), mysql_error(my)); rc= mysql_query(my, "SELECT SLEEP(50)"); @@ -1136,7 +1136,7 @@ static int test_remote2(MYSQL *my) mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "http://localhost/test.cnf"); mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "test"); my_test_connect(mysql, hostname, username, password, schema, - 0, socketname, 0), mysql_error(my); + 0, socketname, 0, 1), mysql_error(my); diag("port: %d", mysql->port); mysql_close(mysql); return OK; @@ -1177,7 +1177,7 @@ static int test_mdev12965(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); my_test_connect(mysql, hostname, username, password, - schema, port, socketname, 0); + schema, port, socketname, 0, 1); remove(cnf_file1); @@ -1461,7 +1461,7 @@ static int test_conc395(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); my_test_connect(mysql, hostname, username, password, - schema, port, socketname, 0); + schema, port, socketname, 0, 1); remove(cnf_file1); @@ -1501,7 +1501,7 @@ static int test_sslenforce(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); my_test_connect(mysql, hostname, username, password, - schema, port, socketname, 0); + schema, port, socketname, 0, 1); remove(cnf_file1); diff --git a/unittest/libmariadb/my_test.h b/unittest/libmariadb/my_test.h index c0024738..f7d08a6b 100644 --- a/unittest/libmariadb/my_test.h +++ b/unittest/libmariadb/my_test.h @@ -219,12 +219,13 @@ MYSQL *my_test_connect(MYSQL *mysql, const char *db, unsigned int port, const char *unix_socket, - unsigned long clientflag); + unsigned long clientflag, + my_bool auto_fingerprint); static const char *schema = 0; static char *hostname = 0; static char *password = 0; -static char fingerprint[65]= {0}; +static char fingerprint[129]= {0}; static unsigned int port = 0; static unsigned int ssl_port = 0; static char *socketname = 0; @@ -542,7 +543,7 @@ MYSQL *test_connect(struct my_tests_st *test) } } if (!(my_test_connect(mysql, hostname, username, password, - schema, port, socketname, (test) ? test->connect_flags:0))) + schema, port, socketname, (test) ? test->connect_flags:0, 1))) { diag("Couldn't establish connection to server %s. Error (%d): %s", hostname, mysql_errno(mysql), mysql_error(mysql)); @@ -654,12 +655,18 @@ MYSQL *my_test_connect(MYSQL *mysql, const char *db, unsigned int port, const char *unix_socket, - unsigned long clientflag) + unsigned long clientflag, + my_bool auto_fingerprint) { + char *have_fp; if (force_tls) mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &force_tls); - if (fingerprint[0]) + mysql_get_optionv(mysql, MARIADB_OPT_SSL_FP, &have_fp); + if (fingerprint[0] && auto_fingerprint) + { + printf("setting fingerprint\n"); mysql_options(mysql, MARIADB_OPT_SSL_FP, fingerprint); + } if (!mysql_real_connect(mysql, host, user, passwd, db, port, unix_socket, clientflag)) { diag("error: %s", mysql_error(mysql)); @@ -712,11 +719,12 @@ void run_tests(struct my_tests_st *test) { mysql_free_result(res); if (mysql_get_ssl_cipher(mysql)) diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); - mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info); + mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info, 384); if (info) { strcpy(fingerprint, info->fingerprint); diag("Peer certificate fingerprint: %s", fingerprint); + diag("Subject: %s", info->subject); diag("--------------------"); } } diff --git a/unittest/libmariadb/ps.c b/unittest/libmariadb/ps.c index 111ce2a6..94d8d833 100644 --- a/unittest/libmariadb/ps.c +++ b/unittest/libmariadb/ps.c @@ -63,7 +63,7 @@ static int test_conc83(MYSQL *unused __attribute__((unused))) mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); FAIL_IF(!(my_test_connect(mysql, hostname, username, password, - schema, port, socketname, 0)), "my_test_connect failed"); + schema, port, socketname, 0, 1)), "my_test_connect failed"); /* 1. Status is inited, so prepare should work */ @@ -845,7 +845,7 @@ static int test_prepare_alter(MYSQL *mysql) mysql_new= mysql_init(NULL); FAIL_IF(!mysql_new, "mysql_init failed"); FAIL_IF(!(my_test_connect(mysql_new, hostname, username, password, - schema, port, socketname, 0)), "my_test_connect failed"); + schema, port, socketname, 0, 1)), "my_test_connect failed"); rc= mysql_query(mysql_new, "ALTER TABLE test_prep_alter change id id_new varchar(20)"); diag("Error: %d %s", mysql_errno(mysql_new), mysql_error(mysql_new)); check_mysql_rc(rc, mysql_new); diff --git a/unittest/libmariadb/rpl_api.c b/unittest/libmariadb/rpl_api.c index b502eec8..eb5a1cf1 100644 --- a/unittest/libmariadb/rpl_api.c +++ b/unittest/libmariadb/rpl_api.c @@ -44,7 +44,7 @@ static int test_rpl_async(MYSQL *my __attribute__((unused))) return SKIP; if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -112,7 +112,7 @@ static int test_rpl_semisync(MYSQL *my __attribute__((unused))) return SKIP; if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -189,7 +189,7 @@ static int test_conc467(MYSQL *my __attribute__((unused))) return SKIP; if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -271,7 +271,7 @@ static int test_conc592(MYSQL *my __attribute__((unused))) return SKIP; if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -279,7 +279,7 @@ static int test_conc592(MYSQL *my __attribute__((unused))) } if (!my_test_connect(mysql_check, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); @@ -356,7 +356,7 @@ static int test_conc689(MYSQL *my __attribute__((unused))) return SKIP; if (!my_test_connect(mysql, hostname, username, - password, schema, port, socketname, 0)) + password, schema, port, socketname, 0, 1)) { diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); diff --git a/unittest/libmariadb/thread.c b/unittest/libmariadb/thread.c index 6fae38a8..00aee2c1 100644 --- a/unittest/libmariadb/thread.c +++ b/unittest/libmariadb/thread.c @@ -15,7 +15,7 @@ static int basic_connect(MYSQL *unused __attribute__((unused))) FAIL_IF(!my, "mysql_init() failed"); FAIL_IF(!my_test_connect(my, hostname, username, password, schema, - port, socketname, 0), mysql_error(my)); + port, socketname, 0, 1), mysql_error(my)); rc= mysql_query(my, "SELECT @@version"); check_mysql_rc(rc, my); @@ -136,7 +136,7 @@ DWORD WINAPI thread_conc27(void) mysql_thread_init(); mysql= mysql_init(NULL); if(!my_test_connect(mysql, hostname, username, password, schema, - port, socketname, 0)) + port, socketname, 0, 1)) { diag(">Error: %s", mysql_error(mysql)); mysql_close(mysql); diff --git a/unittest/libmariadb/tls.c.in b/unittest/libmariadb/tls.c.in new file mode 100644 index 00000000..38574f6a --- /dev/null +++ b/unittest/libmariadb/tls.c.in @@ -0,0 +1,718 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "my_test.h" +#include "ma_common.h" +#include "ma_pvio.h" + +#include + +#ifdef HAVE_GNUTLS +#include +#include +#include +#elif HAVE_OPENSSL +#include +#endif + +#define CHECK_TLS_FLAGS(m, flag, text) \ +{\ + unsigned int status;\ + mariadb_get_infov(mysql, MARIADB_TLS_VERIFY_STATUS, &status);\ + FAIL_IF(!(status & (flag)), (text));\ +} + +#define CHECK_NO_TLS_FLAGS(m)\ +{\ + unsigned int status;\ + mariadb_get_infov(mysql, MARIADB_TLS_VERIFY_STATUS, &status);\ + FAIL_IF(status), "Expected MARIADB_TLS_VERIFY_OK");\ +} + +my_bool auto_generated_cert= 0; +my_bool ignore_self_signed= 0; + +static char *tls_dummy_host= NULL; +static int tls_dummy_port= 0; + +static my_bool ignore_self_signed_cert_error(MYSQL *mysql) +{ + const char *hostname= mysql->host; + const char *local_host_names[]= { +#ifdef _WIN32 + /* + On Unix, we consider TCP connections with "localhost" + an insecure transport, for the single reason to run tests for + insecure transport on CI.This is artificial, but should be ok. + Default client connections use unix sockets anyway, so it + would not hurt much. + + On Windows, the situation is quite different. + Default connections type is TCP, default host name is "localhost", + non-password plugin gssapi is common (every installation) + In this environment, there would be a lot of faux/disruptive + "self-signed certificates" errors there. Thus, "localhost" TCP + needs to be considered secure transport. + */ + "localhost", +#endif + "127.0.0.1", "::1", NULL}; + int i; + + /* if CA or CAPath was specified, we don't ignore self signed + certificates */ + if (mysql->options.ssl_ca || + mysql->options.ssl_capath) + return FALSE; + + if (mysql->net.pvio->type != PVIO_TYPE_SOCKET) + { + return TRUE; + } + if (!hostname) + return FALSE; + for (i= 0; local_host_names[i]; i++) + { + if (strcmp(hostname, local_host_names[i]) == 0) + { + return TRUE; + } + } + return FALSE; +} + +static int test_start_tls_server(MYSQL *my __attribute__((unused))) +{ +#ifdef WIN32 + STARTUPINFO si; + PROCESS_INFORMATION pi; + char szCmdline[1024]; + + snprintf(szCmdline, sizeof(szCmdline), "%s tls_server.py --host=%s --port=%d", "@Python3_EXECUTABLE@", tls_dummy_host, tls_dummy_port); + + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + + GetStartupInfo(&si); + si.cb = sizeof(STARTUPINFO); + si.wShowWindow = SW_SHOWDEFAULT; + + if( !CreateProcessA(NULL, + szCmdline, + NULL, + NULL, + FALSE, + CREATE_NEW_CONSOLE, + NULL, + NULL, + &si, + &pi) + ) + { + diag("couldn't start tls_server. Error %d", GetLastError()); + return FAIL; + } +#else + pid_t tls_server_pid; + + if (!(tls_server_pid= fork())) + { + char hostname[1024], port[128]; + + snprintf(hostname, sizeof(hostname), "--host=%s", tls_dummy_host); + snprintf(port, sizeof(port), "--port=%d", tls_dummy_port); + execlp("@Python3_EXECUTABLE@", "@Python3_EXECUTABLE@", "tls_server.py", hostname, port, NULL); + } + +#endif + sleep(10); + return OK; +} + + +static int set_tls_dummy_options(const char *options) +{ + MA_PVIO_CINFO cinfo; + MARIADB_PVIO *pvio; + MYSQL *mysql= mysql_init(NULL); + char buffer[1024]; + size_t len; + + cinfo.type= PVIO_TYPE_SOCKET; + cinfo.host= tls_dummy_host; + cinfo.port= tls_dummy_port; + cinfo.mysql = mysql; + + if (!(pvio= ma_pvio_init(&cinfo))) + { + diag("pvio_init failed"); + return 1; + } + if (ma_pvio_connect(pvio, &cinfo)) + { + diag("connect failed: %s", mysql_error(mysql)); + return 1; + } + + if (!(len= ma_pvio_read(pvio, (uchar *)buffer, sizeof(buffer)))) + { + diag("Error reading server packet"); + return 1; + } + + if (!ma_pvio_write(pvio, (uchar *)options, strlen(options))) + { + diag("Error writing configuration"); + return 1; + } + + ma_pvio_close(pvio); + return OK; +} + + +static int test_init(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + my_bool verify= 0; + int rc; + int ret= FAIL; + MYSQL_RES *result; + MYSQL_ROW row; + char query[1024]; + + diag("test_init"); + + /* Force use of TLS */ + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + /* Don't verify peer certificate */ + mysql_optionsv(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0, 0), mysql_error(my)); + + rc= mysql_query(mysql, "SELECT @@ssl_cert"); + check_mysql_rc(rc, mysql); + + if ((result= mysql_store_result(mysql))) + { + row= mysql_fetch_row(result); + auto_generated_cert = (row[0] == NULL); + mysql_free_result(result); + } + + diag("autogenerated: %d", auto_generated_cert); + ignore_self_signed= ignore_self_signed_cert_error(mysql); + diag("ignore self signed certificates: %d", ignore_self_signed); + + ret= OK; + + sprintf(query, "CREATE OR REPLACE USER 'tls_user1'@'%s' IDENTIFIED BY 'tls_password'", hostname); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + sprintf(query, "CREATE OR REPLACE USER 'tls_user2'@'%s'", hostname); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + mysql_close(mysql); + return ret; +} + +static int test_no_cert_check(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + my_bool verify= 0; + + /* Force use of TLS */ + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + /* Don't verify peer certificate */ + mysql_optionsv(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0, 0), mysql_error(my)); + + FAIL_IF(!mysql_get_ssl_cipher(mysql), "No TLS connection"); + + mysql_close(mysql); + return OK; +} + +static int test_ca_cert_check(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + int rc= FAIL; + +#ifdef HAVE_SCHANNEL + diag("Not supported by schannel"); + return SKIP; +#endif + + if (ignore_self_signed) + { + diag("Test doesn't work on trusted connection"); + return SKIP; + } + + /* Force use of TLS with faked ca, which contains the server + certificate */ + mysql_ssl_set(mysql, NULL, NULL, "./ca_cert.pem", NULL, NULL); + + if (my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0, 0)) + { + if (auto_generated_cert) + { + diag("expected hostname mismatch for autogenerated certificate"); + goto end; + } + } else { + if (auto_generated_cert) + { + rc= OK; + goto end; + } + diag("Error: %s", mysql_error(mysql)); + goto end; + } + + FAIL_IF(!mysql_get_ssl_cipher(mysql), "No TLS connection"); + rc= OK; +end: + mysql_close(mysql); + return rc; +} + +static int test_fp(MYSQL *my __attribute__((unused))) +{ + unsigned int hash_size[3] = {256, 384, 512}; + int i; + my_bool verify= 0; + MYSQL *mysql= mysql_init(NULL); + + + /* Force use of TLS */ + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + /* Don't verify peer certificate */ + mysql_optionsv(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0, 1), mysql_error(my)); + + for (i=0; i < 3; i++) + { + MYSQL *mysql1= mysql_init(NULL); + MARIADB_X509_INFO *info; + + diag("testing SHA%d fingerprint", hash_size[i]); + + mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info, hash_size[i]); + + mysql_optionsv(mysql1, MARIADB_OPT_SSL_FP, info->fingerprint); + FAIL_IF(!my_test_connect(mysql1, hostname, username, password, schema, + port, socketname, 0, 0), mysql_error(my)); + + FAIL_IF(!mysql_get_ssl_cipher(mysql), "No TLS connection"); + mysql_close(mysql1); + } + mysql_close(mysql); + return OK; +} + +static int test_fp_colon(MYSQL *my __attribute__((unused))) +{ + unsigned int hash_size[3] = {256, 384, 512}; + int i; + my_bool verify= 0; + MYSQL *mysql= mysql_init(NULL); + char fp[200]; + + + /* Force use of TLS */ + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + /* Don't verify peer certificate */ + mysql_optionsv(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0, 0), mysql_error(my)); + + for (i=0; i < 3; i++) + { + MYSQL *mysql1= mysql_init(NULL); + MARIADB_X509_INFO *info; + char *p= &fp[0]; + size_t j; + + diag("testing SHA%d fingerprint with colons", hash_size[i]); + + memset(fp, 0, sizeof(fp)); + + mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info, hash_size[i]); + + for (j=0; j < strlen(info->fingerprint) / 2; j++) + { + memcpy(p, info->fingerprint + j * 2, 2); + p+= 2; + *p++= ':'; + } + p--; + *p= 0; + + + diag ("fp: %s", fp); + mysql_optionsv(mysql1, MARIADB_OPT_SSL_FP, fp); + FAIL_IF(!my_test_connect(mysql1, hostname, username, password, schema, + port, socketname, 0, 0), mysql_error(my)); + + FAIL_IF(!mysql_get_ssl_cipher(mysql), "No TLS connection"); + mysql_close(mysql1); + } + mysql_close(mysql); + return OK; +} + + +static int test_peer_cert_info_fp(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + my_bool verify= 0; + MARIADB_X509_INFO *info; + char old_fp[129] = {0}; + int i; + unsigned int hash_size[3] = {256, 384, 512}; + + /* Force use of TLS */ + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + /* Don't verify peer certificate */ + mysql_optionsv(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0, 0), mysql_error(my)); + + for (i=0; i < 3; i++) + { + mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info, hash_size[i]); + FAIL_IF(!info->version, "Wrong certificate version=0"); + + FAIL_IF(!strcmp(old_fp, info->fingerprint), "Fingerprint was not updated"); + if (strlen(info->fingerprint) != hash_size[i] / 4) + { + diag("Got fingerprint length %zu, expected %u", strlen(info->fingerprint), hash_size[i] / 4); + return FAIL; + } + strcpy(old_fp, info->fingerprint); + } + + mysql_close(mysql); + return OK; +} + +static int test_fp_garbage(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + char fp[129]; + + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + /* 1) use non hex chars */ + strcpy(fp, "IJKLMNOPQRSTUVWXYZABCDEXXXXXX67IJKLMNOPQRSTUVWXYZABCDEXXXXXX679X"); + if (mysql_optionsv(mysql, MARIADB_OPT_SSL_FP, fp)) + return FAIL; + + if (my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0, 0)) + { + diag("Error expected"); + diag("%s", mysql_get_ssl_cipher(mysql)); + return FAIL; + } + CHECK_TLS_FLAGS(mysql, MARIADB_TLS_VERIFY_FINGERPRINT, "Fingerprint verification flag not set"); + mysql_close(mysql); + return OK; +} + +static int test_pw_check(MYSQL *my) +{ + MYSQL *mysql= mysql_init(NULL); + int rc; + my_bool local_safe_connection= ignore_self_signed_cert_error(my); + char query[1024]; + int ret= FAIL; + + /* connect with pasword */ + sprintf(query, "CREATE OR REPLACE USER '%s'@'%s' IDENTIFIED BY '%s'", "tlsuser", this_host, "mypw"); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + diag("expected to pass with self signed"); + if (!my_test_connect(mysql, hostname, "tlsuser", "mypw", NULL, 0, NULL, 0, 0)) + { + diag("Error (password + self signed)"); + goto end; + } + diag("ok"); + + mysql_close(mysql); + mysql= mysql_init(NULL); + + /* connect without pasword */ + if (!local_safe_connection) + { + sprintf(query, "CREATE OR REPLACE USER '%s'@'%s'", "tlsuser", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + if (my_test_connect(mysql, hostname, "tlsuser", NULL, NULL, 0, NULL, 0, 0)) + { + diag("Error expected since no password was specified"); + goto end; + } + + CHECK_TLS_FLAGS(mysql, MARIADB_TLS_VERIFY_TRUST, "Not trusted flag not set") + + mysql_close(mysql); + mysql = mysql_init(NULL); + mysql_optionsv(mysql, MARIADB_OPT_RESTRICTED_AUTH, "mysql_native_password, mysql_old_password"); + + diag("expected to fail with old_password plugin"); + /* Test with an authentication plugin, which doesn't support hashing */ + sprintf(query, "CREATE OR REPLACE USER '%s'@'%s' IDENTIFIED VIA mysql_old_password USING '7c786c222596437b'", "tlsuser", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + rc= mysql_query(my, "SET GLOBAL secure_auth=0"); + check_mysql_rc(rc, my); + + if (my_test_connect(mysql, hostname, "tlsuser", "foo", NULL, 0, NULL, 0, 0)) + { + diag("Error expected since old_password was specified"); + goto end; + } + diag("ok"); + } + + ret= OK; +end: + mysql_close(mysql); + return ret; +} + +static int test_cert_expired(MYSQL *my __attribute__((unused))) +{ + int ret= OK; + MYSQL *mysql; + + /* Set validity in future */ + if (set_tls_dummy_options("CMD:create_new=True validityStartInSeconds=10000 validityEndInSeconds=2000")) + { + diag("Error when setting TLS options"); + return FAIL; + } + + mysql= mysql_init(NULL); + + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + if (my_test_connect(mysql, tls_dummy_host, "tlsuser", "foo", NULL, tls_dummy_port, NULL, 0, 0)) + { + diag("Error expected since cert is not valid yet"); + return FAIL; + } + mysql_close(mysql); + + /* Set cert expired */ + if (set_tls_dummy_options("CMD:create_new=True validityStartInSeconds=-20000 validityEndInSeconds=-10000")) + { + diag("Error when setting TLS options"); + return FAIL; + } + + mysql= mysql_init(NULL); + + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + if (my_test_connect(mysql, tls_dummy_host, "tlsuser", "foo", NULL, tls_dummy_port, NULL, 0, 0)) + { + diag("Error expected since cert is not valid yet"); + return FAIL; + } + CHECK_TLS_FLAGS(mysql, MARIADB_TLS_VERIFY_PERIOD, "NotBefore/NotAfter flag not set") + mysql_close(mysql); + + + return ret; +} + +static int test_wrong_ca(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + + mysql_ssl_set(mysql, NULL, NULL, "selfsigned.pem", NULL, NULL); + if (my_test_connect(mysql, hostname, "tlsuser", "foo", NULL, 0, NULL, 0, 0)) + { + diag("self signed error expected"); + return FAIL; + } + CHECK_TLS_FLAGS(mysql, MARIADB_TLS_VERIFY_TRUST, "Not trusted flag not set") + mysql_close(mysql); + return OK; +} + +static int test_crl(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql; + + /* Set validity in future */ + if (set_tls_dummy_options("CMD:KEY_FILE='./certs/server-key.pem' CERT_FILE='./certs/server-cert.pem'")) + { + diag("Error when setting TLS options"); + return FAIL; + } + + mysql= mysql_init(NULL); + + mysql_optionsv(mysql, MYSQL_OPT_SSL_CRL, "./certs/server-cert.crl"); + mysql_ssl_set(mysql, NULL, NULL, "./certs/cacert.pem", NULL, NULL); + + if (my_test_connect(mysql, tls_dummy_host, "tlsuser", "foo", NULL, tls_dummy_port, NULL, 0, 0)) + { + diag("Error expected since cert revoked"); + return FAIL; + } + CHECK_TLS_FLAGS(mysql, MARIADB_TLS_VERIFY_REVOKED, "Revocation flag not set") + mysql_close(mysql); + return OK; +} + +static int test_crl_with_fp(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql; + /* hardcoded: openssl x509 -noout -fingerprint -sha256 -inform pem -in certs/server-cert.pem */ + const char *fp= "4B:EE:BB:12:6D:30:1A:B2:2A:4A:F8:6D:82:7F:63:44:1F:8F:F4:6B:D3:F2:CA:68:0B:D5:E3:5D:1C:47:A7:16"; + + /* Set validity in future */ + if (set_tls_dummy_options("CMD:KEY_FILE='./certs/server-key.pem' CERT_FILE='./certs/server-cert.pem'")) + { + diag("Error when setting TLS options"); + return FAIL; + } + + mysql= mysql_init(NULL); + mysql_optionsv(mysql, MARIADB_OPT_SSL_FP, fp); + + mysql_optionsv(mysql, MYSQL_OPT_SSL_CRL, "./certs/server-cert.crl"); + mysql_ssl_set(mysql, NULL, NULL, "./certs/cacert.pem", NULL, NULL); + + if (my_test_connect(mysql, tls_dummy_host, "tlsuser", "foo", NULL, tls_dummy_port, NULL, 0, 0)) + { + diag("Error expected since cert revoked"); + return FAIL; + } + CHECK_TLS_FLAGS(mysql, MARIADB_TLS_VERIFY_REVOKED, "Revocation flag not set") + mysql_close(mysql); + return OK; +} + + +static int test_wrong_hostname(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql; + + /* Set validity in future */ + if (set_tls_dummy_options("CMD:create_new=True")) + { + diag("Error when setting TLS options"); + return FAIL; + } + + mysql= mysql_init(NULL); + + mysql_ssl_set(mysql, NULL, NULL, "./selfsigned.pem", NULL, NULL); + + if (my_test_connect(mysql, tls_dummy_host, "tlsuser", "foo", NULL, tls_dummy_port, NULL, 0, 0)) + { + diag("Error expected since hostname doesn't match"); + return FAIL; + } + CHECK_TLS_FLAGS(mysql, MARIADB_TLS_VERIFY_HOST, "Host name validation flag not set") + mysql_close(mysql); + return OK; +} + +static int stop_tls_server(MYSQL *my __attribute__((unused))) +{ + if (set_tls_dummy_options("QUIT")) + { + diag("Error when shutting down tls dummy server"); + return FAIL; + } + return OK; +} + +struct my_tests_st my_tests[] = { + /* Don't add test above, test_init needs to be run first */ + {"test_start_tls_server", test_start_tls_server, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_init", test_init, TEST_CONNECTION_NONE, 0, NULL, NULL}, + /* Here you can add more tests */ + {"test_cert_expired", test_cert_expired, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_pw_check", test_pw_check, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_ca_cert_check", test_ca_cert_check, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_fp_garbage", test_fp_garbage, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_peer_cert_info_fp", test_peer_cert_info_fp, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_no_cert_check", test_no_cert_check, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_fp", test_fp, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_fp_colon", test_fp_colon, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_wrong_ca", test_wrong_ca, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_wrong_hostname", test_wrong_hostname, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_crl", test_crl, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_crl_with_fp", test_crl_with_fp, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"stop_tls_server", stop_tls_server, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, 0} +}; + + +int main(int argc, char **argv) +{ + char *dummy_port; + + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + if (!(tls_dummy_host= getenv("MARIADB_TLS_DUMMY_HOST"))) + tls_dummy_host= (char *)"127.0.0.1"; + if ((dummy_port= getenv("MARIADB_TLS_DUMMY_PORT"))) + tls_dummy_port= atoi(dummy_port); + if (!tls_dummy_port) + tls_dummy_port= 50000; + + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/unittest/libmariadb/tls_server.py b/unittest/libmariadb/tls_server.py new file mode 100755 index 00000000..9ecded16 --- /dev/null +++ b/unittest/libmariadb/tls_server.py @@ -0,0 +1,157 @@ +import socket +import ssl +import argparse +from ast import literal_eval +from OpenSSL import crypto, SSL +import os + +class TlsServer(): + + def __init__(self, *args, **kwargs): + self.host= kwargs.pop("host", "127.0.0.1") + self.port= kwargs.pop("port", 50000) + self.server= None + self.end= False + + try: + self.server= socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.server.bind((self.host, self.port)) + print("# tls dummy_server started: ", self.host, self.port) + self.server.listen() + except Exception as e: + print("Couldn't start tls_server") + print(e) + + def check_server(self): + if not self.server: + raise Exception("Server not started") + + + def send_server_hello(self, conn): + self.check_server() + try: + conn.sendall(server_hello) + except Exception as e: + print("Couldn't send server_hello") + print(e) + return 0 + return 1 + + def generate_cert(self, + create_new=False, + create_crl=False, + emailAddress="emailAddress", + commonName="commonName", + countryName="NT", + localityName="localityName", + stateOrProvinceName="stateOrProvinceName", + organizationName="organizationName", + organizationUnitName="organizationUnitName", + serialNumber=123, + validityStartInSeconds=0, + validityEndInSeconds=10*365*24*60*60, + KEY_FILE = "privkey.pem", + CRL_FILE = "selfsigned.crl", + CERT_FILE="selfsigned.pem"): + + + self.key_file= KEY_FILE + self.cert_file= CERT_FILE + self.crl_file = CRL_FILE + + if create_new: + try: + k = crypto.PKey() + k.generate_key(crypto.TYPE_RSA, 4096) + # create a self-signed cert + cert = crypto.X509() + cert.get_subject().C = countryName + cert.get_subject().ST = stateOrProvinceName + cert.get_subject().L = localityName + cert.get_subject().O = organizationName + cert.get_subject().OU = organizationUnitName + cert.get_subject().CN = commonName + cert.get_subject().emailAddress = emailAddress + cert.set_serial_number(serialNumber) + cert.gmtime_adj_notBefore(validityStartInSeconds) + cert.gmtime_adj_notAfter(validityEndInSeconds) + cert.set_issuer(cert.get_subject()) + cert.set_pubkey(k) + cert.sign(k, 'sha512') + with open(CERT_FILE, "wt") as f: + f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode("utf-8")) + with open(KEY_FILE, "wt") as f: + f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode("utf-8")) + return 1 + except Exception as e: + return 0 + return 1 + + + def set_tls_context(self, reply): + kwargs= {} + if len(reply) > 0: + cmds= reply.decode() + kwargs= dict((k, literal_eval(v)) for k, v in (pair.split('=') for pair in cmds.split())) + print("# command: ", kwargs) + if self.generate_cert(**kwargs): + print("# loading certs", self.cert_file, self.key_file) + self.context= ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + self.context.load_cert_chain(self.cert_file, self.key_file) + return 1 + return 0 + + + def accept(self): + self.check_server() + conn, addr= self.server.accept() + return (conn, addr) + + def run(self): + while not self.end: + connection, address= self.accept() + print("# new connection") + self.send_server_hello(connection) + reply= connection.recv(4096) + if reply[:4] == b'CMD:': + if self.set_tls_context(reply[4:]): + connection.sendall(b'OK') + elif reply[:4] == b'QUIT': + print("# exiting tls_dummy_server") + try: + connection.close() + except: + pass + return + else: + try: + tls_sock= self.context.wrap_socket(connection, server_side=True) + except Exception as e: + print("error occured") + print(e) + connection.close() + connection.close() + +# Hardcoded server hello packet (captured from MariaDB Server 11.4.2) +server_hello = b'R\x00\x00\x00\n11.4.2-MariaDB\x00\xff\x01\x00\x00Nv\ +*hQ;qK\x00\xfe\xff\x08\x02\x00\xff\x81\x15\x00\x00\x00\ +\x00\x00\x00\x1d\x00\x00\x00`$-VIJyC!x[?\x00mysql_native_password\x00' + + +if __name__ == '__main__': + + parser= argparse.ArgumentParser( + prog='tls_server', + description='Simple TLS dummy test server') + parser.add_argument('--host', help='Hostaddress of TLS test server (Default 127.0.0.1)') + parser.add_argument('--port', help='Port of TLS test server. (Default 50000)') + + args= parser.parse_args() + + if not (port := args.port): + port= 50000; + if not (host := args.host): + host= "127.0.0.1" + server= TlsServer(host=host, port=int(port)) + print("# Starting tls_dummy_server") + server.run()