diff --git a/libmariadb/ma_net.c b/libmariadb/ma_net.c index 28d5bfcc..41f42dcf 100644 --- a/libmariadb/ma_net.c +++ b/libmariadb/ma_net.c @@ -450,19 +450,7 @@ ma_real_read(NET *net, size_t *complen) if (i == 0) { /* First parts is packet length */ ulong helping; - if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr) - { - if (net->buff[net->where_b] != (uchar) 255) - { -#ifdef EXTRA_DEBUG - fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n", - (int) net->buff[net->where_b + 3], - (uint) (uchar) net->pkt_nr); -#endif - } - len= packet_error; - goto end; - } + net->pkt_nr= net->buff[net->where_b + 3]; net->compress_pkt_nr= ++net->pkt_nr; #ifdef HAVE_COMPRESS if (net->compress) diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index 775e3a6e..5dab2d82 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -525,7 +525,6 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) ((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)) diff --git a/libmariadb/ma_tls.c b/libmariadb/ma_tls.c index 71d171f8..a3a5176d 100644 --- a/libmariadb/ma_tls.c +++ b/libmariadb/ma_tls.c @@ -124,10 +124,10 @@ static my_bool ma_pvio_tls_compare_fp(const char *fp1, unsigned int fp1_len, { char hexstr[64]; + fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len); if (fp1_len != fp2_len) return 1; - fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len); #ifdef WIN32 if (strnicmp(hexstr, fp2, fp1_len) != 0) #else @@ -140,10 +140,12 @@ static my_bool ma_pvio_tls_compare_fp(const char *fp1, unsigned int fp1_len, my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list) { unsigned int cert_fp_len= 64; - char cert_fp[64]; + char *cert_fp= NULL; my_bool rc=1; MYSQL *mysql= ctls->pvio->mysql; + cert_fp= (char *)malloc(cert_fp_len); + if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1) goto end; if (fp) @@ -154,9 +156,7 @@ my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_l char buff[255]; if (!(fp = ma_open(fp_list, "r", mysql))) - { - return 1; - } + goto end; while (ma_gets(buff, sizeof(buff)-1, fp)) { @@ -181,6 +181,8 @@ my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_l } end: + if (cert_fp) + free(cert_fp); if (rc) { my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index 8508a67a..8c7f598f 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -1696,22 +1696,18 @@ int stmt_read_execute_response(MYSQL_STMT *stmt) return(0); } -int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) + +int stmt_execute_send(MYSQL_STMT *stmt) { MYSQL *mysql= stmt->mysql; char *request; int ret; size_t request_len= 0; - enum mariadb_com_multi multi= MARIADB_COM_MULTI_END; - if (!stmt->mysql) { SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); return(1); } - - mysql_get_optionv(mysql, MARIADB_OPT_COM_MULTI, &multi); - if (stmt->state < MYSQL_STMT_PREPARED) { SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); @@ -1761,11 +1757,12 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) mysql->net.last_error); return(1); } + return 0; +} - if (multi == MARIADB_COM_MULTI_BEGIN) - return(0); - - return(stmt_read_execute_response(stmt)); +int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) +{ + return stmt_execute_send(stmt) || stmt_read_execute_response(stmt); } static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) @@ -2074,7 +2071,6 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, const char *stmt_str, size_t length) { - enum mariadb_com_multi multi= MARIADB_COM_MULTI_BEGIN; MYSQL *mysql= stmt->mysql; if (!mysql) @@ -2083,9 +2079,6 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, goto fail; } - if (mysql_optionsv(mysql, MARIADB_OPT_COM_MULTI, &multi)) - goto fail; - if (!stmt->mysql) { SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); @@ -2095,8 +2088,6 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, if (length == (size_t) -1) length= strlen(stmt_str); - mysql_get_optionv(mysql, MARIADB_OPT_COM_MULTI, &multi); - /* clear flags */ CLEAR_CLIENT_STMT_ERROR(stmt); CLEAR_CLIENT_ERROR(stmt->mysql); @@ -2128,13 +2119,10 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, stmt->state= MYSQL_STMT_PREPARED; /* Since we can't determine stmt_id here, we need to set it to -1, so server will know that the * execute command belongs to previous prepare */ - stmt->stmt_id= -1; - if (mysql_stmt_execute(stmt)) - goto fail; - /* flush multi buffer */ - multi= MARIADB_COM_MULTI_END; - if (mysql_optionsv(mysql, MARIADB_OPT_COM_MULTI, &multi)) + stmt->stmt_id= -1; + ma_net_clear(&mysql->net); + if (stmt_execute_send(stmt)) goto fail; /* read prepare response */ diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index 22077d30..e37e2fb8 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,12 @@ extern unsigned int mariadb_deinitialize_ssl; static int my_verify_callback(gnutls_session_t ssl); +struct st_gnutls_data { + MYSQL *mysql; + gnutls_privkey_t key; + gnutls_pcert_st cert; +}; + struct st_cipher_map { const char *openssl_name; const char *priority; @@ -96,6 +103,20 @@ const struct st_cipher_map gtls_ciphers[]= {NULL, NULL, 0, 0, 0} }; +/* free data assigned to the connection */ +static void free_gnutls_data(struct st_gnutls_data *data) +{ + if (data) + { + if (data->key) + gnutls_privkey_deinit(data->key); + gnutls_pcert_deinit(&data->cert); + free(data); + } +} + +/* map the gnutls cipher suite (defined by key exchange algorithm, cipher + and mac algorithm) to the corresponding OpenSSL cipher name */ static const char *openssl_cipher_name(gnutls_kx_algorithm_t kx, gnutls_cipher_algorithm_t cipher, gnutls_mac_algorithm_t mac) @@ -112,6 +133,7 @@ static const char *openssl_cipher_name(gnutls_kx_algorithm_t kx, return NULL; } +/* get priority string for a given openssl cipher name */ static const char *get_priority(const char *cipher_name) { unsigned int i= 0; @@ -126,7 +148,7 @@ static const char *get_priority(const char *cipher_name) #define MAX_SSL_ERR_LEN 100 -static void ma_tls_set_error(MYSQL *mysql, int ssl_errno) +static void ma_tls_set_error(MYSQL *mysql, void *ssl, int ssl_errno) { char ssl_error[MAX_SSL_ERR_LEN]; const char *ssl_error_reason; @@ -137,6 +159,21 @@ static void ma_tls_set_error(MYSQL *mysql, int ssl_errno) pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); return; } + + /* give a more descriptive error message for alerts */ + if (ssl_errno == GNUTLS_E_FATAL_ALERT_RECEIVED) + { + gnutls_alert_description_t alert_desc; + const char *alert_name; + alert_desc= gnutls_alert_get((gnutls_session_t)ssl); + alert_name= gnutls_alert_get_name(alert_desc); + snprintf(ssl_error, MAX_SSL_ERR_LEN, "fatal alert received: %s", + alert_name); + pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, + ssl_error); + return; + } + if ((ssl_error_reason= gnutls_strerror(ssl_errno))) { pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, @@ -249,15 +286,7 @@ static int ma_gnutls_set_ciphers(gnutls_session_t ssl, char *cipher_str) while (token) { const char *p= get_priority(token); - /* if cipher was not found, we pass the original token to - the priority string, this will allow to specify gnutls - specific settings via cipher */ - if (!p) - { - strncat(prio, ":", PRIO_SIZE - strlen(prio) - 1); - strncat(prio, token, PRIO_SIZE - strlen(prio) - 1); - } - else + if (p) strncat(prio, p, PRIO_SIZE - strlen(prio) - 1); token = strtok(NULL, ":"); } @@ -266,8 +295,6 @@ static int ma_gnutls_set_ciphers(gnutls_session_t ssl, char *cipher_str) static int ma_tls_set_certs(MYSQL *mysql) { - char *certfile= mysql->options.ssl_cert, - *keyfile= mysql->options.ssl_key; int ssl_error= 0; if (mysql->options.ssl_ca) @@ -282,32 +309,78 @@ static int ma_tls_set_certs(MYSQL *mysql) gnutls_certificate_set_verify_function(GNUTLS_xcred, my_verify_callback); - /* GNUTLS doesn't support ca_path */ - - if (keyfile && !certfile) - certfile= keyfile; - if (certfile && !keyfile) - keyfile= certfile; - - /* set key */ - if (certfile || keyfile) - { - if ((ssl_error= gnutls_certificate_set_x509_key_file2(GNUTLS_xcred, - certfile, keyfile, GNUTLS_X509_FMT_PEM, - OPT_HAS_EXT_VAL(mysql, tls_pw) ? mysql->options.extension->tls_pw : NULL, - 0)) < 0) - goto error; - } return 1; error: return ssl_error; } +static int +client_cert_callback(gnutls_session_t session, + const gnutls_datum_t * req_ca_rdn __attribute__((unused)), + int nreqs __attribute__((unused)), + const gnutls_pk_algorithm_t * sign_algos __attribute__((unused)), + int sign_algos_length __attribute__((unused)), + gnutls_pcert_st ** pcert, + unsigned int *pcert_length, gnutls_privkey_t * pkey) +{ + struct st_gnutls_data *session_data; + char *certfile, *keyfile; + gnutls_datum_t data; + MYSQL *mysql; + gnutls_certificate_type_t type= gnutls_certificate_type_get(session); + + session_data= (struct st_gnutls_data *)gnutls_session_get_ptr(session); + + if (!session_data->mysql || + type != GNUTLS_CRT_X509) + return -1; + + mysql= session_data->mysql; + + certfile= session_data->mysql->options.ssl_cert; + keyfile= session_data->mysql->options.ssl_key; + + if (!certfile && !keyfile) + return 0; + if (keyfile && !certfile) + certfile= keyfile; + if (certfile && !keyfile) + keyfile= certfile; + + if (gnutls_load_file(certfile, &data) < 0) + return -1; + if (gnutls_pcert_import_x509_raw(&session_data->cert, &data, GNUTLS_X509_FMT_PEM, 0) < 0) + { + gnutls_free(data.data); + return -1; + } + gnutls_free(data.data); + + if (gnutls_load_file(keyfile, &data) < 0) + return -1; + gnutls_privkey_init(&session_data->key); + if (gnutls_privkey_import_x509_raw(session_data->key, &data, + GNUTLS_X509_FMT_PEM, + mysql->options.extension ? mysql->options.extension->tls_pw : NULL, + 0) < 0) + { + gnutls_free(data.data); + return -1; + } + gnutls_free(data.data); + + *pcert_length= 1; + *pcert= &session_data->cert; + *pkey= session_data->key; + return 0; +} + void *ma_tls_init(MYSQL *mysql) { gnutls_session_t ssl= NULL; int ssl_error= 0; + struct st_gnutls_data *data= NULL; pthread_mutex_lock(&LOCK_gnutls_config); @@ -316,19 +389,28 @@ void *ma_tls_init(MYSQL *mysql) if ((ssl_error = gnutls_init(&ssl, GNUTLS_CLIENT & GNUTLS_NONBLOCK)) < 0) goto error; - gnutls_session_set_ptr(ssl, (void *)mysql); + + if (!(data= (struct st_gnutls_data *)calloc(1, sizeof(struct st_gnutls_data)))) + goto error; + + data->mysql= mysql; + gnutls_certificate_set_retrieve_function2(GNUTLS_xcred, client_cert_callback); + gnutls_session_set_ptr(ssl, (void *)data); ssl_error= ma_gnutls_set_ciphers(ssl, mysql->options.ssl_cipher); if (ssl_error < 0) goto error; + /* we don't load private key and cert by default - if the server requests + a client certificate we will send it via callback function */ if ((ssl_error= gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, GNUTLS_xcred)) < 0) goto error; pthread_mutex_unlock(&LOCK_gnutls_config); return (void *)ssl; error: - ma_tls_set_error(mysql, ssl_error); + free_gnutls_data(data); + ma_tls_set_error(mysql, ssl, ssl_error); if (ssl) gnutls_deinit(ssl); pthread_mutex_unlock(&LOCK_gnutls_config); @@ -362,7 +444,9 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) MYSQL *mysql; MARIADB_PVIO *pvio; int ret; - mysql= (MYSQL *)gnutls_session_get_ptr(ssl); + struct st_gnutls_data *data; + data= (struct st_gnutls_data *)gnutls_session_get_ptr(ssl); + mysql= data->mysql; if (!mysql) return 1; @@ -386,16 +470,16 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) if (ret < 0) { - ma_tls_set_error(mysql, ret); + ma_tls_set_error(mysql, ssl, ret); /* restore blocking mode */ gnutls_deinit((gnutls_session_t )ctls->ssl); + free_gnutls_data(data); ctls->ssl= NULL; if (!blocking) pvio->methods->blocking(pvio, FALSE, 0); return 1; } ctls->ssl= (void *)ssl; - return 0; } @@ -413,7 +497,15 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) { if (ctls->ssl) { - gnutls_bye((gnutls_session_t )ctls->ssl, GNUTLS_SHUT_WR); + MARIADB_PVIO *pvio= ctls->pvio; + struct st_gnutls_data *data= + (struct st_gnutls_data *)gnutls_session_get_ptr(ctls->ssl); + /* this would be the correct way, however can't dectect afterwards + if the socket is closed or not, so we don't send encrypted + finish alert. + rc= gnutls_bye((gnutls_session_t )ctls->ssl, GNUTLS_SHUT_WR); + */ + free_gnutls_data(data); gnutls_deinit((gnutls_session_t )ctls->ssl); ctls->ssl= NULL; } @@ -443,14 +535,19 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls) static int my_verify_callback(gnutls_session_t ssl) { - unsigned int status; + unsigned int status= 0; const gnutls_datum_t *cert_list; unsigned int cert_list_size; - MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl); - MARIADB_PVIO *pvio= mysql->net.pvio; + struct st_gnutls_data *data= (struct st_gnutls_data *)gnutls_session_get_ptr(ssl); + MYSQL *mysql; + MARIADB_PVIO *pvio; + gnutls_x509_crt_t cert; const char *hostname; + mysql= data->mysql; + pvio= mysql->net.pvio; + /* read hostname */ hostname = mysql->host; @@ -518,11 +615,13 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l size_t fp_len= len; const gnutls_datum_t *cert_list; unsigned int cert_list_size; + struct st_gnutls_data *data; if (!ctls || !ctls->ssl) return 0; - mysql= (MYSQL *)gnutls_session_get_ptr(ctls->ssl); + data= (struct st_gnutls_data *)gnutls_session_get_ptr(ctls->ssl); + mysql= (MYSQL *)data->mysql; cert_list = gnutls_certificate_get_peers (ctls->ssl, &cert_list_size); if (cert_list == NULL) diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index b32e0d8c..f550bea7 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -78,7 +78,6 @@ void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo) pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "The Local Security Authority cannot be contacted"); break; default: - __debugbreak(); pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error (0x%x)", ErrorNo); } } @@ -796,41 +795,41 @@ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, } /* }}} */ -my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags) +my_bool ma_schannel_verify_certs(SC_CTX *sctx) { SECURITY_STATUS sRet; - DWORD flags; - MARIADB_PVIO *pvio= sctx->mysql->net.pvio; - PCCERT_CONTEXT pServerCert= NULL; - PCCERT_CONTEXT ca_CTX = NULL; - PCCRL_CONTEXT crl_CTX = NULL; - my_bool is_Ok = 0; + MYSQL *mysql=sctx->mysql; + MARIADB_PVIO *pvio= mysql->net.pvio; + const char *ca_file= mysql->options.ssl_ca; + const char *crl_file= mysql->options.extension ? mysql->options.extension->ssl_crl : NULL; + PCCERT_CONTEXT pServerCert= NULL; + CRL_CONTEXT *crl_ctx= NULL; + CERT_CONTEXT *ca_ctx= NULL; + int ret= 0; + + if (!ca_file && !crl_file) + return 1; + + if (ca_file && !(ca_ctx = ma_schannel_create_cert_context(pvio, ca_file))) + goto end; + + if (crl_file && !(crl_ctx= (CRL_CONTEXT *)ma_schannel_create_crl_context(pvio, mysql->options.extension->ssl_crl))) + goto end; + if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK) { ma_schannel_set_sec_error(pvio, sRet); - return 0; + goto end; } - if (ca_Check) + if (ca_ctx) { - flags = CERT_STORE_SIGNATURE_FLAG | - CERT_STORE_TIME_VALIDITY_FLAG; - - while ((ca_CTX = CertFindCertificateInStore(ca_CertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, - 0, CERT_FIND_ANY, 0, ca_CTX)) && !is_Ok) - { - if (sRet = CertVerifySubjectCertificateContext(pServerCert, ca_CTX, &flags)) - is_Ok = 1; - } - - if (ca_CTX) - CertFreeCertificateContext(ca_CTX); - - if (!is_Ok) + DWORD flags = CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG; + if (!CertVerifySubjectCertificateContext(pServerCert, ca_ctx, &flags)) { ma_schannel_set_win_error(pvio); - return 0; + goto end; } if (flags) @@ -843,30 +842,36 @@ my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags) pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: certificate has expired"); else pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Unknown error during certificate validation"); - return 0; + goto end; } } - /* Check if none of the certificates in the certificate chain have been revoked. */ - if (crl_Check) + + /* Check certificates in the certificate chain have been revoked. */ + if (crl_ctx) { - while ((crl_CTX = CertEnumCRLsInStore(crl_CertStore, crl_CTX))) + if (!CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pServerCert->pCertInfo, 1, &crl_ctx->pCrlInfo)) { - PCRL_INFO Info[1]; - - Info[0] = crl_CTX->pCrlInfo; - if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - pServerCert->pCertInfo, - 1, Info))) - { - CertFreeCRLContext(crl_CTX); - pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CRL Revocation failed"); - return 0; - } + pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: CRL Revocation test failed"); + goto end; } - CertFreeCRLContext(crl_CTX); } - return 1; + ret= 1; + +end: + if (crl_ctx) + { + CertFreeCRLContext(crl_ctx); + } + if (ca_ctx) + { + CertFreeCertificateContext(ca_ctx); + } + if (pServerCert) + { + CertFreeCertificateContext(pServerCert); + } + return ret; } diff --git a/libmariadb/secure/ma_schannel.h b/libmariadb/secure/ma_schannel.h index 98971ea2..27f48c1e 100644 --- a/libmariadb/secure/ma_schannel.h +++ b/libmariadb/secure/ma_schannel.h @@ -68,7 +68,7 @@ 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_load_private_key(MARIADB_PVIO *pvio, CERT_CONTEXT *ctx, char *key_file); PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_PVIO *pvio, const char *pem_file); -my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags); +my_bool ma_schannel_verify_certs(SC_CTX *sctx); size_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 0a6db6df..1b671ae0 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -30,7 +30,12 @@ #include #include -#define HAVE_TLS_SESSION_CACHE 1 +#ifdef HAVE_TLS_SESSION_CACHE +#undef HAVE_TLS_SESSION_CACHE +#endif +#if OPENSSL_USE_BIOMETHOD +#undef OPENSSL_USE_BIOMETHOD +#endif #ifndef HAVE_OPENSSL_DEFAULT #include #define ma_malloc(A,B) malloc((A)) @@ -329,6 +334,7 @@ int ma_tls_start(char *errmsg, size_t errmsg_len) ma_tls_get_error(errmsg, errmsg_len); goto end; } + SSL_CTX_set_options(SSL_context, SSL_OP_ALL); #ifdef HAVE_TLS_SESSION_CACHE SSL_CTX_set_session_cache_mode(SSL_context, SSL_SESS_CACHE_CLIENT); ma_tls_sessions= (MA_SSL_SESSION *)calloc(1, sizeof(struct st_ma_tls_session) * ma_tls_session_cache_size); @@ -411,15 +417,18 @@ int ma_tls_get_password(char *buf, int size, } -static int ma_tls_set_certs(MYSQL *mysql) +static int ma_tls_set_certs(MYSQL *mysql, SSL *ssl) { char *certfile= mysql->options.ssl_cert, *keyfile= mysql->options.ssl_key; + char *pw= (mysql->options.extension) ? + mysql->options.extension->tls_pw : NULL; + /* add cipher */ if ((mysql->options.ssl_cipher && mysql->options.ssl_cipher[0] != 0) && - SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0) + SSL_set_cipher_list(ssl, mysql->options.ssl_cipher) == 0) goto error; /* ca_file and ca_path */ @@ -439,35 +448,37 @@ static int ma_tls_set_certs(MYSQL *mysql) keyfile= certfile; /* set cert */ - if (certfile && certfile[0] != 0) - if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1) - goto error; - - /* If the private key file is encrypted, we need to register a callback function - * for providing password. */ - if (OPT_HAS_EXT_VAL(mysql, tls_pw)) + if (certfile && certfile[0] != 0) { - SSL_CTX_set_default_passwd_cb_userdata(SSL_context, (void *)mysql->options.extension->tls_pw); - SSL_CTX_set_default_passwd_cb(SSL_context, ma_tls_get_password); + if (SSL_CTX_use_certificate_chain_file(SSL_context, certfile) != 1 || + SSL_use_certificate_file(ssl, certfile, SSL_FILETYPE_PEM) != 1) + goto error; } - if (keyfile && keyfile[0]) { - if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1) + FILE *fp; + if ((fp= fopen(keyfile, "rb"))) { - unsigned long err= ERR_peek_error(); - if (!(ERR_GET_LIB(err) == ERR_LIB_X509 && - ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) - goto error; + EVP_PKEY *key= EVP_PKEY_new(); + PEM_read_PrivateKey(fp, &key, NULL, pw); + fclose(fp); + if (SSL_use_PrivateKey(ssl, key) != 1) + { + unsigned long err= ERR_peek_error(); + EVP_PKEY_free(key); + if (!(ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) + goto error; + } + EVP_PKEY_free(key); + } else { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + CER(CR_FILE_NOT_FOUND), keyfile); + return 1; } } - if (OPT_HAS_EXT_VAL(mysql, tls_pw)) - { - SSL_CTX_set_default_passwd_cb_userdata(SSL_context, NULL); - SSL_CTX_set_default_passwd_cb(SSL_context, NULL); - } /* verify key */ - if (certfile && !SSL_CTX_check_private_key(SSL_context)) + if (certfile && !SSL_check_private_key(ssl)) goto error; if (mysql->options.extension && @@ -499,14 +510,14 @@ void *ma_tls_init(MYSQL *mysql) #endif pthread_mutex_lock(&LOCK_openssl_config); - if (ma_tls_set_certs(mysql)) + if (!(ssl= SSL_new(SSL_context))) + goto error; + + if (ma_tls_set_certs(mysql, ssl)) { goto error; } - if (!(ssl= SSL_new(SSL_context))) - goto error; - if (!SSL_set_app_data(ssl, mysql)) goto error; @@ -703,8 +714,7 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls) unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len) { - EVP_MD *digest= (EVP_MD *)EVP_sha1(); - X509 *cert; + X509 *cert= NULL; MYSQL *mysql; unsigned int fp_len; @@ -718,7 +728,7 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate"); - return 0; + goto end; } if (len < EVP_MAX_MD_SIZE) @@ -726,18 +736,21 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), "Finger print buffer too small"); - return 0; + goto end; } - fp_len= len; - if (!X509_digest(cert, digest, (unsigned char *)fp, &fp_len)) + if (!X509_digest(cert, EVP_sha1(), (unsigned char *)fp, &fp_len)) { - ma_free(fp); my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), "invalid finger print of server certificate"); - return 0; + goto end; } + + X509_free(cert); return (fp_len); +end: + X509_free(cert); + return 0; } diff --git a/libmariadb/secure/schannel.c b/libmariadb/secure/schannel.c index 31376daa..adbb1883 100644 --- a/libmariadb/secure/schannel.c +++ b/libmariadb/secure/schannel.c @@ -26,9 +26,6 @@ extern my_bool ma_tls_initialized; -static pthread_mutex_t LOCK_schannel_config; -static pthread_mutex_t *LOCK_crypto= NULL; - struct st_cipher_suite { DWORD aid; CHAR *cipher; @@ -81,17 +78,6 @@ const struct st_cipher_suite valid_ciphers[] = void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo); void ma_schannel_set_win_error(MYSQL *mysql); -HCERTSTORE ca_CertStore= NULL, - crl_CertStore= NULL; -my_bool ca_Check = 1, crl_Check = 0; - - -static int ssl_thread_init() -{ - return 0; -} - - /* Initializes SSL and allocate global context SSL_context @@ -105,23 +91,8 @@ static int ssl_thread_init() */ int ma_tls_start(char *errmsg, size_t errmsg_len) { - if (!ma_tls_initialized) - { - pthread_mutex_init(&LOCK_schannel_config,MY_MUTEX_INIT_FAST); - pthread_mutex_lock(&LOCK_schannel_config); - if (!ca_CertStore) - { - if (!(ca_CertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, NULL)) || - !(crl_CertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, NULL))) - { - snprintf(errmsg, errmsg_len, "Can't open in-memory certstore. Error=%d", GetLastError()); - return 1; - } - - } - ma_tls_initialized = TRUE; - pthread_mutex_unlock(&LOCK_schannel_config); - } + + ma_tls_initialized = TRUE; return 0; } @@ -139,23 +110,6 @@ int ma_tls_start(char *errmsg, size_t errmsg_len) */ void ma_tls_end() { - if (ma_tls_initialized) - { - pthread_mutex_lock(&LOCK_schannel_config); - if (ca_CertStore) - { - CertCloseStore(ca_CertStore, 0); - ca_CertStore = 0; - } - if (crl_CertStore) - { - CertCloseStore(crl_CertStore, 0); - crl_CertStore = 0; - } - ma_tls_initialized= FALSE; - pthread_mutex_unlock(&LOCK_schannel_config); - pthread_mutex_destroy(&LOCK_schannel_config); - } return; } @@ -164,69 +118,30 @@ static int ma_tls_set_client_certs(MARIADB_TLS *ctls) { MYSQL *mysql= ctls->pvio->mysql; char *certfile= mysql->options.ssl_cert, - *keyfile= mysql->options.ssl_key, - *cafile= mysql->options.ssl_ca; - PCERT_CONTEXT ca_ctx= NULL; - PCRL_CONTEXT crl_ctx = NULL; - + *keyfile= mysql->options.ssl_key; SC_CTX *sctx= (SC_CTX *)ctls->ssl; MARIADB_PVIO *pvio= ctls->pvio; - if (cafile) - { - if (!(ca_ctx = ma_schannel_create_cert_context(pvio, cafile))) - goto end; - - /* Add ca to in-memory certificate store */ - if (CertAddCertificateContextToStore(ca_CertStore, ca_ctx, CERT_STORE_ADD_NEWER, NULL) != TRUE && - GetLastError() != CRYPT_E_EXISTS) - { - ma_schannel_set_win_error(sctx->mysql); - goto end; - } - CertFreeCertificateContext(ca_ctx); - ca_ctx = NULL; - } + sctx->client_cert_ctx= NULL; if (!certfile && keyfile) certfile= keyfile; if (!keyfile && certfile) keyfile= certfile; - if (certfile) - if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(ctls->pvio, certfile))) - goto end; + if (!certfile) + return 0; - if (sctx->client_cert_ctx && keyfile) - if (!ma_schannel_load_private_key(pvio, sctx->client_cert_ctx, keyfile)) - goto end; - - if (mysql->options.extension && mysql->options.extension->ssl_crl) + if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(ctls->pvio, certfile))) + return 1; + + if (!ma_schannel_load_private_key(pvio, sctx->client_cert_ctx, keyfile)) { - if (!(crl_ctx= (CRL_CONTEXT *)ma_schannel_create_crl_context(pvio, mysql->options.extension->ssl_crl))) - goto end; - /* Add ca to in-memory certificate store */ - if (CertAddCRLContextToStore(crl_CertStore, crl_ctx, CERT_STORE_ADD_NEWER, NULL) != TRUE && - GetLastError() != CRYPT_E_EXISTS) - { - ma_schannel_set_win_error(sctx->mysql); - goto end; - } - crl_Check = 1; - CertFreeCertificateContext(ca_ctx); - ca_ctx = NULL; + CertFreeCertificateContext(sctx->client_cert_ctx); + sctx->client_cert_ctx= NULL; + return 1; } return 0; - -end: - if (ca_ctx) - CertFreeCertificateContext(ca_ctx); - if (sctx->client_cert_ctx) - CertFreeCertificateContext(sctx->client_cert_ctx); - if (crl_ctx) - CertFreeCRLContext(crl_ctx); - sctx->client_cert_ctx= NULL; - return 1; } /* }}} */ @@ -234,16 +149,11 @@ end: void *ma_tls_init(MYSQL *mysql) { SC_CTX *sctx= NULL; - - pthread_mutex_lock(&LOCK_schannel_config); - if ((sctx= (SC_CTX *)LocalAlloc(0, sizeof(SC_CTX)))) { ZeroMemory(sctx, sizeof(SC_CTX)); sctx->mysql= mysql; } - - pthread_mutex_unlock(&LOCK_schannel_config); return sctx; } /* }}} */ @@ -305,8 +215,6 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) my_bool rc= 1; SC_CTX *sctx; SECURITY_STATUS sRet; - PCCERT_CONTEXT pRemoteCertContext= NULL, - pLocalCertContext= NULL; ALG_ID AlgId[MAX_ALG_ID]; WORD validTokens = 0; @@ -376,21 +284,14 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) if (ma_schannel_client_handshake(ctls) != SEC_E_OK) goto end; - sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext); - if (sRet != SEC_E_OK) - { - ma_schannel_set_sec_error(pvio, sRet); - goto end; - } - if (mysql->options.ssl_ca && !ma_schannel_verify_certs(sctx, 0)) + if (!ma_schannel_verify_certs(sctx)) goto end; return 0; end: - if (pRemoteCertContext) - CertFreeCertificateContext(pRemoteCertContext); + if (rc && sctx->IoBufferSize) LocalFree(sctx->IoBuffer); sctx->IoBufferSize= 0; @@ -453,11 +354,11 @@ int ma_tls_verify_server_cert(MARIADB_TLS *ctls) int rc= 1; char *szName= NULL; char *pszServerName= pvio->mysql->host; + PCCERT_CONTEXT pServerCert= NULL; /* check server name */ if (pszServerName && (sctx->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)) { - PCCERT_CONTEXT pServerCert; DWORD NameSize= 0; char *p1, *p2; SECURITY_STATUS sRet; @@ -465,7 +366,7 @@ int ma_tls_verify_server_cert(MARIADB_TLS *ctls) if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK) { ma_schannel_set_sec_error(pvio, sRet); - return 1; + goto end; } if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType, @@ -473,8 +374,8 @@ int ma_tls_verify_server_cert(MARIADB_TLS *ctls) CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, NULL, 0))) { - pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate"); - return 1; + pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Can't retrieve name of server certificate"); + goto end; } if (!(szName= (char *)LocalAlloc(0, NameSize + 1))) @@ -488,7 +389,7 @@ int ma_tls_verify_server_cert(MARIADB_TLS *ctls) CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, szName, NameSize)) { - pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate"); + pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Can't retrieve name of server certificate"); goto end; } if ((p1 = strstr(szName, "CN="))) @@ -502,12 +403,14 @@ int ma_tls_verify_server_cert(MARIADB_TLS *ctls) goto end; } pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - "Name of server certificate didn't match"); + "SSL connection error: Name of server certificate didn't match"); } } end: if (szName) LocalFree(szName); + if (pServerCert) + CertFreeCertificateContext(pServerCert); return rc; } @@ -543,5 +446,6 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l if (QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext) != SEC_E_OK) return 0; CertGetCertificateContextProperty(pRemoteCertContext, CERT_HASH_PROP_ID, fp, (DWORD *)&len); + CertFreeCertificateContext(pRemoteCertContext); return len; } diff --git a/plugins/pvio/pvio_shmem.c b/plugins/pvio/pvio_shmem.c index 1c39cbf4..0053920c 100644 --- a/plugins/pvio/pvio_shmem.c +++ b/plugins/pvio/pvio_shmem.c @@ -238,7 +238,7 @@ int pvio_shm_fast_send(MARIADB_PVIO *pvio) my_bool pvio_shm_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo) { - char *base_memory_name; + const char *base_memory_name; char *prefixes[]= {"", "Global\\", NULL}; char *shm_name, *shm_suffix, *shm_prefix; uchar i= 0; @@ -453,16 +453,16 @@ my_bool pvio_shm_is_alive(MARIADB_PVIO *pvio) if (!pvio || !pvio->data) return FALSE; pvio_shm= (PVIO_SHM *)pvio->data; - return WaitForSingleObject(pvio_shm->event[PVIO_SHM_CONNECTION_CLOSED], 0); + return WaitForSingleObject(pvio_shm->event[PVIO_SHM_CONNECTION_CLOSED], 0)!=WAIT_OBJECT_0; } my_bool pvio_shm_get_handle(MARIADB_PVIO *pvio, void *handle) { - PVIO_SHM *pvio_shm; + *(HANDLE **)handle= 0; if (!pvio || !pvio->data) return FALSE; - *(HANDLE **)handle= (HANDLE **)((PVIO_SHM*)pvio->data)->event; + *(HANDLE **)handle= ((PVIO_SHM*)pvio->data)->event; return TRUE; } #endif diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index 93bf2c9d..1b4303a9 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -60,13 +60,13 @@ IF(WITH_SSL) STRING(REPLACE "SHA1 Fingerprint=" "" FINGER_PRINT "${FINGER_PRINT}") STRING(REPLACE "\n" "" FINGER_PRINT "${FINGER_PRINT}") STRING(REPLACE ":" "" SSL_CERT_FINGER_PRINT "${FINGER_PRINT}") + CONFIGURE_FILE(${CC_SOURCE_DIR}/unittest/libmariadb/fingerprint.list.in + ${CC_BINARY_DIR}/unittest/libmariadb/fingerprint.list) ENDIF() + SET(API_TESTS ${API_TESTS} "ssl") CONFIGURE_FILE(${CC_SOURCE_DIR}/unittest/libmariadb/ssl.c.in ${CC_BINARY_DIR}/unittest/libmariadb/ssl.c) - CONFIGURE_FILE(${CC_SOURCE_DIR}/unittest/libmariadb/fingerprint.list.in - ${CC_BINARY_DIR}/unittest/libmariadb/fingerprint.list) - SET(API_TESTS ${API_TESTS} "ssl") ADD_EXECUTABLE(ssl ${CC_BINARY_DIR}/unittest/libmariadb/ssl.c) ENDIF() diff --git a/unittest/libmariadb/bulk1.c b/unittest/libmariadb/bulk1.c index 703e5593..612b9aac 100644 --- a/unittest/libmariadb/bulk1.c +++ b/unittest/libmariadb/bulk1.c @@ -26,8 +26,7 @@ char *rand_str(size_t length) { char *dest= (char *)malloc(length+1); char *p= dest; while (length-- > 0) { - size_t index = (double) rand() / RAND_MAX * (sizeof charset - 1); - *dest++ = charset[index]; + *dest++ = charset[rand() % sizeof(charset)]; } *dest = '\0'; return p; diff --git a/unittest/libmariadb/my_test.h b/unittest/libmariadb/my_test.h index 09167cac..f5c7b762 100644 --- a/unittest/libmariadb/my_test.h +++ b/unittest/libmariadb/my_test.h @@ -556,6 +556,5 @@ void run_tests(struct my_tests_st *test) { diag("close default"); mysql_close(mysql_default); } - mysql_server_end(); } diff --git a/unittest/libmariadb/ssl.c.in b/unittest/libmariadb/ssl.c.in index 0d911d26..9efb508b 100644 --- a/unittest/libmariadb/ssl.c.in +++ b/unittest/libmariadb/ssl.c.in @@ -173,9 +173,9 @@ static int test_conc95(MYSQL *unused __attribute__((unused))) mysql= mysql_init(NULL); mysql_ssl_set(mysql, - "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/server-key.pem", - "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/server-cert.pem", - "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem", + NULL, NULL, NULL); @@ -541,13 +541,14 @@ static int verify_ssl_server_cert(MYSQL *unused __attribute__((unused))) mysql= mysql_init(NULL); FAIL_IF(!mysql, "Can't allocate memory"); - mysql_ssl_set(mysql, NULL, NULL, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", NULL, NULL); + mysql_ssl_set(mysql, NULL, NULL, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/cacert.pem", NULL, NULL); mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, port, socketname, 0); FAIL_IF(!mysql_errno(mysql), "Expected error"); + diag("Error (expected): %s", mysql_error(mysql)); mysql_close(mysql); return OK; @@ -758,8 +759,13 @@ static int test_ssl_fp_list(MYSQL *unused __attribute__((unused))) mysql_options(my, MARIADB_OPT_SSL_FP_LIST, "./fingerprint.list"); - FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, - port, socketname, 0), mysql_error(my)); + if(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, + port, socketname, 0)) + { + diag("Error: %s", mysql_error(my)); + mysql_close(my); + return FAIL; + } FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); mysql_close(my); @@ -814,7 +820,6 @@ static int test_schannel_cipher(MYSQL *unused __attribute__((unused))) port, socketname, 0), mysql_error(my)); diag("cipher: %s", mysql_get_ssl_cipher(my)); - FAIL_IF(strcmp(mysql_get_ssl_cipher(my), "CALG_AES_256") != 0, "expected cipher with 256bit strength"); mysql_close(my); @@ -823,7 +828,7 @@ static int test_schannel_cipher(MYSQL *unused __attribute__((unused))) #endif -#ifdef HAVE_GNUTLS +#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) static int test_cipher_mapping(MYSQL *unused __attribute__((unused))) { @@ -847,44 +852,204 @@ static int test_cipher_mapping(MYSQL *unused __attribute__((unused))) MYSQL_RES *res; char c[100]; int rc; - mysql_options(mysql, MYSQL_OPT_SSL_CIPHER, ciphers[i]); + + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, ciphers[i]); diag("%s", ciphers[i]); + + mysql->options.use_ssl= 1; FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, port, socketname, 0), mysql_error(mysql)); - if (strcmp(ciphers[i], mysql_get_ssl_cipher(mysql)) != 0) + if (!mysql_get_ssl_cipher(mysql) || + strcmp(ciphers[i], mysql_get_ssl_cipher(mysql)) != 0) { - diag("expected: %s instead of %s", ciphers[i], mysql_get_ssl_cipher(mysql)); + diag("cipher %s failed", ciphers[i]); mysql_close(mysql); return FAIL; } - rc= mysql_query(mysql, "SHOW STATUS LIKE 'ssl_cipher'"); - check_mysql_rc(rc, mysql); - res= mysql_store_result(mysql); - row= mysql_fetch_row(res); - strcpy(c, row[1]); - mysql_free_result(res); - mysql_close(mysql); - if (strcmp(ciphers[i], c) != 0) + else { - diag("expected: %s instead of %s", ciphers[i], c); - return FAIL; + rc= mysql_query(mysql, "SHOW STATUS LIKE 'ssl_cipher'"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + strcpy(c, row[1]); + mysql_free_result(res); + mysql_close(mysql); + if (strcmp(ciphers[i], c) != 0) + { + diag("expected: %s instead of %s", ciphers[i], c); + return FAIL; + } } - i++; } return OK; } - #endif +static int test_openssl_1(MYSQL *mysql) +{ + int rc; + MYSQL *my; + uchar val= 1; + char query[1024]; + int i; + + if (check_skip_ssl()) + return SKIP; + + for (i=1; i < 6; i++) + { + sprintf(query, "DROP USER IF EXISTS 'ssluser%d'@'%s'", i, sslhost); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + sprintf(query, "CREATE USER 'ssluser%d'@'%s'", i, sslhost); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + } + rc= mysql_query(mysql, "FLUSH PRIVILEGES"); + check_mysql_rc(rc, mysql); + + sprintf(query, "grant select on %s.* to 'ssluser1'@'%s' require ssl", schema, sslhost); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES128-SHA"); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser1", NULL, schema, + port, socketname, 0), mysql_error(my)); + FAIL_IF(!mysql_get_ssl_cipher(my), "No TLS connection"); + mysql_close(my); + + my= mysql_init(NULL); + mysql_options(my, MYSQL_OPT_SSL_ENFORCE, &val); + my->options.use_ssl= 1; + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser1", NULL, schema, + port, socketname, 0), mysql_error(my)); + FAIL_IF(!mysql_get_ssl_cipher(my), "No TLS connection"); + mysql_close(my); + + sprintf(query, "grant select on %s.* to 'ssluser2'@'%s' require cipher 'AES256-SHA'", schema, sslhost); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + +/* ssl_user1: connect with enforce should work */ + my= mysql_init(NULL); + mysql_options(my, MYSQL_OPT_SSL_ENFORCE, &val); + FAIL_IF(mysql_real_connect(my, hostname, "ssluser2", NULL, schema, + port, socketname, 0), "Error expected"); + mysql_close(my); + + /* ssl_user2: connect with cipher should work */ + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES128-SHA"); + FAIL_IF(mysql_real_connect(my, hostname, "ssluser2", NULL, schema, + port, socketname, 0), "Error expected"); + mysql_close(my); + + + /* ssl_user2: connect with correct cipher */ + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA"); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser2", NULL, schema, + port, socketname, 0), mysql_error(my)); + FAIL_IF(strcmp("AES256-SHA", mysql_get_ssl_cipher(my)) != 0, "expected cipher AES256-SHA"); + mysql_close(my); + + + sprintf(query, "grant select on %s.* to 'ssluser3'@'%s' require cipher 'AES256-SHA' AND " + " SUBJECT '/DC=com/DC=example/CN=client'", schema, sslhost); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + /* ssluser3: connect with cipher only */ + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA"); + FAIL_IF(mysql_real_connect(my, hostname, "ssluser3", NULL, schema, + port, socketname, 0), "Error expected"); + mysql_close(my); + + /* ssluser3 connect with cipher and certs */ + my= mysql_init(NULL); + mysql_ssl_set(my, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", + NULL, + "AES256-SHA"); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser3", NULL, schema, + port, socketname, 0), mysql_error(my)); + + mysql_close(my); + + sprintf(query, "grant select on %s.* to 'ssluser4'@'%s' require cipher 'AES256-SHA' AND " + " ISSUER '/DC=com/DC=example/CN=client'", schema, sslhost); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + /* ssluser4: connect with cipher only */ + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA"); + FAIL_IF(mysql_real_connect(my, hostname, "ssluser4", NULL, schema, + port, socketname, 0), "Error expected"); + mysql_close(my); + + /* ssluser4 connect with cipher and certs */ + my= mysql_init(NULL); + mysql_ssl_set(my, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", + NULL, + "AES256-SHA"); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser4", NULL, schema, + port, socketname, 0), mysql_error(my)); + + mysql_close(my); + + return OK; +} + +static int test_ssl_timeout(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + my_bool enforce= 1; + int read_timeout= 1; + int rc; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &enforce); + mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, &read_timeout); + mysql->options.use_ssl= 1; + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(mysql)); + diag("cipher: %s\n", mysql_get_ssl_cipher(mysql)); + rc= mysql_query(mysql, "SELECT SLEEP(600)"); + if (!rc) + { + diag("error expected (timeout)"); + return FAIL; + } + + mysql_close(mysql); + return OK; +} struct my_tests_st my_tests[] = { {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, -#ifdef HAVE_GNUTLS + {"test_ssl_timeout", test_ssl_timeout, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_openssl_1", test_openssl_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#ifndef HAVE_SCHANNEL {"test_cipher_mapping", test_cipher_mapping, TEST_CONNECTION_NONE, 0, NULL, NULL}, #endif {"test_conc127", test_conc127, TEST_CONNECTION_NEW, 0, NULL, NULL}, +/* Both tests work with GNU tls, however we can't create fingerprints with + gnutls-cli in CMakeLists.txt */ +#ifdef HAVE_OPENSSL {"test_ssl_fp", test_ssl_fp, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl_fp_list", test_ssl_fp_list, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#endif {"test_conc50", test_conc50, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc50_1", test_conc50_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc50_2", test_conc50_2, TEST_CONNECTION_NEW, 0, NULL, NULL}, @@ -900,7 +1065,7 @@ struct my_tests_st my_tests[] = { {"test_ssl_version", test_ssl_version, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl_threads", test_ssl_threads, TEST_CONNECTION_NEW, 0, NULL, NULL}, #ifndef HAVE_SCHANNEL - {"test_password_protected", test_password_protected, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_password_protected", test_password_protected, TEST_CONNECTION_NEW, 0, NULL, NULL}, #else {"test_schannel_cipher", test_schannel_cipher, TEST_CONNECTION_NEW, 0, NULL, NULL}, #endif @@ -914,7 +1079,6 @@ int main(int argc, char **argv) if (argc > 1) get_options(argc, argv); - run_tests(my_tests); mysql_server_end();