1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-08 14:02:17 +03:00

Merge commit 'b0411b731f5d61df38fe3f783437df13526774f2' into 3.1

This commit is contained in:
Georg Richter
2019-06-02 13:46:16 +02:00
4 changed files with 294 additions and 32 deletions

View File

@@ -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) static LPBYTE ma_schannel_load_pem(MARIADB_PVIO *pvio, const char *PemFileName, DWORD *buffer_len)
{ {
HANDLE hfile; HANDLE hfile= 0;
char *buffer= NULL; char *buffer= NULL;
DWORD dwBytesRead= 0; DWORD dwBytesRead= 0;
LPBYTE der_buffer= NULL; LPBYTE der_buffer= NULL;
DWORD der_buffer_length; DWORD der_buffer_length= 0;
if (buffer_len == NULL) if (buffer_len == NULL)
return NULL; return NULL;
@@ -156,7 +156,7 @@ static LPBYTE ma_schannel_load_pem(MARIADB_PVIO *pvio, const char *PemFileName,
goto end; 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); pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end; goto end;
@@ -192,7 +192,7 @@ static LPBYTE ma_schannel_load_pem(MARIADB_PVIO *pvio, const char *PemFileName,
} }
*buffer_len= der_buffer_length; *buffer_len= der_buffer_length;
LocalFree(buffer); free(buffer);
return der_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) */ /* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_PVIO *pvio, const char *pem_file) */
/* /*
Create a certification context from ca or cert 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); ma_schannel_set_win_error(pvio);
end: end:
if (der_buffer) if (der_buffer)
LocalFree(der_buffer); free(der_buffer);
return ctx; return ctx;
} }
/* }}} */ /* }}} */
@@ -306,10 +496,8 @@ end:
PCCRL_CONTEXT A pointer to a certification context structure 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; DWORD priv_key_len= 0;
LPBYTE priv_key= NULL; LPBYTE priv_key= NULL;
HCRYPTPROV crypt_prov= 0; 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 }; CERT_KEY_CONTEXT kpi={ 0 };
my_bool rc= 0; my_bool rc= 0;
/* load private key into der binary object */ if (!ctx->der_key || !ctx->client_cert_ctx)
if (!(der_buffer= ma_schannel_load_pem(pvio, key_file, &der_buffer_len))) {
return 0; 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 */ /* determine required buffer size for decoded private key */
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY, PKCS_RSA_PRIVATE_KEY,
der_buffer, der_buffer_len, ctx->der_key->der_buffer, ctx->der_key->der_length,
0, NULL, 0, NULL,
NULL, &priv_key_len)) 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 */ /* 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); pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end; goto end;
} }
/* decode */
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY, PKCS_RSA_PRIVATE_KEY,
der_buffer, der_buffer_len, ctx->der_key->der_buffer, ctx->der_key->der_length,
0, NULL, 0, NULL,
priv_key, &priv_key_len)) priv_key, &priv_key_len))
{ {
ma_schannel_set_win_error(pvio); ma_schannel_set_win_error(pvio);
goto end; goto end;
@@ -368,19 +557,23 @@ my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, const CERT_CONTEXT *ctx
kpi.cbSize= sizeof(kpi); kpi.cbSize= sizeof(kpi);
/* assign private key to certificate context */ /* 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; rc= 1;
else else
ma_schannel_set_win_error(pvio); ma_schannel_set_win_error(pvio);
end: end:
if (der_buffer) if (ctx->der_key)
LocalFree(der_buffer); {
free(ctx->der_key->der_buffer);
free(ctx->der_key);
ctx->der_key = 0;
}
if (priv_key) if (priv_key)
{ {
if (crypt_key) if (crypt_key)
CryptDestroyKey(crypt_key); CryptDestroyKey(crypt_key);
LocalFree(priv_key); free(priv_key);
if (!rc) if (!rc)
if (crypt_prov) if (crypt_prov)
CryptReleaseContext(crypt_prov, 0); CryptReleaseContext(crypt_prov, 0);

View File

@@ -46,9 +46,15 @@
#include <ma_pthread.h> #include <ma_pthread.h>
struct st_DER {
char* der_buffer;
DWORD der_length;
};
struct st_schannel { struct st_schannel {
HCERTSTORE cert_store; HCERTSTORE cert_store;
const CERT_CONTEXT *client_cert_ctx; const CERT_CONTEXT *client_cert_ctx;
struct st_DER *der_key;
CredHandle CredHdl; CredHandle CredHdl;
my_bool FreeCredHdl; my_bool FreeCredHdl;
PUCHAR IoBuffer; 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); 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_client_handshake(MARIADB_TLS *ctls);
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData); 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); 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); my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls);
ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
uchar *WriteBuffer, uchar *WriteBuffer,

View File

@@ -154,7 +154,6 @@ cipher_map[] =
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DHE-RSA-AES256-GCM-SHA384", "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 } { CALG_DH_EPHEM, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
} }
}; };
#define MAX_ALG_ID 50 #define MAX_ALG_ID 50
@@ -206,6 +205,7 @@ static int ma_tls_set_client_certs(MARIADB_TLS *ctls)
MARIADB_PVIO *pvio= ctls->pvio; MARIADB_PVIO *pvio= ctls->pvio;
sctx->client_cert_ctx= NULL; sctx->client_cert_ctx= NULL;
sctx->der_key = NULL;
if (!certfile && keyfile) if (!certfile && keyfile)
certfile= keyfile; certfile= keyfile;
@@ -215,15 +215,26 @@ static int ma_tls_set_client_certs(MARIADB_TLS *ctls)
if (!certfile) if (!certfile)
return 0; 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; 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); if (!ma_schannel_load_private_key(pvio, sctx))
sctx->client_cert_ctx= NULL; 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 1;
} }
return 0; return 0;
} }
/* }}} */ /* }}} */
@@ -293,6 +304,9 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
SC_CTX *sctx; SC_CTX *sctx;
SECURITY_STATUS sRet; SECURITY_STATUS sRet;
ALG_ID AlgId[MAX_ALG_ID]; ALG_ID AlgId[MAX_ALG_ID];
size_t i;
DWORD protocol = 0;
if (!ctls || !ctls->pvio) if (!ctls || !ctls->pvio)
return 1;; return 1;;
@@ -310,10 +324,8 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
/* Set cipher */ /* Set cipher */
if (mysql->options.ssl_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 * In this case don't allow cipher suites which belong to newer protocols
* Please note: There are no cipher suites for TLS1.1 * 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 }; SecPkgContext_CipherInfo CipherInfo = { SECPKGCONTEXT_CIPHERINFO_V1 };
SECURITY_STATUS sRet; SECURITY_STATUS sRet;
SC_CTX *sctx; SC_CTX *sctx;
SecPkgContext_ConnectionInfo ci;
if (!ctls || !ctls->ssl) if (!ctls || !ctls->ssl)
return NULL; return NULL;
sctx= (SC_CTX *)ctls->ssl; 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) if (sRet != SEC_E_OK)
return NULL; return NULL;

View File

@@ -16,6 +16,11 @@
or write to the Free Software Foundation, Inc., or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/ *************************************************************************************/
#if defined(WIN32) && defined(HEAP_CHECK)
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include "my_test.h" #include "my_test.h"
#include <ma_pthread.h> #include <ma_pthread.h>
@@ -34,6 +39,7 @@ const char *ssluser= "ssluser";
const char *sslpw= "sslpw"; const char *sslpw= "sslpw";
char sslhost[128]; char sslhost[128];
char sslcert[FNLEN]; char sslcert[FNLEN];
char sslcombined[FNLEN];
char sslkey[FNLEN]; char sslkey[FNLEN];
char sslkey_enc[FNLEN]; char sslkey_enc[FNLEN];
char sslca[FNLEN]; char sslca[FNLEN];
@@ -63,6 +69,7 @@ int check_skip_ssl()
} }
} }
snprintf(sslcert, FNLEN - 1, "%s/%s", ssldir, "client-cert.pem"); 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, FNLEN - 1, "%s/%s", ssldir, "client-key.pem");
snprintf(sslkey_enc, FNLEN - 1, "%s/%s", ssldir, "client-key-enc.pem"); snprintf(sslkey_enc, FNLEN - 1, "%s/%s", ssldir, "client-key-enc.pem");
snprintf(sslca, FNLEN - 1, "%s/%s", ssldir, "cacert.pem"); snprintf(sslca, FNLEN - 1, "%s/%s", ssldir, "cacert.pem");
@@ -1287,6 +1294,33 @@ static int test_mdev14101(MYSQL *my __attribute__((unused)))
return OK; 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[] = { struct my_tests_st my_tests[] = {
{"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_mdev14101", test_mdev14101, 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 #else
{"test_schannel_cipher", test_schannel_cipher, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_schannel_cipher", test_schannel_cipher, TEST_CONNECTION_NEW, 0, NULL, NULL},
#endif #endif
{"test_conc386", test_conc386, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"drop_ssl_user", drop_ssl_user, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"drop_ssl_user", drop_ssl_user, TEST_CONNECTION_NEW, 0, NULL, NULL},
{NULL, NULL, 0, 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) 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(); get_envvars();
if (argc > 1) if (argc > 1)
@@ -1337,6 +1382,9 @@ int main(int argc, char **argv)
run_tests(my_tests); run_tests(my_tests);
mysql_server_end(); mysql_server_end();
#if defined(WIN32) && defined(HEAP_CHECK)
_CrtDumpMemoryLeaks();
#endif
return(exit_status()); return(exit_status());
} }