From 509b948e7dda787d02f6c8fdd236060e4d38ad72 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 16 Feb 2016 13:04:16 +0100 Subject: [PATCH] SSL fixes: - added MARIADB_OPT_SSL_CIPHER_STRENGTH (value uint) for Schannel - fixed mutes in all ssl variants --- include/ma_common.h | 1 + include/mysql.h | 1 + libmariadb/libmariadb.c | 6 + libmariadb/ma_pvio.c | 3 +- libmariadb/ma_ssl.c | 25 ++-- libmariadb/secure/gnutls.c | 20 +-- libmariadb/secure/ma_schannel.c | 86 ++++++----- libmariadb/secure/ma_schannel.h | 12 +- libmariadb/secure/openssl.c | 46 +++--- libmariadb/secure/schannel.c | 226 ++++++++++++++++------------- unittest/libmariadb/CMakeLists.txt | 8 +- unittest/libmariadb/ssl.c.in | 29 ++++ 12 files changed, 264 insertions(+), 199 deletions(-) diff --git a/include/ma_common.h b/include/ma_common.h index 87fca0e8..edf69c42 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -53,6 +53,7 @@ struct st_mysql_options_extension { char *ssl_fp_list; /* white list of finger prints */ char *ssl_pw; /* password for encrypted certificates */ char *url; /* for connection handler we need to save URL for reconnect */ + unsigned int ssl_cipher_strength; my_bool read_only; char *connection_handler; HASH userdata; diff --git a/include/mysql.h b/include/mysql.h index 88cabc18..05365676 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -221,6 +221,7 @@ extern unsigned int mariadb_deinitialize_ssl; MARIADB_OPT_CONNECTION_READ_ONLY, MYSQL_OPT_CONNECT_ATTRS, /* for mysql_get_optionv */ MARIADB_OPT_USERDATA, + MARIADB_OPT_SSL_CIPHER_STRENGTH, MARIADB_OPT_CONNECTION_HANDLER }; diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index c72e0ea8..5d6811ff 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -2930,6 +2930,9 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) my_free(mysql->options.bind_address); mysql->options.bind_address= my_strdup(arg1, MYF(MY_WME)); break; + case MARIADB_OPT_SSL_CIPHER_STRENGTH: + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, ssl_cipher_strength, *((unsigned int *)arg1)); + break; case MARIADB_OPT_SSL_FP: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp, (char *)arg1); break; @@ -3108,6 +3111,9 @@ mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...) case MYSQL_OPT_BIND: *((char **)arg)= mysql->options.bind_address; break; + case MARIADB_OPT_SSL_CIPHER_STRENGTH: + *((unsigned int *)arg) = mysql->options.extension ? mysql->options.extension->ssl_cipher_strength : 0; + break; case MARIADB_OPT_SSL_FP: *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_fp : NULL; break; diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index 7d012c66..c4bc98ca 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -521,7 +521,8 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) return 1; if (pvio->mysql->options.extension && - (pvio->mysql->options.extension->ssl_fp || pvio->mysql->options.extension->ssl_fp_list)) + (pvio->mysql->options.extension->ssl_fp && pvio->mysql->options.extension->ssl_fp[0]) || + (pvio->mysql->options.extension->ssl_fp_list && pvio->mysql->options.extension->ssl_fp_list[0])) { if (ma_pvio_ssl_check_fp(pvio->cssl, diff --git a/libmariadb/ma_ssl.c b/libmariadb/ma_ssl.c index 529979ea..772c90b7 100644 --- a/libmariadb/ma_ssl.c +++ b/libmariadb/ma_ssl.c @@ -140,6 +140,7 @@ my_bool ma_pvio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_l unsigned int cert_fp_len= 64; unsigned char cert_fp[64]; my_bool rc=1; + MYSQL *mysql= cssl->pvio->mysql; if ((cert_fp_len= ma_ssl_get_finger_print(cssl, cert_fp, cert_fp_len)) < 1) goto end; @@ -147,20 +148,15 @@ my_bool ma_pvio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_l rc= ma_pvio_ssl_compare_fp(cert_fp, cert_fp_len, (char *)fp, (unsigned int)strlen(fp)); else if (fp_list) { - FILE *fp; + MA_FILE *fp; char buff[255]; - if (!(fp = fopen(fp_list, "r"))) + if (!(fp = ma_open(fp_list, "r", mysql))) { -/* - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Can't open finger print list"); - */ - goto end; + return 1; } - while (fgets(buff, sizeof(buff)-1, fp)) + while (ma_gets(buff, sizeof(buff)-1, fp)) { /* remove trailing new line character */ char *pos= strchr(buff, '\r'); @@ -172,18 +168,23 @@ my_bool ma_pvio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_l if (!ma_pvio_ssl_compare_fp(cert_fp, cert_fp_len, buff, (unsigned int)strlen(buff))) { /* finger print is valid: close file and exit */ - fclose(fp); + ma_close(fp); rc= 0; goto end; } } /* No finger print matched - close file and return error */ - fclose(fp); + ma_close(fp); } - end: + if (rc) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Fingerprint verification of server certificate failed"); + } return rc; } #endif /* HAVE_SSL */ diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index 534c2592..ebbf1787 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -97,20 +97,20 @@ int ma_ssl_start(char *errmsg, size_t errmsg_len) { int rc= 0; + if (ma_ssl_initialized) + return 0; + pthread_mutex_init(&LOCK_gnutls_config,MY_MUTEX_INIT_FAST); pthread_mutex_lock(&LOCK_gnutls_config); - if (!ma_ssl_initialized) + if ((rc= gnutls_global_init()) != GNUTLS_E_SUCCESS) { - if ((rc= gnutls_global_init()) != GNUTLS_E_SUCCESS) - { - ma_ssl_get_error(errmsg, errmsg_len, rc); - goto end; - } - ma_ssl_initialized= TRUE; + ma_ssl_get_error(errmsg, errmsg_len, rc); + goto end; } /* Allocate a global context for credentials */ rc= gnutls_certificate_allocate_credentials(&GNUTLS_xcred); + ma_ssl_initialized= TRUE; end: pthread_mutex_unlock(&LOCK_gnutls_config); return rc; @@ -130,9 +130,9 @@ end: */ void ma_ssl_end() { - pthread_mutex_lock(&LOCK_gnutls_config); if (ma_ssl_initialized) { + pthread_mutex_lock(&LOCK_gnutls_config); gnutls_certificate_free_keys(GNUTLS_xcred); gnutls_certificate_free_cas(GNUTLS_xcred); gnutls_certificate_free_crls(GNUTLS_xcred); @@ -141,9 +141,9 @@ void ma_ssl_end() if (mariadb_deinitialize_ssl) gnutls_global_deinit(); ma_ssl_initialized= FALSE; + pthread_mutex_unlock(&LOCK_gnutls_config); + pthread_mutex_destroy(&LOCK_gnutls_config); } - pthread_mutex_unlock(&LOCK_gnutls_config); - pthread_mutex_destroy(&LOCK_gnutls_config); return; } diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index 2ed2d739..4da14cbe 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -387,7 +387,7 @@ end: SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData) { SecBufferDesc OutBuffer, InBuffer; - SecBuffer InBuffers[2], OutBuffers[1]; + SecBuffer InBuffers[2], OutBuffers; DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer; TimeStamp tsExpiry; SECURITY_STATUS rc; @@ -457,12 +457,12 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRe /* output buffer */ - OutBuffers[0].pvBuffer = NULL; - OutBuffers[0].BufferType= SECBUFFER_TOKEN; - OutBuffers[0].cbBuffer = 0; + OutBuffers.pvBuffer = NULL; + OutBuffers.BufferType= SECBUFFER_TOKEN; + OutBuffers.cbBuffer = 0; OutBuffer.cBuffers = 1; - OutBuffer.pBuffers = OutBuffers; + OutBuffer.pBuffers = &OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; @@ -484,19 +484,19 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRe rc == SEC_I_CONTINUE_NEEDED || FAILED(rc) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) { - if(OutBuffers[0].cbBuffer && OutBuffers[0].pvBuffer) + if(OutBuffers.cbBuffer && OutBuffers.pvBuffer) { - cbData= (DWORD)pvio->methods->write(pvio, (uchar *)OutBuffers[0].pvBuffer, (size_t)OutBuffers[0].cbBuffer); + cbData= (DWORD)pvio->methods->write(pvio, (uchar *)OutBuffers.pvBuffer, (size_t)OutBuffers.cbBuffer); if(cbData == SOCKET_ERROR || cbData == 0) { - FreeContextBuffer(OutBuffers[0].pvBuffer); + FreeContextBuffer(OutBuffers.pvBuffer); DeleteSecurityContext(&sctx->ctxt); return SEC_E_INTERNAL_ERROR; } /* Free output context buffer */ - FreeContextBuffer(OutBuffers[0].pvBuffer); - OutBuffers[0].pvBuffer = NULL; + FreeContextBuffer(OutBuffers.pvBuffer); + OutBuffers.pvBuffer = NULL; } } @@ -589,7 +589,7 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; SecBufferDesc BufferOut; - SecBuffer BuffersOut[1]; + SecBuffer BuffersOut; if (!cssl || !cssl->pvio) return 1; @@ -598,13 +598,13 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) sctx= (SC_CTX *)cssl->ssl; /* Initialie securifty context */ - BuffersOut[0].BufferType= SECBUFFER_TOKEN; - BuffersOut[0].cbBuffer= 0; - BuffersOut[0].pvBuffer= NULL; + BuffersOut.BufferType= SECBUFFER_TOKEN; + BuffersOut.cbBuffer= 0; + BuffersOut.pvBuffer= NULL; BufferOut.cBuffers= 1; - BufferOut.pBuffers= BuffersOut; + BufferOut.pBuffers= &BuffersOut; BufferOut.ulVersion= SECBUFFER_VERSION; sRet = InitializeSecurityContext(&sctx->CredHdl, @@ -627,9 +627,9 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) } /* send client hello packaet */ - if(BuffersOut[0].cbBuffer != 0 && BuffersOut[0].pvBuffer != NULL) + if(BuffersOut.cbBuffer != 0 && BuffersOut.pvBuffer != NULL) { - r= (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut[0].pvBuffer, (size_t)BuffersOut[0].cbBuffer); + r= (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut.pvBuffer, (size_t)BuffersOut.cbBuffer); if (r <= 0) { sRet= SEC_E_INTERNAL_ERROR; @@ -654,7 +654,7 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) end: LocalFree(sctx->IoBuffer); sctx->IoBufferSize= 0; - FreeContextBuffer(BuffersOut[0].pvBuffer); + FreeContextBuffer(BuffersOut.pvBuffer); DeleteSecurityContext(&sctx->ctxt); return sRet; } @@ -782,6 +782,9 @@ my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags) DWORD flags; MARIADB_PVIO *pvio= sctx->mysql->net.pvio; PCCERT_CONTEXT pServerCert= NULL; + PCERT_CONTEXT ca_CTX = NULL; + PCRL_CONTEXT crl_CTX = NULL; + my_bool is_Ok = 0; if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK) { @@ -789,16 +792,22 @@ my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags) return 0; } - flags= CERT_STORE_SIGNATURE_FLAG | - CERT_STORE_TIME_VALIDITY_FLAG; - - - - if (sctx->client_ca_ctx) + if (ca_Check) { - if (!(sRet= CertVerifySubjectCertificateContext(pServerCert, - sctx->client_ca_ctx, - &flags))) + 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) { ma_schannel_set_win_error(pvio); return 0; @@ -819,18 +828,23 @@ my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags) } /* Check if none of the certificates in the certificate chain have been revoked. */ - if (sctx->client_crl_ctx) + if (crl_Check) { - PCRL_INFO Info[1]; - - Info[0]= sctx->client_crl_ctx->pCrlInfo; - if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - pServerCert->pCertInfo, - 1, Info)) ) + while ((crl_CTX = CertEnumCRLsInStore(crl_CertStore, crl_CTX))) { - pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CRL Revocation failed"); - return 0; + 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; + } } + CertFreeCRLContext(crl_CTX); } return 1; } diff --git a/libmariadb/secure/ma_schannel.h b/libmariadb/secure/ma_schannel.h index 614a274b..359b6aae 100644 --- a/libmariadb/secure/ma_schannel.h +++ b/libmariadb/secure/ma_schannel.h @@ -56,19 +56,10 @@ typedef void VOID; struct st_schannel { HCERTSTORE cert_store; CERT_CONTEXT *client_cert_ctx; - CERT_CONTEXT *client_ca_ctx; - CRL_CONTEXT *client_crl_ctx; CredHandle CredHdl; my_bool FreeCredHdl; PUCHAR IoBuffer; DWORD IoBufferSize; -/* PUCHAR EncryptBuffer; - DWORD EncryptBufferSize; - DWORD EncryptBufferLength; - PUCHAR DecryptBuffer; - DWORD DecryptBufferSize; - DWORD DecryptBufferLength; - uchar thumbprint[21]; */ SecPkgContext_StreamSizes Sizes; CtxtHandle ctxt; @@ -77,6 +68,9 @@ struct st_schannel { typedef struct st_schannel SC_CTX; +extern HCERTSTORE ca_CertStore, crl_CertStore; +extern my_bool ca_Check, crl_Check; + CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_PVIO *pvio, const char *pem_file); SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl); SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData); diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index f8a844dd..7053e0d5 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -159,37 +159,37 @@ static int ssl_thread_init() int ma_ssl_start(char *errmsg, size_t errmsg_len) { int rc= 1; + if (ma_ssl_initialized) + return 0; + /* lock mutex to prevent multiple initialization */ pthread_mutex_init(&LOCK_openssl_config,MY_MUTEX_INIT_FAST); pthread_mutex_lock(&LOCK_openssl_config); - if (!ma_ssl_initialized) + if (ssl_thread_init()) { - if (ssl_thread_init()) - { - strncpy(errmsg, "Not enough memory", errmsg_len); - goto end; - } - SSL_library_init(); + strncpy(errmsg, "Not enough memory", errmsg_len); + goto end; + } + SSL_library_init(); #if SSLEAY_VERSION_NUMBER >= 0x00907000L - OPENSSL_config(NULL); + OPENSSL_config(NULL); #endif - /* load errors */ - SSL_load_error_strings(); - /* digests and ciphers */ - OpenSSL_add_all_algorithms(); + /* load errors */ + SSL_load_error_strings(); + /* digests and ciphers */ + OpenSSL_add_all_algorithms(); #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) - if (!(SSL_context= SSL_CTX_new(TLS_client_method()))) + if (!(SSL_context= SSL_CTX_new(TLS_client_method()))) #else - if (!(SSL_context= SSL_CTX_new(SSLv23_client_method()))) + if (!(SSL_context= SSL_CTX_new(SSLv23_client_method()))) #endif - { - ma_ssl_get_error(errmsg, errmsg_len); - goto end; - } - rc= 0; - ma_ssl_initialized= TRUE; + { + ma_ssl_get_error(errmsg, errmsg_len); + goto end; } + rc= 0; + ma_ssl_initialized= TRUE; end: pthread_mutex_unlock(&LOCK_openssl_config); return rc; @@ -209,10 +209,10 @@ end: */ void ma_ssl_end() { - pthread_mutex_lock(&LOCK_openssl_config); if (ma_ssl_initialized) { int i; + pthread_mutex_lock(&LOCK_openssl_config); CRYPTO_set_locking_callback(NULL); CRYPTO_set_id_callback(NULL); @@ -238,9 +238,9 @@ void ma_ssl_end() sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); } ma_ssl_initialized= FALSE; + pthread_mutex_unlock(&LOCK_openssl_config); + pthread_mutex_destroy(&LOCK_openssl_config); } - pthread_mutex_unlock(&LOCK_openssl_config); - pthread_mutex_destroy(&LOCK_openssl_config); return; } diff --git a/libmariadb/secure/schannel.c b/libmariadb/secure/schannel.c index 1f27dd44..66802460 100644 --- a/libmariadb/secure/schannel.c +++ b/libmariadb/secure/schannel.c @@ -34,61 +34,57 @@ struct st_cipher_suite { CHAR *cipher; }; +const struct st_cipher_suite valid_ciphers[] = +{ + { CALG_3DES, "CALG_3DES" }, + { CALG_3DES_112, "CALG_3DES_112" }, + { CALG_AES, "CALG_AES" }, + { CALG_AES_128, "CALG_AES_128" }, + { CALG_AES_192, "CALG_AES_192" }, + { CALG_AES_256, "CALG_AES_256" }, + { CALG_AGREEDKEY_ANY, "CALG_AGREEDKEY_ANY" }, + { CALG_CYLINK_MEK, "CALG_CYLINK_MEK" }, + { CALG_DES, "CALG_DES" }, + { CALG_DESX, "CALG_DESX" }, + { CALG_DH_EPHEM, "CALG_DH_EPHEM" }, + { CALG_DH_SF, "CALG_DH_SF" }, + { CALG_DSS_SIGN, "CALG_DSS_SIGN" }, + { CALG_ECDH, "CALG_ECDH" }, + { CALG_ECDSA, "CALG_ECDSA" }, + { CALG_ECMQV, "CALG_ECMQV" }, + { CALG_HASH_REPLACE_OWF, "CALG_HASH_REPLACE_OWF" }, + { CALG_HUGHES_MD5, "CALG_HUGHES_MD5" }, + { CALG_HMAC, "CALG_HMAC" }, + { CALG_KEA_KEYX, "CALG_KEA_KEYX" }, + { CALG_MAC, "CALG_MAC" }, + { CALG_MD2, "CALG_MD2" }, + { CALG_MD4, "CALG_MD4" }, + { CALG_MD4, "CALG_MD5" }, + { CALG_NO_SIGN, "CALG_NO_SIGN" }, + { CALG_OID_INFO_CNG_ONLY, "CALG_OID_INFO_CNG_ONLY" }, + { CALG_OID_INFO_PARAMETERS, "CALG_OID_INFO_PARAMETERS" }, + { CALG_RC2, "CALG_RC2" }, + { CALG_RC4, "CALG_RC4" }, + { CALG_RC5, "CALG_RC5" }, + { CALG_RSA_KEYX, "CALG_RSA_KEYX" }, + { CALG_RSA_SIGN, "CALG_RSA_SIGN" }, + { CALG_SHA, "CALG_SHA" }, + { CALG_SHA1, "CALG_SHA1" }, + { CALG_SHA_256, "CALG_SHA_256" }, + { CALG_SHA_384, "CALG_SHA_384" }, + { CALG_SHA_512, "CALG_SHA_512" }, + { 0, NULL } +}; + +#define MAX_ALG_ID 50 + void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo); void ma_schannel_set_win_error(MYSQL *mysql); -const struct st_cipher_suite sc_ciphers[]= -{ - {CALG_3DES, "CALG_3DES"}, - {CALG_3DES_112, "CALG_3DES_112"}, - {CALG_AES, "CALG_AES"}, - {CALG_AES_128, "CALG_AES_128"}, - {CALG_AES_192, "CALG_AES_192"}, - {CALG_AES_256, "CALG_AES_256"}, - {CALG_AGREEDKEY_ANY, "CALG_AGREEDKEY_ANY"}, - {CALG_CYLINK_MEK, "CALG_CYLINK_MEK"}, - {CALG_DES, "CALG_DES"}, - {CALG_DESX, "CALG_DESX"}, - {CALG_DH_EPHEM, "CALG_DH_EPHEM"}, - {CALG_DH_SF, "CALG_DH_SF"}, - {CALG_DSS_SIGN, "CALG_DSS_SIGN"}, - {CALG_ECDH, "CALG_ECDH"}, - {CALG_ECDSA, "CALG_ECDSA"}, - {CALG_ECMQV, "CALG_ECMQV"}, - {CALG_HASH_REPLACE_OWF, "CALG_HASH_REPLACE_OWF"}, - {CALG_HUGHES_MD5, "CALG_HUGHES_MD5"}, - {CALG_HMAC, "CALG_HMAC"}, - {CALG_KEA_KEYX, "CALG_KEA_KEYX"}, - {CALG_MAC, "CALG_MAC"}, - {CALG_MD2, "CALG_MD2"}, - {CALG_MD4, "CALG_MD4"}, - {CALG_MD4, "CALG_MD5"}, - {CALG_NO_SIGN, "CALG_NO_SIGN"}, - {CALG_OID_INFO_CNG_ONLY, "CALG_OID_INFO_CNG_ONLY"}, - {CALG_OID_INFO_PARAMETERS, "CALG_OID_INFO_PARAMETERS"}, - {CALG_PCT1_MASTER, "CALG_PCT1_MASTER"}, - {CALG_RC2, "CALG_RC2"}, - {CALG_RC4, "CALG_RC4"}, - {CALG_RC5, "CALG_RC5"}, - {CALG_RSA_KEYX, "CALG_RSA_KEYX"}, - {CALG_RSA_SIGN, "CALG_RSA_SIGN"}, - {CALG_SCHANNEL_MAC_KEY, "CALG_SCHANNEL_MAC_KEY"}, - {CALG_SCHANNEL_MASTER_HASH, "CALG_SCHANNEL_MASTER_HASH"}, - {CALG_SEAL, "CALG_SEAL"}, - {CALG_SHA, "CALG_SHA"}, - {CALG_SHA1, "CALG_SHA1"}, - {CALG_SHA_256, "CALG_SHA_256"}, - {CALG_SHA_384, "CALG_SHA_384"}, - {CALG_SHA_512, "CALG_SHA_512"}, - {CALG_SKIPJACK, "CALG_SKIPJACK"}, - {CALG_SSL2_MASTER, "CALG_SSL2_MASTER"}, - {CALG_SSL3_MASTER, "CALG_SSL3_MASTER"}, - {CALG_SSL3_SHAMD5, "CALG_SSL3_SHAMD5"}, - {CALG_TEK, "CALG_TEK"}, - {CALG_TLS1_MASTER, "CALG_TLS1_MASTER"}, - {CALG_TLS1PRF, "CALG_TLS1PRF"}, - {0, NULL} -}; +HCERTSTORE ca_CertStore= NULL, + crl_CertStore= NULL; +my_bool ca_Check = 0, crl_Check = 0; + static int ssl_thread_init() { @@ -113,7 +109,17 @@ int ma_ssl_start(char *errmsg, size_t errmsg_len) { pthread_mutex_init(&LOCK_schannel_config,MY_MUTEX_INIT_FAST); pthread_mutex_lock(&LOCK_schannel_config); - ma_ssl_initialized= TRUE; + 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_ssl_initialized = TRUE; pthread_mutex_unlock(&LOCK_schannel_config); } return 0; @@ -133,14 +139,23 @@ int ma_ssl_start(char *errmsg, size_t errmsg_len) */ void ma_ssl_end() { - pthread_mutex_lock(&LOCK_schannel_config); if (ma_ssl_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_ssl_initialized= FALSE; + pthread_mutex_unlock(&LOCK_schannel_config); + pthread_mutex_destroy(&LOCK_schannel_config); } - pthread_mutex_unlock(&LOCK_schannel_config); - pthread_mutex_destroy(&LOCK_schannel_config); return; } @@ -151,32 +166,26 @@ static int ma_ssl_set_client_certs(MARIADB_SSL *cssl) 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; SC_CTX *sctx= (SC_CTX *)cssl->ssl; MARIADB_PVIO *pvio= cssl->pvio; if (cafile) { - HCERTSTORE myCS= NULL; - - if (!(sctx->client_ca_ctx = ma_schannel_create_cert_context(pvio, cafile))) + if (!(ca_ctx = ma_schannel_create_cert_context(pvio, cafile))) goto end; - /* For X509 authentication we need to add ca certificate to local MY store. - Schannel doesn't provide a callback to send ca to server during handshake */ - if ((myCS= CertOpenStore(CERT_STORE_PROV_SYSTEM, - 0, //X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - 0, - CERT_SYSTEM_STORE_CURRENT_USER, - L"CA"))) + /* Add ca to in-memory certificate store */ + if (CertAddCertificateContextToStore(ca_CertStore, ca_ctx, CERT_STORE_ADD_NEWER, NULL) != TRUE && + GetLastError() != CRYPT_E_EXISTS) { - CertAddCertificateContextToStore(myCS, sctx->client_ca_ctx, CERT_STORE_ADD_NEWER, NULL); - CertCloseStore(myCS, 0); - } - else { ma_schannel_set_win_error(sctx->mysql); goto end; } + ca_Check= 0; + CertFreeCertificateContext(ca_ctx); } if (!certfile && keyfile) @@ -194,20 +203,28 @@ static int ma_ssl_set_client_certs(MARIADB_SSL *cssl) if (mysql->options.extension && mysql->options.extension->ssl_crl) { - if (!(sctx->client_crl_ctx= (CRL_CONTEXT *)ma_schannel_create_crl_context(pvio, mysql->options.extension->ssl_crl))) + 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); } return 0; end: - if (sctx->client_ca_ctx) - CertFreeCertificateContext(sctx->client_ca_ctx); + if (ca_ctx) + CertFreeCertificateContext(ca_ctx); if (sctx->client_cert_ctx) CertFreeCertificateContext(sctx->client_cert_ctx); - if (sctx->client_crl_ctx) - CertFreeCRLContext(sctx->client_crl_ctx); - sctx->client_ca_ctx= sctx->client_cert_ctx= NULL; - sctx->client_crl_ctx= NULL; + if (crl_ctx) + CertFreeCRLContext(crl_ctx); + sctx->client_cert_ctx= NULL; return 1; } /* }}} */ @@ -243,7 +260,7 @@ my_bool ma_ssl_connect(MARIADB_SSL *cssl) SECURITY_STATUS sRet; PCCERT_CONTEXT pRemoteCertContext = NULL, pLocalCertContext= NULL; - ALG_ID AlgId[2]= {0, 0}; + ALG_ID AlgId[MAX_ALG_ID]= {0}; if (!cssl || !cssl->pvio) return 1;; @@ -260,31 +277,42 @@ my_bool ma_ssl_connect(MARIADB_SSL *cssl) if (ma_ssl_set_client_certs(cssl)) goto end; + ZeroMemory(&Cred, sizeof(SCHANNEL_CRED)); + /* Set cipher */ if (mysql->options.ssl_cipher) { - DWORD i= 0; - while (sc_ciphers[i].cipher) { - if (!strcmp(sc_ciphers[i].cipher, mysql->options.ssl_cipher)) + WORD validTokens = 0; + char *token = strtok(mysql->options.ssl_cipher, ":"); + while (token) + { + struct st_cipher_suite *valid; + for (valid = valid_ciphers; valid && valid->aid; valid++) { - AlgId[0]= sc_ciphers[i].aid; - break; + if (!strcmp(token, valid->cipher)) + { + AlgId[validTokens++] = valid->aid; + break; + } } + token = strtok(NULL, ":"); } - Cred.palgSupportedAlgs= (ALG_ID *)&AlgId; } - - - ZeroMemory(&Cred, sizeof(SCHANNEL_CRED)); + Cred.palgSupportedAlgs = (ALG_ID *)&AlgId; + Cred.dwVersion= SCHANNEL_CRED_VERSION; - Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK | SCH_SEND_ROOT_CERT | - SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION; + if (mysql->options.extension) + Cred.dwMinimumCipherStrength = MAX(128, mysql->options.extension->ssl_cipher_strength); + else + Cred.dwMinimumCipherStrength = 128; + Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK | SCH_SEND_ROOT_CERT | + SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION; if (sctx->client_cert_ctx) { Cred.cCreds = 1; Cred.paCred = &sctx->client_cert_ctx; } - Cred.grbitEnabledProtocols= SP_PROT_TLS1_1PLUS; + Cred.grbitEnabledProtocols = SP_PROT_TLS1_0 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2; if ((sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL)) != SEC_E_OK) @@ -315,15 +343,9 @@ end: if (rc && sctx->IoBufferSize) LocalFree(sctx->IoBuffer); sctx->IoBufferSize= 0; - if (sctx->client_ca_ctx) - CertFreeCertificateContext(sctx->client_ca_ctx); if (sctx->client_cert_ctx) CertFreeCertificateContext(sctx->client_cert_ctx); - if (sctx->client_crl_ctx) - CertFreeCRLContext(sctx->client_crl_ctx); - sctx->client_ca_ctx= 0; sctx->client_cert_ctx= 0; - sctx->client_crl_ctx= 0; return 1; } @@ -363,12 +385,8 @@ my_bool ma_ssl_close(MARIADB_SSL *cssl) { if (sctx->IoBufferSize) LocalFree(sctx->IoBuffer); - if (sctx->client_ca_ctx) - CertFreeCertificateContext(sctx->client_ca_ctx); if (sctx->client_cert_ctx) CertFreeCertificateContext(sctx->client_cert_ctx); - if (sctx->client_crl_ctx) - CertFreeCRLContext(sctx->client_crl_ctx); FreeCredentialHandle(&sctx->CredHdl); DeleteSecurityContext(&sctx->ctxt); } @@ -458,10 +476,10 @@ const char *ma_ssl_get_cipher(MARIADB_SSL *cssl) if (sRet != SEC_E_OK) return NULL; - while (sc_ciphers[i].cipher) + while (valid_ciphers[i].cipher) { - if (sc_ciphers[i].aid == cinfo.aiCipher) - return sc_ciphers[i].cipher; + if (valid_ciphers[i].aid == cinfo.aiCipher) + return valid_ciphers[i].cipher; i++; } return NULL; diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index cee2743e..92334361 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -51,21 +51,21 @@ IF(WITH_SSL AND OPENSSL_FOUND) STRING(REPLACE "\n" "" FINGER_PRINT "${FINGER_PRINT}") STRING(REPLACE ":" "" SSL_CERT_FINGER_PRINT "${FINGER_PRINT}") +ENDIF() CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c.in ${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/unittest/libmariadb/fingerprint.list.in ${CMAKE_SOURCE_DIR}/unittest/libmariadb/fingerprint.list) - SET(API_TESTS ${API_TESTS} "ssl") -ENDIF() +SET(API_TESTS ${API_TESTS} "ssl") FOREACH(API_TEST ${API_TESTS}) ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c ${CMAKE_SOURCE_DIR}/libmariadb/getopt.c) - TARGET_LINK_LIBRARIES(${API_TEST} mytap libmariadb) + TARGET_LINK_LIBRARIES(${API_TEST} mytap mariadbclient) ADD_TEST(${API_TEST} ${EXECUTABLE_OUTPUT_PATH}/${API_TEST}) SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 120) ENDFOREACH(API_TEST) FOREACH(API_TEST ${MANUAL_TESTS}) ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c ${CMAKE_SOURCE_DIR}/libmariadb/getopt.c) - TARGET_LINK_LIBRARIES(${API_TEST} mytap libmariadb) + TARGET_LINK_LIBRARIES(${API_TEST} mytap mariadbclient) ENDFOREACH() diff --git a/unittest/libmariadb/ssl.c.in b/unittest/libmariadb/ssl.c.in index 8b79761e..d32fdbb6 100644 --- a/unittest/libmariadb/ssl.c.in +++ b/unittest/libmariadb/ssl.c.in @@ -793,6 +793,33 @@ static int test_ssl_version(MYSQL *mysql) return OK; } + +#ifdef HAVE_SCHANNEL +static int test_schannel_cipher(MYSQL *mysql) +{ + MYSQL *my; + unsigned int cipher_strength= 256; + + if (check_skip_ssl()) + return SKIP; + + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + mysql_ssl_set(my,0, 0, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", 0, 0); + mysql_options(my, MARIADB_OPT_SSL_CIPHER_STRENGTH, &cipher_strength); + FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, + 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); + + return OK; +} + +#endif struct my_tests_st my_tests[] = { @@ -816,6 +843,8 @@ struct my_tests_st my_tests[] = { {"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}, +#else + {"test_schannel_cipher", test_schannel_cipher, TEST_CONNECTION_NEW, 0, NULL, NULL}, #endif {NULL, NULL, 0, 0, NULL, NULL} };