You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-07 02:42:49 +03:00
Use SCH_CREDENTIAL structure, to support TLSv1.3 Allow TLSv1.3 starting with Windows 11 / Server 2022, which are first Windows releases to officially support latest TLS version
758 lines
21 KiB
C
758 lines
21 KiB
C
/************************************************************************************
|
|
Copyright (C) 2014 MariaDB Corporation Ab
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
|
or write to the Free Software Foundation, Inc.,
|
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
|
|
|
*************************************************************************************/
|
|
|
|
#define SCHANNEL_USE_BLACKLISTS
|
|
#include <windows.h>
|
|
#include <winternl.h>
|
|
|
|
#include "ma_schannel.h"
|
|
#include "schannel_certs.h"
|
|
#include <string.h>
|
|
|
|
extern my_bool ma_tls_initialized;
|
|
char tls_library_version[] = "Schannel";
|
|
|
|
#define PROT_SSL3 SP_PROT_SSL3_CLIENT
|
|
#define PROT_TLS1_0 SP_PROT_TLS1_0_CLIENT
|
|
#define PROT_TLS1_1 SP_PROT_TLS1_1_CLIENT
|
|
#define PROT_TLS1_2 SP_PROT_TLS1_2_CLIENT
|
|
#define PROT_TLS1_3 SP_PROT_TLS1_3_CLIENT
|
|
|
|
static struct
|
|
{
|
|
DWORD cipher_id;
|
|
DWORD protocol;
|
|
const char *iana_name;
|
|
const char *openssl_name;
|
|
ALG_ID algs[4]; /* exchange, encryption, hash, signature */
|
|
}
|
|
cipher_map[] =
|
|
{
|
|
{
|
|
0x0002,
|
|
PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
|
|
"TLS_RSA_WITH_NULL_SHA", "NULL-SHA",
|
|
{ CALG_RSA_KEYX, 0, CALG_SHA1, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x0004,
|
|
PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
|
|
"TLS_RSA_WITH_RC4_128_MD5", "RC4-MD5",
|
|
{ CALG_RSA_KEYX, CALG_RC4, CALG_MD5, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x0005,
|
|
PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
|
|
"TLS_RSA_WITH_RC4_128_SHA", "RC4-SHA",
|
|
{ CALG_RSA_KEYX, CALG_RC4, CALG_SHA1, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x000A,
|
|
PROT_SSL3,
|
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA", "DES-CBC3-SHA",
|
|
{CALG_RSA_KEYX, CALG_3DES, CALG_SHA1, CALG_DSS_SIGN}
|
|
},
|
|
{
|
|
0x0013,
|
|
PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
|
|
"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "EDH-DSS-DES-CBC3-SHA",
|
|
{ CALG_DH_EPHEM, CALG_3DES, CALG_SHA1, CALG_DSS_SIGN }
|
|
},
|
|
{
|
|
0x002F,
|
|
PROT_SSL3 | PROT_TLS1_0 | PROT_TLS1_2,
|
|
"TLS_RSA_WITH_AES_128_CBC_SHA", "AES128-SHA",
|
|
{ CALG_RSA_KEYX, CALG_AES_128, CALG_SHA, CALG_RSA_SIGN}
|
|
},
|
|
{
|
|
0x0032,
|
|
PROT_TLS1_0 | PROT_TLS1_2,
|
|
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "DHE-DSS-AES128-SHA",
|
|
{ CALG_DH_EPHEM, CALG_AES_128, CALG_SHA1, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x0033,
|
|
PROT_TLS1_0 | PROT_TLS1_2,
|
|
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "DHE-RSA-AES128-SHA",
|
|
{ CALG_DH_EPHEM, CALG_AES_128, CALG_SHA1, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x0035,
|
|
PROT_TLS1_0 | PROT_TLS1_2,
|
|
"TLS_RSA_WITH_AES_256_CBC_SHA", "AES256-SHA",
|
|
{ CALG_RSA_KEYX, CALG_AES_256, CALG_SHA1, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x0038,
|
|
PROT_TLS1_0 | PROT_TLS1_2,
|
|
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "DHE-DSS-AES256-SHA",
|
|
{ CALG_DH_EPHEM, CALG_AES_256, CALG_SHA1, CALG_DSS_SIGN }
|
|
},
|
|
{
|
|
0x0039,
|
|
PROT_TLS1_0 | PROT_TLS1_2,
|
|
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "DHE-RSA-AES256-SHA",
|
|
{ CALG_DH_EPHEM, CALG_AES_256, CALG_SHA1, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x003B,
|
|
PROT_TLS1_2,
|
|
"TLS_RSA_WITH_NULL_SHA256", "NULL-SHA256",
|
|
{ CALG_RSA_KEYX, 0, CALG_SHA_256, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x003C,
|
|
PROT_TLS1_2,
|
|
"TLS_RSA_WITH_AES_128_CBC_SHA256", "AES128-SHA256",
|
|
{ CALG_RSA_KEYX, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x003D,
|
|
PROT_TLS1_2,
|
|
"TLS_RSA_WITH_AES_256_CBC_SHA256", "AES256-SHA256",
|
|
{ CALG_RSA_KEYX, CALG_AES_256, CALG_SHA_256, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x0040,
|
|
PROT_TLS1_2,
|
|
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DHE-DSS-AES128-SHA256",
|
|
{ CALG_DH_EPHEM, CALG_AES_128, CALG_SHA_256, CALG_DSS_SIGN }
|
|
},
|
|
{
|
|
0x009C,
|
|
PROT_TLS1_2,
|
|
"TLS_RSA_WITH_AES_128_GCM_SHA256", "AES128-GCM-SHA256",
|
|
{ CALG_RSA_KEYX, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x009D,
|
|
PROT_TLS1_2,
|
|
"TLS_RSA_WITH_AES_256_GCM_SHA384", "AES256-GCM-SHA384",
|
|
{ CALG_RSA_KEYX, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x009E,
|
|
PROT_TLS1_2,
|
|
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DHE-RSA-AES128-GCM-SHA256",
|
|
{ CALG_DH_EPHEM, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0x009F,
|
|
PROT_TLS1_2,
|
|
"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 }
|
|
},
|
|
{
|
|
0xC027,
|
|
PROT_TLS1_2,
|
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDHE-RSA-AES128-SHA256",
|
|
{ CALG_ECDH, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
|
|
},
|
|
{
|
|
0xC028,
|
|
PROT_TLS1_2,
|
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDHE-RSA-AES256-SHA384",
|
|
{ CALG_ECDH, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
|
|
}
|
|
};
|
|
|
|
#define MAX_ALG_ID 50
|
|
|
|
extern void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo);
|
|
|
|
/*
|
|
Initializes SSL and allocate global
|
|
context SSL_context
|
|
|
|
SYNOPSIS
|
|
ma_tls_start
|
|
|
|
RETURN VALUES
|
|
0 success
|
|
1 error
|
|
*/
|
|
int ma_tls_start(char *errmsg, size_t errmsg_len)
|
|
{
|
|
ma_tls_initialized = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Release SSL and free resources
|
|
Will be automatically executed by
|
|
mysql_server_end() function
|
|
|
|
SYNOPSIS
|
|
ma_tls_end()
|
|
void
|
|
|
|
RETURN VALUES
|
|
void
|
|
*/
|
|
void ma_tls_end()
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* {{{ static int ma_tls_set_client_certs(MARIADB_TLS *ctls) */
|
|
static int ma_tls_set_client_certs(MARIADB_TLS *ctls, client_cert_handle *cert_handle)
|
|
{
|
|
MYSQL *mysql= ctls->pvio->mysql;
|
|
char *certfile= mysql->options.ssl_cert,
|
|
*keyfile= mysql->options.ssl_key;
|
|
MARIADB_PVIO *pvio= ctls->pvio;
|
|
char errmsg[256];
|
|
SECURITY_STATUS status;
|
|
|
|
if (!certfile && keyfile)
|
|
certfile= keyfile;
|
|
if (!keyfile && certfile)
|
|
keyfile= certfile;
|
|
|
|
if (!certfile)
|
|
return 0;
|
|
|
|
status = schannel_create_cert_context(certfile, keyfile, cert_handle, errmsg, sizeof(errmsg));
|
|
if (status)
|
|
{
|
|
pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, errmsg);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ void *ma_tls_init(MARIADB_TLS *ctls, MYSQL *mysql) */
|
|
void *ma_tls_init(MYSQL *mysql)
|
|
{
|
|
SC_CTX *sctx = (SC_CTX *)LocalAlloc(LMEM_ZEROINIT, sizeof(SC_CTX));
|
|
if (sctx)
|
|
{
|
|
SecInvalidateHandle(&sctx->CredHdl);
|
|
SecInvalidateHandle(&sctx->hCtxt);
|
|
}
|
|
return sctx;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/*
|
|
Maps between openssl suite names and schannel alg_ids.
|
|
Every suite has 4 algorithms (for exchange, encryption, hash and signing).
|
|
|
|
The input string is a set of suite names (openssl), separated
|
|
by ':'
|
|
|
|
The output is written into the array 'arr' of size 'arr_size'
|
|
The function returns number of elements written to the 'arr'.
|
|
*/
|
|
|
|
static struct _tls_version {
|
|
const char *tls_version;
|
|
DWORD protocol;
|
|
} tls_version[]= {
|
|
{"TLSv1.0", PROT_TLS1_0},
|
|
{"TLSv1.1", PROT_TLS1_1},
|
|
{"TLSv1.2", PROT_TLS1_2},
|
|
{"TLSv1.3", PROT_TLS1_3},
|
|
{"SSLv3", PROT_SSL3}
|
|
};
|
|
|
|
/* The following list was produced with OpenSSL 1.1.1j
|
|
by executing `openssl ciphers -V`. */
|
|
static struct {
|
|
DWORD dwCipherSuite;
|
|
const char *openssl_name;
|
|
} openssl_ciphers[] = {
|
|
{0x002F, "AES128-SHA"},
|
|
{0x0033, "DHE-RSA-AES128-SHA"},
|
|
{0x0035, "AES256-SHA"},
|
|
{0x0039, "DHE-RSA-AES256-SHA"},
|
|
{0x003C, "AES128-SHA256"},
|
|
{0x003D, "AES256-SHA256"},
|
|
{0x0067, "DHE-RSA-AES128-SHA256"},
|
|
{0x006B, "DHE-RSA-AES256-SHA256"},
|
|
{0x008C, "PSK-AES128-CBC-SHA"},
|
|
{0x008D, "PSK-AES256-CBC-SHA"},
|
|
{0x0090, "DHE-PSK-AES128-CBC-SHA"},
|
|
{0x0091, "DHE-PSK-AES256-CBC-SHA"},
|
|
{0x0094, "RSA-PSK-AES128-CBC-SHA"},
|
|
{0x0095, "RSA-PSK-AES256-CBC-SHA"},
|
|
{0x009C, "AES128-GCM-SHA256"},
|
|
{0x009D, "AES256-GCM-SHA384"},
|
|
{0x009E, "DHE-RSA-AES128-GCM-SHA256"},
|
|
{0x009F, "DHE-RSA-AES256-GCM-SHA384"},
|
|
{0x00A8, "PSK-AES128-GCM-SHA256"},
|
|
{0x00A9, "PSK-AES256-GCM-SHA384"},
|
|
{0x00AA, "DHE-PSK-AES128-GCM-SHA256"},
|
|
{0x00AB, "DHE-PSK-AES256-GCM-SHA384"},
|
|
{0x00AC, "RSA-PSK-AES128-GCM-SHA256"},
|
|
{0x00AD, "RSA-PSK-AES256-GCM-SHA384"},
|
|
{0x00AE, "PSK-AES128-CBC-SHA256"},
|
|
{0x00AF, "PSK-AES256-CBC-SHA384"},
|
|
{0x00B2, "DHE-PSK-AES128-CBC-SHA256"},
|
|
{0x00B3, "DHE-PSK-AES256-CBC-SHA384"},
|
|
{0x00B6, "RSA-PSK-AES128-CBC-SHA256"},
|
|
{0x00B7, "RSA-PSK-AES256-CBC-SHA384"},
|
|
{0x1301, "TLS_AES_128_GCM_SHA256"},
|
|
{0x1302, "TLS_AES_256_GCM_SHA384"},
|
|
{0x1303, "TLS_CHACHA20_POLY1305_SHA256"},
|
|
{0xC009, "ECDHE-ECDSA-AES128-SHA"},
|
|
{0xC00A, "ECDHE-ECDSA-AES256-SHA"},
|
|
{0xC013, "ECDHE-RSA-AES128-SHA"},
|
|
{0xC014, "ECDHE-RSA-AES256-SHA"},
|
|
{0xC01D, "SRP-AES-128-CBC-SHA"},
|
|
{0xC01E, "SRP-RSA-AES-128-CBC-SHA"},
|
|
{0xC020, "SRP-AES-256-CBC-SHA"},
|
|
{0xC021, "SRP-RSA-AES-256-CBC-SHA"},
|
|
{0xC023, "ECDHE-ECDSA-AES128-SHA256"},
|
|
{0xC024, "ECDHE-ECDSA-AES256-SHA384"},
|
|
{0xC027, "ECDHE-RSA-AES128-SHA256"},
|
|
{0xC028, "ECDHE-RSA-AES256-SHA384"},
|
|
{0xC02B, "ECDHE-ECDSA-AES128-GCM-SHA256"},
|
|
{0xC02C, "ECDHE-ECDSA-AES256-GCM-SHA384"},
|
|
{0xC02F, "ECDHE-RSA-AES128-GCM-SHA256"},
|
|
{0xC030, "ECDHE-RSA-AES256-GCM-SHA384"},
|
|
{0xC035, "ECDHE-PSK-AES128-CBC-SHA"},
|
|
{0xC036, "ECDHE-PSK-AES256-CBC-SHA"},
|
|
{0xC037, "ECDHE-PSK-AES128-CBC-SHA256"},
|
|
{0xC038, "ECDHE-PSK-AES256-CBC-SHA384"},
|
|
{0xCCA8, "ECDHE-RSA-CHACHA20-POLY1305"},
|
|
{0xCCA9, "ECDHE-ECDSA-CHACHA20-POLY1305"},
|
|
{0xCCAA, "DHE-RSA-CHACHA20-POLY1305"},
|
|
{0xCCAB, "PSK-CHACHA20-POLY1305"},
|
|
{0xCCAC, "ECDHE-PSK-CHACHA20-POLY1305"},
|
|
{0xCCAD, "DHE-PSK-CHACHA20-POLY1305"},
|
|
{0xCCAE, "RSA-PSK-CHACHA20-POLY1305"}
|
|
};
|
|
|
|
static size_t set_cipher(char * cipher_str, DWORD protocol, ALG_ID *arr , size_t arr_size)
|
|
{
|
|
char *token = strtok(cipher_str, ":");
|
|
size_t pos = 0;
|
|
|
|
while (token)
|
|
{
|
|
size_t i;
|
|
|
|
for(i = 0; i < sizeof(cipher_map)/sizeof(cipher_map[0]) ; i++)
|
|
{
|
|
if((pos + 4 < arr_size && strcmp(cipher_map[i].openssl_name, token) == 0) ||
|
|
(cipher_map[i].protocol <= protocol))
|
|
{
|
|
memcpy(arr + pos, cipher_map[i].algs, sizeof(ALG_ID)* 4);
|
|
pos += 4;
|
|
break;
|
|
}
|
|
}
|
|
token = strtok(NULL, ":");
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
static LONG ma_RtlGetVersion(RTL_OSVERSIONINFOEXW *osvi)
|
|
{
|
|
typedef LONG (WINAPI * func_RtlGetVersion)(RTL_OSVERSIONINFOEXW *);
|
|
static func_RtlGetVersion pRtlGetVersion;
|
|
if (!pRtlGetVersion)
|
|
pRtlGetVersion = (func_RtlGetVersion) (void*) GetProcAddress(GetModuleHandleW(L"ntdll.dll"),
|
|
"RtlGetVersion");
|
|
if (pRtlGetVersion)
|
|
{
|
|
return pRtlGetVersion(osvi);
|
|
}
|
|
return STATUS_ENTRYPOINT_NOT_FOUND;
|
|
}
|
|
|
|
/** Check if OS is modern enough to support SCH_CREDENTIALS struct */
|
|
static BOOL os_version_greater_equal(DWORD major, DWORD minor, DWORD build)
|
|
{
|
|
RTL_OSVERSIONINFOEXW osvi;
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
if (!ma_RtlGetVersion(&osvi))
|
|
{
|
|
return (osvi.dwMajorVersion > major)
|
|
|| (osvi.dwMajorVersion == major && osvi.dwMinorVersion > minor)
|
|
|| (osvi.dwMajorVersion == major && osvi.dwMinorVersion == minor && osvi.dwBuildNumber >= build);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
typedef struct _MA_SCHANNEL_CREDENTIALS {
|
|
BOOL use_old_cred_structure;
|
|
SCHANNEL_CRED schannel_cred;
|
|
SCH_CREDENTIALS sch_credentials;
|
|
ALG_ID AlgId[MAX_ALG_ID];
|
|
TLS_PARAMETERS tls_parameters;
|
|
} MA_SCHANNEL_CREDENTIALS;
|
|
|
|
/**
|
|
Initialize authentication data for the client side, before passing
|
|
it to AcquireCredentialsHandle()
|
|
|
|
Take care of specific TLS versions and cipher suites.
|
|
|
|
This function choses between the legacy and new credential structures
|
|
(SCHANNEL_CRED rsp SCH_CREDENTIALS) based on the OS version and the
|
|
requested cipher suite.
|
|
|
|
The new SCH_CREDENTIALS structure is not used, if a cipher suite is
|
|
requested, specific TLS protocol prior to TLSv1.3 is requested, or before
|
|
Windows 11 / Windows Server 2022.
|
|
|
|
@param ma_cred Pointer to the MA_SCHANNEL_CREDENTIALS structure
|
|
@param ssl_cipher The requested cipher suite
|
|
@param tls_ver The requested TLS version(s), comma separated
|
|
|
|
@return SEC_E_OK on success,
|
|
SEC_E_ALGORITHM_MISMATCH, if the requested cipher suite is not known,
|
|
SEC_E_UNSUPPORTED_FUNCTION,. TLSv1.3 is requested on an older Windows
|
|
*/
|
|
static SECURITY_STATUS init_auth_data(MA_SCHANNEL_CREDENTIALS *ma_cred,
|
|
char *ssl_cipher,
|
|
const char *tls_ver)
|
|
{
|
|
ma_cred->use_old_cred_structure = FALSE;
|
|
SCHANNEL_CRED *schannel_cred= &ma_cred->schannel_cred;
|
|
SCH_CREDENTIALS *sch_credentials= &ma_cred->sch_credentials;
|
|
DWORD protocol= 0;
|
|
BOOL ssl_cipher_is_protocol= FALSE;
|
|
|
|
if (ssl_cipher || tls_ver)
|
|
{
|
|
for (int i= 0; i < sizeof(tls_version) / sizeof(tls_version[0]); i++)
|
|
{
|
|
const char *v= tls_version[i].tls_version;
|
|
if (ssl_cipher && (_stricmp(ssl_cipher, v) == 0))
|
|
{
|
|
ssl_cipher_is_protocol= TRUE;
|
|
protocol|= tls_version[i].protocol;
|
|
}
|
|
else if (tls_ver && strstr(tls_ver, v))
|
|
{
|
|
protocol|= tls_version[i].protocol;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ssl_cipher && !ssl_cipher_is_protocol)
|
|
{
|
|
ma_cred->use_old_cred_structure= TRUE;
|
|
}
|
|
|
|
if (!os_version_greater_equal(10, 0, 22000))
|
|
{
|
|
ma_cred->use_old_cred_structure= TRUE;
|
|
}
|
|
|
|
if(protocol == PROT_TLS1_3 && ma_cred->use_old_cred_structure)
|
|
{
|
|
return SEC_E_UNSUPPORTED_FUNCTION;
|
|
}
|
|
|
|
if ((protocol != 0) && (protocol != PROT_TLS1_3))
|
|
{
|
|
ma_cred->use_old_cred_structure= TRUE;
|
|
}
|
|
|
|
if (ma_cred->use_old_cred_structure)
|
|
{
|
|
memset(schannel_cred, 0, sizeof(*schannel_cred));
|
|
schannel_cred->dwVersion= SCHANNEL_CRED_VERSION;
|
|
/*
|
|
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
|
|
*/
|
|
if (ssl_cipher)
|
|
{
|
|
memset(ma_cred->AlgId, 0, sizeof(ma_cred->AlgId));
|
|
schannel_cred->cSupportedAlgs=
|
|
(DWORD) set_cipher(ssl_cipher, protocol, ma_cred->AlgId, MAX_ALG_ID);
|
|
if (schannel_cred->cSupportedAlgs)
|
|
{
|
|
schannel_cred->palgSupportedAlgs= ma_cred->AlgId;
|
|
}
|
|
else if (!ssl_cipher_is_protocol)
|
|
{
|
|
/* Don't know those protocols. */
|
|
return SEC_E_ALGORITHM_MISMATCH;
|
|
}
|
|
}
|
|
schannel_cred->grbitEnabledProtocols= protocol & ~PROT_TLS1_3;
|
|
schannel_cred->dwFlags= SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS |
|
|
SCH_CRED_MANUAL_CRED_VALIDATION;
|
|
}
|
|
else
|
|
{
|
|
memset(sch_credentials, 0, sizeof(*sch_credentials));
|
|
sch_credentials->dwVersion= SCH_CREDENTIALS_VERSION;
|
|
sch_credentials->dwFlags= SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS |
|
|
SCH_CRED_MANUAL_CRED_VALIDATION;
|
|
if (protocol)
|
|
{
|
|
TLS_PARAMETERS *tls_parameters= &ma_cred->tls_parameters;
|
|
memset(tls_parameters, 0, sizeof(*tls_parameters));
|
|
|
|
tls_parameters->grbitDisabledProtocols= ~protocol;
|
|
sch_credentials->pTlsParameters= tls_parameters;
|
|
sch_credentials->cTlsParameters= 1;
|
|
}
|
|
}
|
|
return SEC_E_OK;
|
|
}
|
|
|
|
static void set_auth_data_cert(MA_SCHANNEL_CREDENTIALS *ma_cred, PCCERT_CONTEXT *cert)
|
|
{
|
|
if (!cert)
|
|
return;
|
|
if (ma_cred->use_old_cred_structure)
|
|
{
|
|
ma_cred->schannel_cred.cCreds= 1;
|
|
ma_cred->schannel_cred.paCred= cert;
|
|
}
|
|
else
|
|
{
|
|
ma_cred->sch_credentials.cCreds= 1;
|
|
ma_cred->sch_credentials.paCred= cert;
|
|
}
|
|
}
|
|
|
|
my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
|
{
|
|
MYSQL *mysql;
|
|
MA_SCHANNEL_CREDENTIALS ma_cred;
|
|
void *auth_data;
|
|
|
|
MARIADB_PVIO *pvio;
|
|
my_bool rc= 1;
|
|
SC_CTX *sctx;
|
|
SECURITY_STATUS sRet;
|
|
int verify_certs;
|
|
client_cert_handle cert_handle= {0};
|
|
|
|
if (!ctls)
|
|
return 1;
|
|
|
|
pvio= ctls->pvio;
|
|
sctx= (SC_CTX *)ctls->ssl;
|
|
if (!pvio || !sctx)
|
|
return 1;
|
|
|
|
mysql= pvio->mysql;
|
|
if (!mysql)
|
|
return 1;
|
|
|
|
sRet= init_auth_data(&ma_cred, mysql->options.ssl_cipher,
|
|
mysql->options.extension->tls_version);
|
|
switch(sRet)
|
|
{
|
|
case SEC_E_UNSUPPORTED_FUNCTION:
|
|
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
|
|
"TLS1.3 is not supported on Windows before Windows 11 or Windows Server 2022");
|
|
goto end;
|
|
case SEC_E_ALGORITHM_MISMATCH:
|
|
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
|
|
"Unknown cipher suite");
|
|
goto end;
|
|
case SEC_E_OK:
|
|
break;
|
|
default:
|
|
assert(0);
|
|
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
|
|
"Unknown error before the handshake");
|
|
goto end;
|
|
}
|
|
|
|
if (ma_tls_set_client_certs(ctls, &cert_handle))
|
|
goto end;
|
|
set_auth_data_cert(&ma_cred, cert_handle.cert?&cert_handle.cert:NULL);
|
|
auth_data= ma_cred.use_old_cred_structure ? (void*) &ma_cred.schannel_cred :(void*) &ma_cred.sch_credentials;
|
|
|
|
sRet= AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND,
|
|
NULL, auth_data, NULL, NULL, &sctx->CredHdl, NULL);
|
|
|
|
/* We do not need to keep certificates after this point */
|
|
schannel_free_cert_context(&cert_handle);
|
|
if (sRet)
|
|
{
|
|
ma_schannel_set_sec_error(pvio, sRet);
|
|
goto end;
|
|
}
|
|
if (ma_schannel_client_handshake(ctls) != SEC_E_OK)
|
|
goto end;
|
|
|
|
verify_certs = mysql->options.ssl_ca || mysql->options.ssl_capath ||
|
|
(mysql->options.extension->tls_verify_server_cert);
|
|
|
|
if (verify_certs)
|
|
{
|
|
if (!ma_schannel_verify_certs(ctls, mysql->options.extension->tls_verify_server_cert))
|
|
goto end;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
end:
|
|
if (cert_handle.cert)
|
|
schannel_free_cert_context(&cert_handle);
|
|
return rc;
|
|
}
|
|
|
|
ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
|
{
|
|
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
|
|
MARIADB_PVIO *pvio= ctls->pvio;
|
|
DWORD dlength= 0;
|
|
SECURITY_STATUS status;
|
|
SecBuffer tmp_extra_buf= {0};
|
|
|
|
retry:
|
|
status= ma_schannel_read_decrypt(pvio, &sctx->hCtxt, &dlength,
|
|
(uchar *) buffer, (DWORD) length);
|
|
if (tmp_extra_buf.cbBuffer)
|
|
{
|
|
/*
|
|
This memory was allocated in renegotiation processing
|
|
below, free it.
|
|
*/
|
|
LocalFree(tmp_extra_buf.pvBuffer);
|
|
tmp_extra_buf.cbBuffer= 0;
|
|
}
|
|
switch (status) {
|
|
case SEC_E_OK:
|
|
return (ssize_t) dlength;
|
|
case SEC_I_CONTEXT_EXPIRED:
|
|
/* Other side shut down the connection. */
|
|
return 0;
|
|
case SEC_I_RENEGOTIATE:
|
|
/* Rerun handshake steps */
|
|
tmp_extra_buf= sctx->extraBuf;
|
|
tmp_extra_buf.BufferType= SECBUFFER_TOKEN;
|
|
sctx->extraBuf.cbBuffer= 0;
|
|
sctx->extraBuf.pvBuffer= NULL;
|
|
status= ma_schannel_handshake_loop(pvio, FALSE, &tmp_extra_buf);
|
|
sctx->extraBuf= tmp_extra_buf;
|
|
if (status != SEC_E_OK)
|
|
return -1;
|
|
/*
|
|
If decrypt returned some decrypted bytes prior to
|
|
renegotiation, return them.
|
|
Otherwise, retry the read-decrypt again
|
|
*/
|
|
if (dlength)
|
|
return dlength;
|
|
|
|
goto retry;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
|
{
|
|
MARIADB_PVIO *pvio= ctls->pvio;
|
|
ssize_t rc, wlength= 0;
|
|
ssize_t remain= length;
|
|
|
|
while (remain > 0)
|
|
{
|
|
if ((rc= ma_schannel_write_encrypt(pvio, (uchar *)buffer + wlength, remain)) <= 0)
|
|
return rc;
|
|
wlength+= rc;
|
|
remain-= rc;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
/* {{{ my_bool ma_tls_close(MARIADB_PVIO *pvio) */
|
|
my_bool ma_tls_close(MARIADB_TLS *ctls)
|
|
{
|
|
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
|
|
|
|
if (sctx)
|
|
{
|
|
LocalFree(sctx->IoBuffer);
|
|
|
|
if (SecIsValidHandle(&sctx->CredHdl))
|
|
FreeCredentialHandle(&sctx->CredHdl);
|
|
|
|
if (SecIsValidHandle(&sctx->hCtxt))
|
|
DeleteSecurityContext(&sctx->hCtxt);
|
|
}
|
|
LocalFree(sctx);
|
|
return 0;
|
|
}
|
|
/* }}} */
|
|
|
|
int ma_tls_verify_server_cert(MARIADB_TLS *ctls)
|
|
{
|
|
/* Done elsewhere */
|
|
return 0;
|
|
}
|
|
|
|
static const char *cipher_name(const SecPkgContext_CipherInfo *CipherInfo)
|
|
{
|
|
size_t i;
|
|
|
|
for(i = 0; i < sizeof(openssl_ciphers)/sizeof(openssl_ciphers[0]) ; i++)
|
|
{
|
|
if (CipherInfo->dwCipherSuite == openssl_ciphers[i].dwCipherSuite)
|
|
return openssl_ciphers[i].openssl_name;
|
|
}
|
|
return "";
|
|
};
|
|
|
|
const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
|
|
{
|
|
SecPkgContext_CipherInfo CipherInfo = { SECPKGCONTEXT_CIPHERINFO_V1 };
|
|
SECURITY_STATUS sRet;
|
|
SC_CTX *sctx;
|
|
|
|
if (!ctls || !ctls->ssl)
|
|
return NULL;
|
|
|
|
sctx= (SC_CTX *)ctls->ssl;
|
|
sRet= QueryContextAttributesA(&sctx->hCtxt, SECPKG_ATTR_CIPHER_INFO, (PVOID)&CipherInfo);
|
|
|
|
if (sRet != SEC_E_OK)
|
|
return NULL;
|
|
|
|
return cipher_name(&CipherInfo);
|
|
}
|
|
|
|
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
|
|
{
|
|
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
|
|
PCCERT_CONTEXT pRemoteCertContext = NULL;
|
|
if (QueryContextAttributes(&sctx->hCtxt, 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;
|
|
}
|
|
|
|
void ma_tls_set_connection(MYSQL *mysql __attribute__((unused)))
|
|
{
|
|
return;
|
|
}
|