From 1730326a6a3942f10fa5dd178790bba399002b0d Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Mon, 13 May 2019 13:48:05 -0400 Subject: [PATCH 1/2] bump the VERSION --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 937c29d5..710c152a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ SET(CC_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) SET(CPACK_PACKAGE_VERSION_MAJOR 3) SET(CPACK_PACKAGE_VERSION_MINOR 0) -SET(CPACK_PACKAGE_VERSION_PATCH 10) +SET(CPACK_PACKAGE_VERSION_PATCH 11) 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 + From b0411b731f5d61df38fe3f783437df13526774f2 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sun, 2 Jun 2019 13:39:27 +0200 Subject: [PATCH 2/2] CONC-386: Added support for pem files which contain certificate and private key. In case the file will contain more than one certificate or key, the first certificate or key found will be used. --- libmariadb/secure/ma_schannel.c | 237 +++++++++++++++++++++++++++++--- libmariadb/secure/ma_schannel.h | 9 +- libmariadb/secure/schannel.c | 32 +++-- unittest/libmariadb/ssl.c.in | 48 +++++++ 4 files changed, 294 insertions(+), 32 deletions(-) diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index 4b281401..06c9705f 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -133,11 +133,11 @@ void ma_schannel_set_win_error(MARIADB_PVIO *pvio) */ static LPBYTE ma_schannel_load_pem(MARIADB_PVIO *pvio, const char *PemFileName, DWORD *buffer_len) { - HANDLE hfile; + HANDLE hfile= 0; char *buffer= NULL; DWORD dwBytesRead= 0; LPBYTE der_buffer= NULL; - DWORD der_buffer_length; + DWORD der_buffer_length= 0; if (buffer_len == NULL) return NULL; @@ -156,7 +156,7 @@ static LPBYTE ma_schannel_load_pem(MARIADB_PVIO *pvio, const char *PemFileName, goto end; } - if (!(buffer= LocalAlloc(0, *buffer_len + 1))) + if (!(buffer= malloc((size_t)(*buffer_len + 1)))) { pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); goto end; @@ -192,7 +192,7 @@ static LPBYTE ma_schannel_load_pem(MARIADB_PVIO *pvio, const char *PemFileName, } *buffer_len= der_buffer_length; - LocalFree(buffer); + free(buffer); return der_buffer; @@ -208,6 +208,196 @@ end: } /* }}} */ +static LPBYTE ma_schannel_read(MARIADB_PVIO* pvio, const char* PemFile, DWORD* buffer_len) +{ + HANDLE hfile = NULL; + char* buffer = NULL; + DWORD dwBytesRead = 0; + + if ((hfile = CreateFile(PemFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) + { + ma_schannel_set_win_error(pvio); + return NULL; + } + + if (!(*buffer_len = GetFileSize(hfile, NULL))) + { + pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Invalid pem format"); + goto end; + } + + if (!(buffer = malloc((size_t)* buffer_len + 1))) + { + pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); + goto end; + } + + if (!ReadFile(hfile, buffer, *buffer_len, &dwBytesRead, NULL)) + { + ma_schannel_set_win_error(pvio); + goto end; + } + + buffer[*buffer_len] = 0; + + CloseHandle(hfile); + return buffer; +end: + if (hfile != INVALID_HANDLE_VALUE) + CloseHandle(hfile); + if (buffer) + free(buffer); + *buffer_len = 0; + return NULL; +} + +LPBYTE ma_schannel_convert_base64(MARIADB_PVIO* pvio, char* buffer, DWORD buffer_len, DWORD* der_len) +{ + LPBYTE der_buffer = NULL; + + *der_len = 0; + + /* calculate the length of DER binary */ + if (!CryptStringToBinaryA(buffer, buffer_len, CRYPT_STRING_BASE64HEADER, + NULL, der_len, NULL, NULL)) + { + ma_schannel_set_win_error(pvio); + goto end; + } + /* allocate DER binary buffer */ + if (!(der_buffer = (LPBYTE)malloc(*der_len))) + { + pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); + goto end; + } + /* convert to DER binary */ + if (!CryptStringToBinaryA(buffer, buffer_len, CRYPT_STRING_BASE64HEADER, + der_buffer, der_len, NULL, NULL)) + { + ma_schannel_set_win_error(pvio); + goto end; + } + return der_buffer; +end: + if (der_buffer) + free(der_buffer); + return NULL; +} + + +DWORD ma_schannel_load_certs_and_keys(MARIADB_PVIO* pvio, const char* PemFileName, SC_CTX* ctx) +{ + char* buffer = NULL; + char* p, * type; + DWORD buffer_len = 0; + LPBYTE der_buffer = NULL; + DWORD der_buffer_length = 0; + + /* check if cert and key was already loaded */ + if (ctx->client_cert_ctx && ctx->der_key) + return 0; + + if (!(buffer = ma_schannel_read(pvio, PemFileName, &buffer_len))) + return 0; + + p = buffer; + + while ((p = strstr(p, "-----BEGIN"))) + { + my_bool is_cert = 0; + char* cert_end = strstr(p, "-----END"); + + if (!cert_end) + { + pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Unknown or unsupported X509 PEM type"); goto error; + goto error; + } + + if (!(cert_end = strchr(cert_end, '\n'))) + goto error; + + if ((type = strstr(p, "CERTIFICATE")) && type < cert_end) + { + is_cert = 1; + /* We only read first certificate, further certificates will be ignored */ + if (ctx->client_cert_ctx) + { + p = cert_end; + continue; + } + } + else if ((type = strstr(p, "PRIVATE KEY")) && type < cert_end) + { + /* We only read the first key, further keys will be ignored */ + if (ctx->der_key) + { + p = cert_end; + continue; + } + } + else + { + pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Unknown or unsupported X509 PEM type"); + goto error; + } + + if (!(der_buffer = ma_schannel_convert_base64(pvio, p, (DWORD)(cert_end - p), &der_buffer_length))) + goto error; + + if (is_cert) + { + if (!(ctx->client_cert_ctx = (CERT_CONTEXT*)CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + der_buffer, der_buffer_length))) + { + ma_schannel_set_win_error(pvio); + goto error; + } + } + else + { + if (!(ctx->der_key= (struct st_DER *)malloc(sizeof(struct st_DER)))) + { + pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Not enough memory"); + goto error; + } + + ctx->der_key->der_buffer = der_buffer; + ctx->der_key->der_length = der_buffer_length; + + der_buffer = 0; + der_buffer_length = 0; + p = cert_end; + } + + free(der_buffer); + der_buffer = 0; + der_buffer_length = 0; + p = cert_end; + + if (ctx->client_cert_ctx && ctx->der_key) + break; + } + free(buffer); + return 0; +error: + if (buffer) + free(buffer); + if (der_buffer) + free(der_buffer); + return 1; +} + +void ma_delete_key_buffer(SC_CTX* ctx) +{ + if (!ctx->der_key) + return; + free(ctx->der_key->der_buffer); + free(ctx->der_key); + ctx->der_key = 0; +} + + /* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_PVIO *pvio, const char *pem_file) */ /* Create a certification context from ca or cert file @@ -281,7 +471,7 @@ PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_PVIO *pvio, const char *pem ma_schannel_set_win_error(pvio); end: if (der_buffer) - LocalFree(der_buffer); + free(der_buffer); return ctx; } /* }}} */ @@ -306,10 +496,8 @@ end: PCCRL_CONTEXT A pointer to a certification context structure */ -my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, const CERT_CONTEXT *ctx, char *key_file) +my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, SC_CTX *ctx) { - DWORD der_buffer_len= 0; - LPBYTE der_buffer= NULL; DWORD priv_key_len= 0; LPBYTE priv_key= NULL; HCRYPTPROV crypt_prov= 0; @@ -317,14 +505,16 @@ my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, const CERT_CONTEXT *ctx CERT_KEY_CONTEXT kpi={ 0 }; my_bool rc= 0; - /* load private key into der binary object */ - if (!(der_buffer= ma_schannel_load_pem(pvio, key_file, &der_buffer_len))) - return 0; + if (!ctx->der_key || !ctx->client_cert_ctx) + { + pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Invalid certificate or key"); + goto end; + } /* determine required buffer size for decoded private key */ if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, - der_buffer, der_buffer_len, + ctx->der_key->der_buffer, ctx->der_key->der_length, 0, NULL, NULL, &priv_key_len)) { @@ -333,18 +523,17 @@ my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, const CERT_CONTEXT *ctx } /* allocate buffer for decoded private key */ - if (!(priv_key= LocalAlloc(0, priv_key_len))) + if (!(priv_key= malloc(priv_key_len))) { pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); goto end; } - /* decode */ if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - PKCS_RSA_PRIVATE_KEY, - der_buffer, der_buffer_len, - 0, NULL, - priv_key, &priv_key_len)) + PKCS_RSA_PRIVATE_KEY, + ctx->der_key->der_buffer, ctx->der_key->der_length, + 0, NULL, + priv_key, &priv_key_len)) { ma_schannel_set_win_error(pvio); goto end; @@ -368,19 +557,23 @@ my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, const CERT_CONTEXT *ctx kpi.cbSize= sizeof(kpi); /* assign private key to certificate context */ - if (CertSetCertificateContextProperty(ctx, CERT_KEY_CONTEXT_PROP_ID, 0, &kpi)) + if (CertSetCertificateContextProperty(ctx->client_cert_ctx, CERT_KEY_CONTEXT_PROP_ID, 0, &kpi)) rc= 1; else ma_schannel_set_win_error(pvio); end: - if (der_buffer) - LocalFree(der_buffer); + if (ctx->der_key) + { + free(ctx->der_key->der_buffer); + free(ctx->der_key); + ctx->der_key = 0; + } if (priv_key) { if (crypt_key) CryptDestroyKey(crypt_key); - LocalFree(priv_key); + free(priv_key); if (!rc) if (crypt_prov) CryptReleaseContext(crypt_prov, 0); diff --git a/libmariadb/secure/ma_schannel.h b/libmariadb/secure/ma_schannel.h index 6471d8f7..abe54a63 100644 --- a/libmariadb/secure/ma_schannel.h +++ b/libmariadb/secure/ma_schannel.h @@ -46,9 +46,15 @@ #include +struct st_DER { + char* der_buffer; + DWORD der_length; +}; + struct st_schannel { HCERTSTORE cert_store; const CERT_CONTEXT *client_cert_ctx; + struct st_DER *der_key; CredHandle CredHdl; my_bool FreeCredHdl; PUCHAR IoBuffer; @@ -70,8 +76,9 @@ 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_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, const CERT_CONTEXT *ctx, char *key_file); +my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, SC_CTX *ctx); PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_PVIO *pvio, const char *pem_file); +void ma_delete_key_buffer(SC_CTX* ctx); my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls); ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, uchar *WriteBuffer, diff --git a/libmariadb/secure/schannel.c b/libmariadb/secure/schannel.c index 014f6d4e..d1f519be 100644 --- a/libmariadb/secure/schannel.c +++ b/libmariadb/secure/schannel.c @@ -154,7 +154,6 @@ cipher_map[] = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DHE-RSA-AES256-GCM-SHA384", { CALG_DH_EPHEM, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN } } - }; #define MAX_ALG_ID 50 @@ -206,6 +205,7 @@ static int ma_tls_set_client_certs(MARIADB_TLS *ctls) MARIADB_PVIO *pvio= ctls->pvio; sctx->client_cert_ctx= NULL; + sctx->der_key = NULL; if (!certfile && keyfile) certfile= keyfile; @@ -215,15 +215,26 @@ static int ma_tls_set_client_certs(MARIADB_TLS *ctls) if (!certfile) return 0; - if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(ctls->pvio, certfile))) + if (certfile && ma_schannel_load_certs_and_keys(pvio, certfile, sctx)) return 1; - if (!ma_schannel_load_private_key(pvio, sctx->client_cert_ctx, keyfile)) + if (keyfile && ma_schannel_load_certs_and_keys(pvio, keyfile, sctx)) + return 1; + + if (sctx->client_cert_ctx) { - CertFreeCertificateContext(sctx->client_cert_ctx); - sctx->client_cert_ctx= NULL; + if (!ma_schannel_load_private_key(pvio, sctx)) + return 1; + } + else if (sctx->der_key) + { + free(sctx->der_key->der_buffer); + free(sctx->der_key); + sctx->der_key = 0; + pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Cert not found"); return 1; } + return 0; } /* }}} */ @@ -293,6 +304,9 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) SC_CTX *sctx; SECURITY_STATUS sRet; ALG_ID AlgId[MAX_ALG_ID]; + size_t i; + DWORD protocol = 0; + if (!ctls || !ctls->pvio) return 1;; @@ -310,10 +324,8 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) /* Set cipher */ if (mysql->options.ssl_cipher) { - size_t i; - DWORD protocol = 0; - /* check if a protocol was specified as a cipher: + /* check if a protocol was specified as a cipher: * In this case don't allow cipher suites which belong to newer protocols * Please note: There are no cipher suites for TLS1.1 */ @@ -533,13 +545,15 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls) SecPkgContext_CipherInfo CipherInfo = { SECPKGCONTEXT_CIPHERINFO_V1 }; SECURITY_STATUS sRet; SC_CTX *sctx; + SecPkgContext_ConnectionInfo ci; if (!ctls || !ctls->ssl) return NULL; sctx= (SC_CTX *)ctls->ssl; + sRet = QueryContextAttributesA(&sctx->ctxt, SECPKG_ATTR_CONNECTION_INFO, (PVOID)&ci); + sRet= QueryContextAttributesA(&sctx->ctxt, SECPKG_ATTR_CIPHER_INFO, (PVOID)&CipherInfo); - sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_CIPHER_INFO, (PVOID)&CipherInfo); if (sRet != SEC_E_OK) return NULL; diff --git a/unittest/libmariadb/ssl.c.in b/unittest/libmariadb/ssl.c.in index 6dd4ed85..c3ce6875 100644 --- a/unittest/libmariadb/ssl.c.in +++ b/unittest/libmariadb/ssl.c.in @@ -16,6 +16,11 @@ or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ +#if defined(WIN32) && defined(HEAP_CHECK) +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif #include "my_test.h" #include @@ -34,6 +39,7 @@ const char *ssluser= "ssluser"; const char *sslpw= "sslpw"; char sslhost[128]; char sslcert[FNLEN]; +char sslcombined[FNLEN]; char sslkey[FNLEN]; char sslkey_enc[FNLEN]; char sslca[FNLEN]; @@ -63,6 +69,7 @@ int check_skip_ssl() } } snprintf(sslcert, FNLEN - 1, "%s/%s", ssldir, "client-cert.pem"); + snprintf(sslcombined, FNLEN - 1, "%s/%s", ssldir, "client-certkey.pem"); snprintf(sslkey, FNLEN - 1, "%s/%s", ssldir, "client-key.pem"); snprintf(sslkey_enc, FNLEN - 1, "%s/%s", ssldir, "client-key-enc.pem"); snprintf(sslca, FNLEN - 1, "%s/%s", ssldir, "cacert.pem"); @@ -1287,6 +1294,33 @@ static int test_mdev14101(MYSQL *my __attribute__((unused))) return OK; } +static int test_conc386(MYSQL *mysql) +{ +#ifdef WIN32 + if (_access(sslcombined, 0) == -1) +#else + if (access(sslcombined, R_OK) != 0) +#endif + { + diag("combined cert/key file not found"); + return SKIP; + } + + mysql= mysql_init(NULL); + mysql_ssl_set(mysql, + sslcombined, + NULL, + NULL, + NULL, + NULL); + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(mysql)); + FAIL_IF(check_cipher(mysql) != 0, "Invalid cipher"); + mysql_close(mysql); + unlink(sslcombined); + return OK; +} + struct my_tests_st my_tests[] = { {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_mdev14101", test_mdev14101, TEST_CONNECTION_NEW, 0, NULL, NULL}, @@ -1323,6 +1357,8 @@ struct my_tests_st my_tests[] = { #else {"test_schannel_cipher", test_schannel_cipher, TEST_CONNECTION_NEW, 0, NULL, NULL}, #endif + + {"test_conc386", test_conc386, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"drop_ssl_user", drop_ssl_user, TEST_CONNECTION_NEW, 0, NULL, NULL}, {NULL, NULL, 0, 0, NULL, NULL} }; @@ -1330,6 +1366,15 @@ struct my_tests_st my_tests[] = { int main(int argc, char **argv) { +#if defined(WIN32) && defined(HEAP_CHECK) + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT ); +#endif + get_envvars(); if (argc > 1) @@ -1337,6 +1382,9 @@ int main(int argc, char **argv) run_tests(my_tests); mysql_server_end(); +#if defined(WIN32) && defined(HEAP_CHECK) + _CrtDumpMemoryLeaks(); +#endif return(exit_status()); }