1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-07 02:42:49 +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)
{
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);

View File

@@ -46,9 +46,15 @@
#include <ma_pthread.h>
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,

View File

@@ -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;

View File

@@ -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 <stdlib.h>
#include <crtdbg.h>
#endif
#include "my_test.h"
#include <ma_pthread.h>
@@ -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());
}