mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-08-03 14:41:48 +02:00
Add MariaDB Connector/C as a built-in alternative (v3.2.3).
This commit is contained in:
1463
vendor/MDBC/libmariadb/secure/gnutls.c
vendored
Normal file
1463
vendor/MDBC/libmariadb/secure/gnutls.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
77
vendor/MDBC/libmariadb/secure/gnutls_crypt.c
vendored
Normal file
77
vendor/MDBC/libmariadb/secure/gnutls_crypt.c
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (C) 2018 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
|
||||
*/
|
||||
#include <ma_crypt.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
static gnutls_digest_algorithm_t ma_hash_get_algorithm(unsigned int alg)
|
||||
{
|
||||
switch(alg)
|
||||
{
|
||||
case MA_HASH_MD5:
|
||||
return GNUTLS_DIG_MD5;
|
||||
case MA_HASH_SHA1:
|
||||
return GNUTLS_DIG_SHA1;
|
||||
case MA_HASH_SHA256:
|
||||
return GNUTLS_DIG_SHA256;
|
||||
case MA_HASH_SHA384:
|
||||
return GNUTLS_DIG_SHA384;
|
||||
case MA_HASH_SHA512:
|
||||
return GNUTLS_DIG_SHA512;
|
||||
case MA_HASH_RIPEMD160:
|
||||
return GNUTLS_DIG_RMD160;
|
||||
default:
|
||||
return GNUTLS_DIG_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
MA_HASH_CTX *ma_hash_new(unsigned int algorithm, MA_HASH_CTX *unused_ctx __attribute__((unused)))
|
||||
{
|
||||
gnutls_hash_hd_t ctx= NULL;
|
||||
gnutls_digest_algorithm_t hash_alg= ma_hash_get_algorithm(algorithm);
|
||||
|
||||
/* unknown or unsupported hash algorithm */
|
||||
if (hash_alg == GNUTLS_DIG_UNKNOWN)
|
||||
return NULL;
|
||||
|
||||
if (gnutls_hash_init(&ctx, hash_alg) < 0)
|
||||
return NULL;
|
||||
|
||||
return (MA_HASH_CTX *)ctx;
|
||||
}
|
||||
|
||||
void ma_hash_free(MA_HASH_CTX *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
gnutls_hash_deinit((gnutls_hash_hd_t)ctx, NULL);
|
||||
}
|
||||
|
||||
void ma_hash_input(MA_HASH_CTX *ctx,
|
||||
const unsigned char *buffer,
|
||||
size_t len)
|
||||
{
|
||||
gnutls_hash((gnutls_hash_hd_t)ctx, (const void *)buffer, len);
|
||||
}
|
||||
|
||||
void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
|
||||
{
|
||||
gnutls_hash_output((gnutls_hash_hd_t)ctx, digest);
|
||||
}
|
||||
|
||||
|
637
vendor/MDBC/libmariadb/secure/ma_schannel.c
vendored
Normal file
637
vendor/MDBC/libmariadb/secure/ma_schannel.c
vendored
Normal file
@@ -0,0 +1,637 @@
|
||||
/************************************************************************************
|
||||
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
|
||||
|
||||
Author: Georg Richter
|
||||
|
||||
*************************************************************************************/
|
||||
#include "ma_schannel.h"
|
||||
#include "schannel_certs.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define SC_IO_BUFFER_SIZE 0x4000
|
||||
#define MAX_SSL_ERR_LEN 100
|
||||
|
||||
#define SCHANNEL_PAYLOAD(A) ((A).cbMaximumMessage + (A).cbHeader + (A).cbTrailer)
|
||||
void ma_schannel_set_win_error(MARIADB_PVIO *pvio, DWORD ErrorNo);
|
||||
|
||||
|
||||
|
||||
|
||||
/* {{{ void ma_schannel_set_sec_error */
|
||||
void ma_schannel_set_sec_error(MARIADB_PVIO* pvio, DWORD ErrorNo)
|
||||
{
|
||||
MYSQL* mysql = pvio->mysql;
|
||||
if (ErrorNo != SEC_E_OK)
|
||||
mysql->net.extension->extended_errno = ErrorNo;
|
||||
if (ErrorNo == SEC_E_INTERNAL_ERROR && GetLastError())
|
||||
{
|
||||
ma_schannel_set_win_error(pvio, GetLastError());
|
||||
return;
|
||||
}
|
||||
ma_schannel_set_win_error(pvio, ErrorNo);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#include "win32_errmsg.h"
|
||||
/* {{{ void ma_schnnel_set_win_error */
|
||||
void ma_schannel_set_win_error(MARIADB_PVIO *pvio, DWORD ErrorNo)
|
||||
{
|
||||
char buffer[256];
|
||||
ma_format_win32_error(buffer, sizeof(buffer), ErrorNo, "SSL connection error: ");
|
||||
pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, buffer);
|
||||
return;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData) */
|
||||
/*
|
||||
perform handshake loop
|
||||
|
||||
SYNOPSIS
|
||||
ma_schannel_handshake_loop()
|
||||
pvio Pointer to an Communication/IO structure
|
||||
InitialRead TRUE if it's the very first read
|
||||
ExtraData Pointer to an SecBuffer which contains extra data (sent by application)
|
||||
|
||||
|
||||
*/
|
||||
|
||||
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData)
|
||||
{
|
||||
SecBufferDesc OutBuffer, InBuffer;
|
||||
SecBuffer InBuffers[2], OutBuffers;
|
||||
DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
|
||||
TimeStamp tsExpiry;
|
||||
SECURITY_STATUS rc;
|
||||
PUCHAR IoBuffer;
|
||||
BOOL fDoRead;
|
||||
MARIADB_TLS *ctls= pvio->ctls;
|
||||
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
|
||||
|
||||
|
||||
dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
|
||||
ISC_REQ_REPLAY_DETECT |
|
||||
ISC_REQ_CONFIDENTIALITY |
|
||||
ISC_RET_EXTENDED_ERROR |
|
||||
ISC_REQ_ALLOCATE_MEMORY |
|
||||
ISC_REQ_STREAM;
|
||||
|
||||
|
||||
/* Allocate data buffer */
|
||||
if (!(IoBuffer = LocalAlloc(LMEM_FIXED, SC_IO_BUFFER_SIZE)))
|
||||
return SEC_E_INSUFFICIENT_MEMORY;
|
||||
|
||||
cbIoBuffer = 0;
|
||||
fDoRead = InitialRead;
|
||||
|
||||
/* handshake loop: We will leave if handshake is finished
|
||||
or an error occurs */
|
||||
|
||||
rc = SEC_I_CONTINUE_NEEDED;
|
||||
|
||||
while (rc == SEC_I_CONTINUE_NEEDED ||
|
||||
rc == SEC_E_INCOMPLETE_MESSAGE ||
|
||||
rc == SEC_I_INCOMPLETE_CREDENTIALS )
|
||||
{
|
||||
/* Read data */
|
||||
if (rc == SEC_E_INCOMPLETE_MESSAGE ||
|
||||
!cbIoBuffer)
|
||||
{
|
||||
if(fDoRead)
|
||||
{
|
||||
ssize_t nbytes = pvio->methods->read(pvio, IoBuffer + cbIoBuffer, (size_t)(SC_IO_BUFFER_SIZE - cbIoBuffer));
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
rc = SEC_E_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
cbData = (DWORD)nbytes;
|
||||
cbIoBuffer += cbData;
|
||||
}
|
||||
else
|
||||
fDoRead = TRUE;
|
||||
}
|
||||
|
||||
/* input buffers
|
||||
First buffer stores data received from server. leftover data
|
||||
will be stored in second buffer with BufferType SECBUFFER_EXTRA */
|
||||
|
||||
InBuffers[0].pvBuffer = IoBuffer;
|
||||
InBuffers[0].cbBuffer = cbIoBuffer;
|
||||
InBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||||
|
||||
InBuffers[1].pvBuffer = NULL;
|
||||
InBuffers[1].cbBuffer = 0;
|
||||
InBuffers[1].BufferType = SECBUFFER_EMPTY;
|
||||
|
||||
InBuffer.cBuffers = 2;
|
||||
InBuffer.pBuffers = InBuffers;
|
||||
InBuffer.ulVersion = SECBUFFER_VERSION;
|
||||
|
||||
|
||||
/* output buffer */
|
||||
OutBuffers.pvBuffer = NULL;
|
||||
OutBuffers.BufferType= SECBUFFER_TOKEN;
|
||||
OutBuffers.cbBuffer = 0;
|
||||
|
||||
OutBuffer.cBuffers = 1;
|
||||
OutBuffer.pBuffers = &OutBuffers;
|
||||
OutBuffer.ulVersion = SECBUFFER_VERSION;
|
||||
|
||||
|
||||
rc = InitializeSecurityContextA(&sctx->CredHdl,
|
||||
&sctx->hCtxt,
|
||||
NULL,
|
||||
dwSSPIFlags,
|
||||
0,
|
||||
SECURITY_NATIVE_DREP,
|
||||
&InBuffer,
|
||||
0,
|
||||
NULL,
|
||||
&OutBuffer,
|
||||
&dwSSPIOutFlags,
|
||||
&tsExpiry );
|
||||
|
||||
|
||||
if (rc == SEC_E_OK ||
|
||||
rc == SEC_I_CONTINUE_NEEDED ||
|
||||
(FAILED(rc) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)))
|
||||
{
|
||||
if(OutBuffers.cbBuffer && OutBuffers.pvBuffer)
|
||||
{
|
||||
ssize_t nbytes = pvio->methods->write(pvio, (uchar *)OutBuffers.pvBuffer, (size_t)OutBuffers.cbBuffer);
|
||||
if(nbytes <= 0)
|
||||
{
|
||||
FreeContextBuffer(OutBuffers.pvBuffer);
|
||||
DeleteSecurityContext(&sctx->hCtxt);
|
||||
return SEC_E_INTERNAL_ERROR;
|
||||
}
|
||||
cbData= (DWORD)nbytes;
|
||||
/* Free output context buffer */
|
||||
FreeContextBuffer(OutBuffers.pvBuffer);
|
||||
OutBuffers.pvBuffer = NULL;
|
||||
}
|
||||
}
|
||||
/* check if we need to read more data */
|
||||
switch (rc) {
|
||||
case SEC_E_INCOMPLETE_MESSAGE:
|
||||
/* we didn't receive all data, so just continue loop */
|
||||
continue;
|
||||
break;
|
||||
case SEC_E_OK:
|
||||
/* handshake completed, but we need to check if extra
|
||||
data was sent (which contains encrypted application data) */
|
||||
if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
|
||||
{
|
||||
if (!(pExtraData->pvBuffer= LocalAlloc(0, InBuffers[1].cbBuffer)))
|
||||
return SEC_E_INSUFFICIENT_MEMORY;
|
||||
|
||||
MoveMemory(pExtraData->pvBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
|
||||
pExtraData->BufferType = SECBUFFER_TOKEN;
|
||||
pExtraData->cbBuffer = InBuffers[1].cbBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
pExtraData->BufferType= SECBUFFER_EMPTY;
|
||||
pExtraData->pvBuffer= NULL;
|
||||
pExtraData->cbBuffer= 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SEC_I_INCOMPLETE_CREDENTIALS:
|
||||
/* Provided credentials didn't contain a valid client certificate.
|
||||
We will try to connect anonymously, using current credentials */
|
||||
fDoRead= FALSE;
|
||||
rc= SEC_I_CONTINUE_NEEDED;
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
if (FAILED(rc))
|
||||
{
|
||||
goto loopend;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
|
||||
{
|
||||
MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
|
||||
cbIoBuffer = InBuffers[1].cbBuffer;
|
||||
}
|
||||
else
|
||||
cbIoBuffer = 0;
|
||||
}
|
||||
loopend:
|
||||
if (FAILED(rc))
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, rc);
|
||||
DeleteSecurityContext(&sctx->hCtxt);
|
||||
}
|
||||
LocalFree(IoBuffer);
|
||||
|
||||
return rc;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls) */
|
||||
/*
|
||||
performs client side handshake
|
||||
|
||||
SYNOPSIS
|
||||
ma_schannel_client_handshake()
|
||||
ctls Pointer to a MARIADB_TLS structure
|
||||
|
||||
DESCRIPTION
|
||||
initiates a client/server handshake. This function can be used
|
||||
by clients only
|
||||
|
||||
RETURN
|
||||
SEC_E_OK on success
|
||||
*/
|
||||
|
||||
SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls)
|
||||
{
|
||||
MARIADB_PVIO *pvio;
|
||||
SECURITY_STATUS sRet;
|
||||
DWORD OutFlags;
|
||||
DWORD r;
|
||||
SC_CTX *sctx;
|
||||
SecBuffer ExtraData;
|
||||
DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
|
||||
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
|
||||
ISC_REQ_USE_SUPPLIED_CREDS |
|
||||
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
|
||||
|
||||
SecBufferDesc BufferOut;
|
||||
SecBuffer BuffersOut;
|
||||
|
||||
if (!ctls || !ctls->pvio)
|
||||
return 1;
|
||||
|
||||
pvio= ctls->pvio;
|
||||
sctx= (SC_CTX *)ctls->ssl;
|
||||
|
||||
/* Initialie securifty context */
|
||||
BuffersOut.BufferType= SECBUFFER_TOKEN;
|
||||
BuffersOut.cbBuffer= 0;
|
||||
BuffersOut.pvBuffer= NULL;
|
||||
|
||||
|
||||
BufferOut.cBuffers= 1;
|
||||
BufferOut.pBuffers= &BuffersOut;
|
||||
BufferOut.ulVersion= SECBUFFER_VERSION;
|
||||
|
||||
sRet = InitializeSecurityContext(&sctx->CredHdl,
|
||||
NULL,
|
||||
pvio->mysql->host,
|
||||
SFlags,
|
||||
0,
|
||||
SECURITY_NATIVE_DREP,
|
||||
NULL,
|
||||
0,
|
||||
&sctx->hCtxt,
|
||||
&BufferOut,
|
||||
&OutFlags,
|
||||
NULL);
|
||||
|
||||
if(sRet != SEC_I_CONTINUE_NEEDED)
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, sRet);
|
||||
return sRet;
|
||||
}
|
||||
|
||||
/* send client hello packaet */
|
||||
if(BuffersOut.cbBuffer != 0 && BuffersOut.pvBuffer != NULL)
|
||||
{
|
||||
ssize_t nbytes = (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut.pvBuffer, (size_t)BuffersOut.cbBuffer);
|
||||
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
sRet= SEC_E_INTERNAL_ERROR;
|
||||
goto end;
|
||||
}
|
||||
r = (DWORD)nbytes;
|
||||
}
|
||||
sRet= ma_schannel_handshake_loop(pvio, TRUE, &ExtraData);
|
||||
|
||||
/* allocate IO-Buffer for write operations: After handshake
|
||||
was successful, we are able now to calculate payload */
|
||||
if ((sRet = QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes )))
|
||||
goto end;
|
||||
|
||||
sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
|
||||
if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, sctx->IoBufferSize)))
|
||||
{
|
||||
sRet= SEC_E_INSUFFICIENT_MEMORY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
return sRet;
|
||||
end:
|
||||
if (BuffersOut.pvBuffer)
|
||||
FreeContextBuffer(BuffersOut.pvBuffer);
|
||||
return sRet;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext,
|
||||
DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */
|
||||
/*
|
||||
Reads encrypted data from a SSL stream and decrypts it.
|
||||
|
||||
SYNOPSIS
|
||||
ma_schannel_read
|
||||
pvio pointer to Communication IO structure
|
||||
phContext a context handle
|
||||
DecryptLength size of decrypted buffer
|
||||
ReadBuffer Buffer for decrypted data
|
||||
ReadBufferSize size of ReadBuffer
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
Reads decrypted data from a SSL stream and encrypts it.
|
||||
|
||||
RETURN
|
||||
SEC_E_OK on success
|
||||
SEC_E_* if an error occurred
|
||||
*/
|
||||
|
||||
SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
|
||||
CtxtHandle * phContext,
|
||||
DWORD *DecryptLength,
|
||||
uchar *ReadBuffer,
|
||||
DWORD ReadBufferSize)
|
||||
{
|
||||
ssize_t nbytes = 0;
|
||||
DWORD dwOffset = 0;
|
||||
SC_CTX *sctx;
|
||||
SECURITY_STATUS sRet = 0;
|
||||
SecBufferDesc Msg;
|
||||
SecBuffer Buffers[4];
|
||||
int i;
|
||||
|
||||
if (!pvio || !pvio->methods || !pvio->methods->read || !pvio->ctls || !DecryptLength)
|
||||
return SEC_E_INTERNAL_ERROR;
|
||||
|
||||
sctx = (SC_CTX *)pvio->ctls->ssl;
|
||||
*DecryptLength = 0;
|
||||
|
||||
if (sctx->dataBuf.cbBuffer)
|
||||
{
|
||||
/* Have unread decrypted data from the last time, copy. */
|
||||
nbytes = MIN(ReadBufferSize, sctx->dataBuf.cbBuffer);
|
||||
memcpy(ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
|
||||
sctx->dataBuf.pvBuffer = (char *)(sctx->dataBuf.pvBuffer) + nbytes;
|
||||
sctx->dataBuf.cbBuffer -= (DWORD)nbytes;
|
||||
*DecryptLength = (DWORD)nbytes;
|
||||
return SEC_E_OK;
|
||||
}
|
||||
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* Check for any encrypted data returned by last DecryptMessage() in SECBUFFER_EXTRA buffer. */
|
||||
if (sctx->extraBuf.cbBuffer)
|
||||
{
|
||||
memmove(sctx->IoBuffer, sctx->extraBuf.pvBuffer, sctx->extraBuf.cbBuffer);
|
||||
dwOffset = sctx->extraBuf.cbBuffer;
|
||||
sctx->extraBuf.cbBuffer = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
assert(sctx->IoBufferSize > dwOffset);
|
||||
if (dwOffset == 0 || sRet == SEC_E_INCOMPLETE_MESSAGE)
|
||||
{
|
||||
nbytes = pvio->methods->read(pvio, sctx->IoBuffer + dwOffset, (size_t)(sctx->IoBufferSize - dwOffset));
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
/* server closed connection, or an error */
|
||||
// todo: error
|
||||
return SEC_E_INVALID_HANDLE;
|
||||
}
|
||||
dwOffset += (DWORD)nbytes;
|
||||
}
|
||||
ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
|
||||
Buffers[0].pvBuffer = sctx->IoBuffer;
|
||||
Buffers[0].cbBuffer = dwOffset;
|
||||
|
||||
Buffers[0].BufferType = SECBUFFER_DATA;
|
||||
Buffers[1].BufferType = SECBUFFER_EMPTY;
|
||||
Buffers[2].BufferType = SECBUFFER_EMPTY;
|
||||
Buffers[3].BufferType = SECBUFFER_EMPTY;
|
||||
|
||||
Msg.ulVersion = SECBUFFER_VERSION; // Version number
|
||||
Msg.cBuffers = 4;
|
||||
Msg.pBuffers = Buffers;
|
||||
|
||||
sRet = DecryptMessage(phContext, &Msg, 0, NULL);
|
||||
|
||||
} while (sRet == SEC_E_INCOMPLETE_MESSAGE); /* Continue reading until full message arrives */
|
||||
|
||||
|
||||
if (sRet != SEC_E_OK)
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, sRet);
|
||||
return sRet;
|
||||
}
|
||||
|
||||
sctx->extraBuf.cbBuffer = 0;
|
||||
sctx->dataBuf.cbBuffer = 0;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (Buffers[i].BufferType == SECBUFFER_DATA)
|
||||
sctx->dataBuf = Buffers[i];
|
||||
if (Buffers[i].BufferType == SECBUFFER_EXTRA)
|
||||
sctx->extraBuf = Buffers[i];
|
||||
}
|
||||
|
||||
|
||||
if (sctx->dataBuf.cbBuffer)
|
||||
{
|
||||
assert(sctx->dataBuf.pvBuffer);
|
||||
/*
|
||||
Copy at most ReadBufferSize bytes to output.
|
||||
Store the rest (if any) to be processed next time.
|
||||
*/
|
||||
nbytes = MIN(sctx->dataBuf.cbBuffer, ReadBufferSize);
|
||||
memcpy((char *)ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
|
||||
sctx->dataBuf.cbBuffer -= (unsigned long)nbytes;
|
||||
sctx->dataBuf.pvBuffer = (char *)sctx->dataBuf.pvBuffer + nbytes;
|
||||
|
||||
*DecryptLength = (DWORD)nbytes;
|
||||
return SEC_E_OK;
|
||||
}
|
||||
// No data buffer, loop
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
#include "win32_errmsg.h"
|
||||
my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name)
|
||||
{
|
||||
SECURITY_STATUS status;
|
||||
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
MYSQL *mysql= pvio->mysql;
|
||||
SC_CTX *sctx = (SC_CTX *)ctls->ssl;
|
||||
const char *ca_file= mysql->options.ssl_ca;
|
||||
const char* ca_path = mysql->options.ssl_capath;
|
||||
const char *crl_file= mysql->options.extension ? mysql->options.extension->ssl_crl : NULL;
|
||||
const char* crl_path = mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL;
|
||||
PCCERT_CONTEXT pServerCert= NULL;
|
||||
char errmsg[256];
|
||||
HCERTSTORE store= NULL;
|
||||
int ret= 0;
|
||||
|
||||
status = schannel_create_store(ca_file, ca_path, crl_file, crl_path, &store, errmsg, sizeof(errmsg));
|
||||
if(status)
|
||||
goto end;
|
||||
|
||||
status = QueryContextAttributesA(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert);
|
||||
if (status)
|
||||
{
|
||||
ma_format_win32_error(errmsg, sizeof(errmsg), GetLastError(),
|
||||
"QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
status = schannel_verify_server_certificate(
|
||||
pServerCert,
|
||||
store,
|
||||
crl_file != 0 || crl_path != 0,
|
||||
mysql->host,
|
||||
verify_server_name,
|
||||
errmsg, sizeof(errmsg));
|
||||
|
||||
if (status)
|
||||
goto end;
|
||||
|
||||
ret= 1;
|
||||
|
||||
end:
|
||||
if (!ret)
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
"SSL connection error: %s", errmsg);
|
||||
}
|
||||
if (pServerCert)
|
||||
CertFreeCertificateContext(pServerCert);
|
||||
if(store)
|
||||
schannel_free_store(store);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* {{{ size_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext) */
|
||||
/*
|
||||
Decrypts data and write to SSL stream
|
||||
SYNOPSIS
|
||||
ma_schannel_write_decrypt
|
||||
pvio pointer to Communication IO structure
|
||||
phContext a context handle
|
||||
DecryptLength size of decrypted buffer
|
||||
ReadBuffer Buffer for decrypted data
|
||||
ReadBufferSize size of ReadBuffer
|
||||
|
||||
DESCRIPTION
|
||||
Write encrypted data to SSL stream.
|
||||
|
||||
RETURN
|
||||
SEC_E_OK on success
|
||||
SEC_E_* if an error occurred
|
||||
*/
|
||||
ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
|
||||
uchar *WriteBuffer,
|
||||
size_t WriteBufferSize)
|
||||
{
|
||||
SECURITY_STATUS scRet;
|
||||
SecBufferDesc Message;
|
||||
SecBuffer Buffers[4];
|
||||
DWORD cbMessage;
|
||||
PBYTE pbMessage;
|
||||
SC_CTX *sctx= (SC_CTX *)pvio->ctls->ssl;
|
||||
size_t payload;
|
||||
ssize_t nbytes;
|
||||
DWORD write_size;
|
||||
|
||||
payload= MIN(WriteBufferSize, sctx->Sizes.cbMaximumMessage);
|
||||
|
||||
memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload);
|
||||
pbMessage = sctx->IoBuffer + sctx->Sizes.cbHeader;
|
||||
cbMessage = (DWORD)payload;
|
||||
|
||||
Buffers[0].pvBuffer = sctx->IoBuffer;
|
||||
Buffers[0].cbBuffer = sctx->Sizes.cbHeader;
|
||||
Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; // Type of the buffer
|
||||
|
||||
Buffers[1].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader];
|
||||
Buffers[1].cbBuffer = (DWORD)payload;
|
||||
Buffers[1].BufferType = SECBUFFER_DATA;
|
||||
|
||||
Buffers[2].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader] + payload;
|
||||
Buffers[2].cbBuffer = sctx->Sizes.cbTrailer;
|
||||
Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
|
||||
|
||||
Buffers[3].pvBuffer = SECBUFFER_EMPTY; // Pointer to buffer 4
|
||||
Buffers[3].cbBuffer = SECBUFFER_EMPTY; // length of buffer 4
|
||||
Buffers[3].BufferType = SECBUFFER_EMPTY; // Type of the buffer 4
|
||||
|
||||
|
||||
Message.ulVersion = SECBUFFER_VERSION;
|
||||
Message.cBuffers = 4;
|
||||
Message.pBuffers = Buffers;
|
||||
if ((scRet = EncryptMessage(&sctx->hCtxt, 0, &Message, 0))!= SEC_E_OK)
|
||||
return -1;
|
||||
write_size = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
|
||||
nbytes = pvio->methods->write(pvio, sctx->IoBuffer, write_size);
|
||||
return nbytes == write_size ? payload : -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
extern char *ssl_protocol_version[5];
|
||||
|
||||
/* {{{ ma_tls_get_protocol_version(MARIADB_TLS *ctls) */
|
||||
int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
|
||||
{
|
||||
SC_CTX *sctx;
|
||||
SecPkgContext_ConnectionInfo ConnectionInfo;
|
||||
if (!ctls->ssl)
|
||||
return 1;
|
||||
|
||||
sctx= (SC_CTX *)ctls->ssl;
|
||||
|
||||
if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_CONNECTION_INFO, &ConnectionInfo) != SEC_E_OK)
|
||||
return -1;
|
||||
|
||||
switch(ConnectionInfo.dwProtocol)
|
||||
{
|
||||
case SP_PROT_SSL3_CLIENT:
|
||||
return PROTOCOL_SSLV3;
|
||||
case SP_PROT_TLS1_CLIENT:
|
||||
return PROTOCOL_TLS_1_0;
|
||||
case SP_PROT_TLS1_1_CLIENT:
|
||||
return PROTOCOL_TLS_1_1;
|
||||
case SP_PROT_TLS1_2_CLIENT:
|
||||
return PROTOCOL_TLS_1_2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
87
vendor/MDBC/libmariadb/secure/ma_schannel.h
vendored
Normal file
87
vendor/MDBC/libmariadb/secure/ma_schannel.h
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/************************************************************************************
|
||||
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
|
||||
|
||||
Author: Georg Richter
|
||||
|
||||
*************************************************************************************/
|
||||
#ifndef _ma_schannel_h_
|
||||
#define _ma_schannel_h_
|
||||
|
||||
#define SECURITY_WIN32
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_common.h>
|
||||
#include <ma_pvio.h>
|
||||
#include <errmsg.h>
|
||||
|
||||
|
||||
#include <wincrypt.h>
|
||||
#include <wintrust.h>
|
||||
|
||||
|
||||
#include <security.h>
|
||||
|
||||
#include <schnlsp.h>
|
||||
#undef SECURITY_WIN32
|
||||
#include <windows.h>
|
||||
#include <sspi.h>
|
||||
|
||||
#define SC_IO_BUFFER_SIZE 0x4000
|
||||
|
||||
|
||||
#include <ma_pthread.h>
|
||||
|
||||
struct st_DER {
|
||||
char* der_buffer;
|
||||
DWORD der_length;
|
||||
};
|
||||
|
||||
struct st_schannel {
|
||||
CredHandle CredHdl;
|
||||
PUCHAR IoBuffer;
|
||||
DWORD IoBufferSize;
|
||||
SecPkgContext_StreamSizes Sizes;
|
||||
CtxtHandle hCtxt;
|
||||
|
||||
/* Cached data from the last read/decrypt call.*/
|
||||
SecBuffer extraBuf; /* encrypted data read from server. */
|
||||
SecBuffer dataBuf; /* decrypted but still unread data from server.*/
|
||||
|
||||
};
|
||||
|
||||
typedef struct st_schannel SC_CTX;
|
||||
|
||||
extern HCERTSTORE ca_CertStore, crl_CertStore;
|
||||
extern my_bool ca_Check, crl_Check;
|
||||
|
||||
;
|
||||
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_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name);
|
||||
ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
|
||||
uchar *WriteBuffer,
|
||||
size_t WriteBufferSize);
|
||||
SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
|
||||
CtxtHandle* phContext,
|
||||
DWORD *DecryptLength,
|
||||
uchar *ReadBuffer,
|
||||
DWORD ReadBufferSize);
|
||||
|
||||
|
||||
#endif /* _ma_schannel_h_ */
|
797
vendor/MDBC/libmariadb/secure/openssl.c
vendored
Normal file
797
vendor/MDBC/libmariadb/secure/openssl.c
vendored
Normal file
@@ -0,0 +1,797 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2012 Monty Program 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
|
||||
|
||||
*************************************************************************************/
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_common.h>
|
||||
#include <ma_pvio.h>
|
||||
#include <errmsg.h>
|
||||
#include <string.h>
|
||||
#include <mysql/client_plugin.h>
|
||||
#include <string.h>
|
||||
#include <openssl/ssl.h> /* SSL and SSL_CTX */
|
||||
#include <openssl/err.h> /* error reporting */
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/md4.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C)
|
||||
#include <openssl/applink.c>
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
#include <openssl/x509v3.h>
|
||||
#define HAVE_OPENSSL_CHECK_HOST 1
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
#define HAVE_OPENSSL_1_1_API
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10000000L
|
||||
#define SSL_OP_NO_TLSv1_1 0L
|
||||
#define SSL_OP_NO_TLSv1_2 0L
|
||||
#define CRYPTO_THREADID_set_callback CRYPTO_set_id_callback
|
||||
#define CRYPTO_THREADID_get_callback CRYPTO_get_id_callback
|
||||
#endif
|
||||
|
||||
#if defined(OPENSSL_USE_BIOMETHOD)
|
||||
#undef OPENSSL_USE_BIOMETHOD
|
||||
#endif
|
||||
#ifndef HAVE_OPENSSL_DEFAULT
|
||||
#include <memory.h>
|
||||
#define ma_malloc(A,B) malloc((A))
|
||||
#undef ma_free
|
||||
#define ma_free(A) free((A))
|
||||
#define ma_snprintf snprintf
|
||||
#define ma_vsnprintf vsnprintf
|
||||
#undef SAFE_MUTEX
|
||||
#endif
|
||||
#include <ma_pthread.h>
|
||||
|
||||
#include <mariadb_async.h>
|
||||
#include <ma_context.h>
|
||||
|
||||
extern my_bool ma_tls_initialized;
|
||||
extern unsigned int mariadb_deinitialize_ssl;
|
||||
|
||||
#define MAX_SSL_ERR_LEN 100
|
||||
char tls_library_version[TLS_VERSION_LENGTH];
|
||||
|
||||
static pthread_mutex_t LOCK_openssl_config;
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
static pthread_mutex_t *LOCK_crypto= NULL;
|
||||
#endif
|
||||
#if defined(OPENSSL_USE_BIOMETHOD)
|
||||
static int ma_bio_read(BIO *h, char *buf, int size);
|
||||
static int ma_bio_write(BIO *h, const char *buf, int size);
|
||||
static BIO_METHOD ma_BIO_method;
|
||||
#endif
|
||||
|
||||
|
||||
static long ma_tls_version_options(const char *version)
|
||||
{
|
||||
long protocol_options,
|
||||
disable_all_protocols;
|
||||
|
||||
protocol_options= disable_all_protocols=
|
||||
SSL_OP_NO_SSLv2 |
|
||||
SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_TLSv1 |
|
||||
SSL_OP_NO_TLSv1_1 |
|
||||
SSL_OP_NO_TLSv1_2
|
||||
#ifdef TLS1_3_VERSION
|
||||
| SSL_OP_NO_TLSv1_3
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!version)
|
||||
return 0;
|
||||
|
||||
if (strstr(version, "TLSv1.0"))
|
||||
protocol_options&= ~SSL_OP_NO_TLSv1;
|
||||
if (strstr(version, "TLSv1.1"))
|
||||
protocol_options&= ~SSL_OP_NO_TLSv1_1;
|
||||
if (strstr(version, "TLSv1.2"))
|
||||
protocol_options&= ~SSL_OP_NO_TLSv1_2;
|
||||
#ifdef TLS1_3_VERSION
|
||||
if (strstr(version, "TLSv1.3"))
|
||||
protocol_options&= ~SSL_OP_NO_TLSv1_3;
|
||||
#endif
|
||||
|
||||
if (protocol_options != disable_all_protocols)
|
||||
return protocol_options;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ma_tls_set_error(MYSQL *mysql)
|
||||
{
|
||||
ulong ssl_errno= ERR_get_error();
|
||||
char ssl_error[MAX_SSL_ERR_LEN];
|
||||
const char *ssl_error_reason;
|
||||
MARIADB_PVIO *pvio= mysql->net.pvio;
|
||||
|
||||
if (!ssl_errno)
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
|
||||
return;
|
||||
}
|
||||
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
0, ssl_error_reason);
|
||||
return;
|
||||
}
|
||||
snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno);
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, ssl_error);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
/*
|
||||
thread safe callbacks for OpenSSL
|
||||
Crypto call back functions will be
|
||||
set during ssl_initialization
|
||||
*/
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10000000L
|
||||
static unsigned long my_cb_threadid(void)
|
||||
{
|
||||
/* cast pthread_t to unsigned long */
|
||||
return (unsigned long) pthread_self();
|
||||
}
|
||||
#else
|
||||
static void my_cb_threadid(CRYPTO_THREADID *id)
|
||||
{
|
||||
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
static void my_cb_locking(int mode, int n,
|
||||
const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused)))
|
||||
{
|
||||
if (mode & CRYPTO_LOCK)
|
||||
pthread_mutex_lock(&LOCK_crypto[n]);
|
||||
else
|
||||
pthread_mutex_unlock(&LOCK_crypto[n]);
|
||||
}
|
||||
|
||||
static int ssl_thread_init()
|
||||
{
|
||||
if (!CRYPTO_THREADID_get_callback()
|
||||
#ifndef OPENSSL_NO_DEPRECATED
|
||||
&& !CRYPTO_get_id_callback()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int i, max= CRYPTO_num_locks();
|
||||
|
||||
if (LOCK_crypto == NULL)
|
||||
{
|
||||
if (!(LOCK_crypto=
|
||||
(pthread_mutex_t *)ma_malloc(sizeof(pthread_mutex_t) * max, MYF(0))))
|
||||
return 1;
|
||||
|
||||
for (i=0; i < max; i++)
|
||||
pthread_mutex_init(&LOCK_crypto[i], NULL);
|
||||
}
|
||||
CRYPTO_set_locking_callback(my_cb_locking);
|
||||
CRYPTO_THREADID_set_callback(my_cb_threadid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || !defined(DISABLE_SIGPIPE)
|
||||
#define disable_sigpipe()
|
||||
#else
|
||||
#include <signal.h>
|
||||
static void ma_sigpipe_handler()
|
||||
{
|
||||
}
|
||||
|
||||
static void disable_sigpipe()
|
||||
{
|
||||
struct sigaction old_handler, new_handler={NULL};
|
||||
if (!sigaction (SIGPIPE, NULL, &old_handler) &&
|
||||
!old_handler.sa_handler)
|
||||
{
|
||||
new_handler.sa_handler= ma_sigpipe_handler;
|
||||
new_handler.sa_flags= 0;
|
||||
if (!sigemptyset(&new_handler.sa_mask))
|
||||
sigaction(SIGPIPE, &new_handler, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Initializes SSL
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_start
|
||||
mysql connection handle
|
||||
|
||||
RETURN VALUES
|
||||
0 success
|
||||
1 error
|
||||
*/
|
||||
int ma_tls_start(char *errmsg __attribute__((unused)), size_t errmsg_len __attribute__((unused)))
|
||||
{
|
||||
int rc= 1;
|
||||
char *p;
|
||||
if (ma_tls_initialized)
|
||||
return 0;
|
||||
|
||||
/* lock mutex to prevent multiple initialization */
|
||||
pthread_mutex_init(&LOCK_openssl_config, NULL);
|
||||
pthread_mutex_lock(&LOCK_openssl_config);
|
||||
#ifdef HAVE_OPENSSL_1_1_API
|
||||
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL))
|
||||
goto end;
|
||||
#else
|
||||
if (ssl_thread_init())
|
||||
{
|
||||
strncpy(errmsg, "Not enough memory", errmsg_len);
|
||||
goto end;
|
||||
}
|
||||
SSL_library_init();
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
|
||||
OPENSSL_config(NULL);
|
||||
#endif
|
||||
#endif
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
/* load errors */
|
||||
SSL_load_error_strings();
|
||||
/* digests and ciphers */
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif
|
||||
disable_sigpipe();
|
||||
#ifdef OPENSSL_USE_BIOMETHOD
|
||||
memcpy(&ma_BIO_method, BIO_s_socket(), sizeof(BIO_METHOD));
|
||||
ma_BIO_method.bread= ma_bio_read;
|
||||
ma_BIO_method.bwrite= ma_bio_write;
|
||||
#endif
|
||||
snprintf(tls_library_version, TLS_VERSION_LENGTH - 1, "%s",
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) || !defined(HAVE_OPENSSL_1_1_API)
|
||||
SSLeay_version(SSLEAY_VERSION));
|
||||
#else
|
||||
OpenSSL_version(OPENSSL_VERSION));
|
||||
#endif
|
||||
/* remove date from version */
|
||||
if ((p= strstr(tls_library_version, " ")))
|
||||
*p= 0;
|
||||
rc= 0;
|
||||
ma_tls_initialized= TRUE;
|
||||
end:
|
||||
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
Release SSL and free resources
|
||||
Will be automatically executed by
|
||||
mysql_server_end() function
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_end()
|
||||
void
|
||||
|
||||
RETURN VALUES
|
||||
void
|
||||
*/
|
||||
void ma_tls_end()
|
||||
{
|
||||
if (ma_tls_initialized)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_openssl_config);
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
if (LOCK_crypto)
|
||||
{
|
||||
int i;
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
CRYPTO_THREADID_set_callback(NULL);
|
||||
|
||||
for (i=0; i < CRYPTO_num_locks(); i++)
|
||||
pthread_mutex_destroy(&LOCK_crypto[i]);
|
||||
ma_free((gptr)LOCK_crypto);
|
||||
LOCK_crypto= NULL;
|
||||
}
|
||||
#endif
|
||||
if (mariadb_deinitialize_ssl)
|
||||
{
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
ERR_remove_thread_state(NULL);
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
ERR_free_strings();
|
||||
CONF_modules_free();
|
||||
CONF_modules_unload(1);
|
||||
#endif
|
||||
}
|
||||
ma_tls_initialized= FALSE;
|
||||
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||
pthread_mutex_destroy(&LOCK_openssl_config);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int ma_tls_get_password(char *buf, int size,
|
||||
int rwflag __attribute__((unused)),
|
||||
void *userdata)
|
||||
{
|
||||
memset(buf, 0, size);
|
||||
if (userdata)
|
||||
strncpy(buf, (char *)userdata, size);
|
||||
return (int)strlen(buf);
|
||||
}
|
||||
|
||||
|
||||
static int ma_tls_set_certs(MYSQL *mysql, SSL_CTX *ctx)
|
||||
{
|
||||
char *certfile= mysql->options.ssl_cert,
|
||||
*keyfile= mysql->options.ssl_key;
|
||||
char *pw= (mysql->options.extension) ?
|
||||
mysql->options.extension->tls_pw : NULL;
|
||||
|
||||
/* add cipher */
|
||||
if ((mysql->options.ssl_cipher &&
|
||||
mysql->options.ssl_cipher[0] != 0))
|
||||
{
|
||||
if(
|
||||
#ifdef TLS1_3_VERSION
|
||||
SSL_CTX_set_ciphersuites(ctx, mysql->options.ssl_cipher) == 0 &&
|
||||
#endif
|
||||
SSL_CTX_set_cipher_list(ctx, mysql->options.ssl_cipher) == 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* ca_file and ca_path */
|
||||
if (!SSL_CTX_load_verify_locations(ctx,
|
||||
mysql->options.ssl_ca,
|
||||
mysql->options.ssl_capath))
|
||||
{
|
||||
if (mysql->options.ssl_ca || mysql->options.ssl_capath)
|
||||
goto error;
|
||||
if (SSL_CTX_set_default_verify_paths(ctx) == 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mysql->options.extension &&
|
||||
(mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath))
|
||||
{
|
||||
X509_STORE *certstore;
|
||||
|
||||
if ((certstore= SSL_CTX_get_cert_store(ctx)))
|
||||
{
|
||||
if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl,
|
||||
mysql->options.extension->ssl_crlpath) == 0)
|
||||
goto error;
|
||||
|
||||
if (X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) == 0)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyfile && !certfile)
|
||||
certfile= keyfile;
|
||||
if (certfile && !keyfile)
|
||||
keyfile= certfile;
|
||||
|
||||
/* set cert */
|
||||
if (certfile && certfile[0] != 0)
|
||||
{
|
||||
if (SSL_CTX_use_certificate_chain_file(ctx, certfile) != 1)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyfile && keyfile[0])
|
||||
{
|
||||
FILE *fp;
|
||||
if ((fp= fopen(keyfile, "rb")))
|
||||
{
|
||||
EVP_PKEY *key= EVP_PKEY_new();
|
||||
PEM_read_PrivateKey(fp, &key, NULL, pw);
|
||||
fclose(fp);
|
||||
if (SSL_CTX_use_PrivateKey(ctx, key) != 1)
|
||||
{
|
||||
unsigned long err= ERR_peek_error();
|
||||
EVP_PKEY_free(key);
|
||||
if (!(ERR_GET_LIB(err) == ERR_LIB_X509 &&
|
||||
ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE))
|
||||
goto error;
|
||||
}
|
||||
EVP_PKEY_free(key);
|
||||
} else {
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
CER(CR_FILE_NOT_FOUND), keyfile);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* verify key */
|
||||
if (certfile && SSL_CTX_check_private_key(ctx) != 1)
|
||||
goto error;
|
||||
|
||||
SSL_CTX_set_verify(ctx, (mysql->options.ssl_ca || mysql->options.ssl_capath) ?
|
||||
SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
ma_tls_set_error(mysql);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *ma_tls_init(MYSQL *mysql)
|
||||
{
|
||||
SSL *ssl= NULL;
|
||||
SSL_CTX *ctx= NULL;
|
||||
long default_options= SSL_OP_ALL |
|
||||
SSL_OP_NO_SSLv2 |
|
||||
SSL_OP_NO_SSLv3;
|
||||
long options= 0;
|
||||
pthread_mutex_lock(&LOCK_openssl_config);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if (!(ctx= SSL_CTX_new(TLS_client_method())))
|
||||
#else
|
||||
if (!(ctx= SSL_CTX_new(SSLv23_client_method())))
|
||||
#endif
|
||||
goto error;
|
||||
if (mysql->options.extension)
|
||||
options= ma_tls_version_options(mysql->options.extension->tls_version);
|
||||
SSL_CTX_set_options(ctx, options ? options : default_options);
|
||||
|
||||
if (ma_tls_set_certs(mysql, ctx))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(ssl= SSL_new(ctx)))
|
||||
goto error;
|
||||
|
||||
if (!SSL_set_app_data(ssl, mysql))
|
||||
goto error;
|
||||
|
||||
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||
return (void *)ssl;
|
||||
error:
|
||||
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||
if (ctx)
|
||||
SSL_CTX_free(ctx);
|
||||
if (ssl)
|
||||
SSL_free(ssl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
{
|
||||
SSL *ssl = (SSL *)ctls->ssl;
|
||||
my_bool blocking, try_connect= 1;
|
||||
MYSQL *mysql;
|
||||
MARIADB_PVIO *pvio;
|
||||
int rc;
|
||||
#ifdef OPENSSL_USE_BIOMETHOD
|
||||
BIO_METHOD *bio_method= NULL;
|
||||
BIO *bio;
|
||||
#endif
|
||||
|
||||
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||
pvio= mysql->net.pvio;
|
||||
|
||||
/* Set socket to non blocking if not already set */
|
||||
if (!(blocking= pvio->methods->is_blocking(pvio)))
|
||||
pvio->methods->blocking(pvio, FALSE, 0);
|
||||
|
||||
SSL_clear(ssl);
|
||||
|
||||
#ifdef OPENSSL_USE_BIOMETHOD
|
||||
bio= BIO_new(&ma_BIO_method);
|
||||
bio->ptr= pvio;
|
||||
SSL_set_bio(ssl, bio, bio);
|
||||
BIO_set_fd(bio, mysql_get_socket(mysql), BIO_NOCLOSE);
|
||||
#else
|
||||
SSL_set_fd(ssl, (int)mysql_get_socket(mysql));
|
||||
#endif
|
||||
|
||||
while (try_connect && (rc= SSL_connect(ssl)) == -1)
|
||||
{
|
||||
switch((SSL_get_error(ssl, rc))) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
|
||||
try_connect= 0;
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
|
||||
try_connect= 0;
|
||||
break;
|
||||
default:
|
||||
try_connect= 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* In case handshake failed or if a root certificate (ca) was specified,
|
||||
we need to check the result code of X509 verification. A detailed check
|
||||
of the peer certificate (hostname checking will follow later) */
|
||||
if (rc != 1 ||
|
||||
(mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) ||
|
||||
(mysql->options.ssl_ca || mysql->options.ssl_capath))
|
||||
{
|
||||
long x509_err= SSL_get_verify_result(ssl);
|
||||
if (x509_err != X509_V_OK)
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(x509_err));
|
||||
/* restore blocking mode */
|
||||
if (!blocking)
|
||||
pvio->methods->blocking(pvio, FALSE, 0);
|
||||
|
||||
return 1;
|
||||
} else if (rc != 1) {
|
||||
ma_tls_set_error(mysql);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
pvio->ctls->ssl= ctls->ssl= (void *)ssl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static my_bool
|
||||
ma_tls_async_check_result(int res, struct mysql_async_context *b, SSL *ssl)
|
||||
{
|
||||
int ssl_err;
|
||||
b->events_to_wait_for= 0;
|
||||
if (res >= 0)
|
||||
return 1;
|
||||
ssl_err= SSL_get_error(ssl, res);
|
||||
if (ssl_err == SSL_ERROR_WANT_READ)
|
||||
b->events_to_wait_for|= MYSQL_WAIT_READ;
|
||||
else if (ssl_err == SSL_ERROR_WANT_WRITE)
|
||||
b->events_to_wait_for|= MYSQL_WAIT_WRITE;
|
||||
else
|
||||
return 1;
|
||||
if (b->suspend_resume_hook)
|
||||
(*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
|
||||
my_context_yield(&b->async_context);
|
||||
if (b->suspend_resume_hook)
|
||||
(*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t ma_tls_read_async(MARIADB_PVIO *pvio,
|
||||
const unsigned char *buffer,
|
||||
size_t length)
|
||||
{
|
||||
int res;
|
||||
struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
|
||||
MARIADB_TLS *ctls= pvio->ctls;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
res= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length);
|
||||
if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl))
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t ma_tls_write_async(MARIADB_PVIO *pvio,
|
||||
const unsigned char *buffer,
|
||||
size_t length)
|
||||
{
|
||||
int res;
|
||||
struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
|
||||
MARIADB_TLS *ctls= pvio->ctls;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
res= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length);
|
||||
if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl))
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
||||
{
|
||||
int rc;
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
|
||||
while ((rc= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length)) < 0)
|
||||
{
|
||||
int error= SSL_get_error((SSL *)ctls->ssl, rc);
|
||||
if (error != SSL_ERROR_WANT_READ)
|
||||
return rc;
|
||||
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.read_timeout) < 1)
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
||||
{
|
||||
int rc;
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
|
||||
while ((rc= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length)) <= 0)
|
||||
{
|
||||
int error= SSL_get_error((SSL *)ctls->ssl, rc);
|
||||
if (error != SSL_ERROR_WANT_WRITE)
|
||||
return rc;
|
||||
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.write_timeout) < 1)
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
my_bool ma_tls_close(MARIADB_TLS *ctls)
|
||||
{
|
||||
int i, rc;
|
||||
SSL *ssl;
|
||||
SSL_CTX *ctx= NULL;
|
||||
|
||||
if (!ctls || !ctls->ssl)
|
||||
return 1;
|
||||
ssl= (SSL *)ctls->ssl;
|
||||
ctx= SSL_get_SSL_CTX(ssl);
|
||||
if (ctx)
|
||||
SSL_CTX_free(ctx);
|
||||
|
||||
SSL_set_quiet_shutdown(ssl, 1);
|
||||
/* 2 x pending + 2 * data = 4 */
|
||||
for (i=0; i < 4; i++)
|
||||
if ((rc= SSL_shutdown(ssl)))
|
||||
break;
|
||||
|
||||
/* Since we transferred ownership of BIO to ssl, BIO will
|
||||
automatically freed - no need for an explicit BIO_free_all */
|
||||
|
||||
SSL_free(ssl);
|
||||
ctls->ssl= NULL;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ma_tls_verify_server_cert(MARIADB_TLS *ctls)
|
||||
{
|
||||
X509 *cert;
|
||||
MYSQL *mysql;
|
||||
SSL *ssl;
|
||||
MARIADB_PVIO *pvio;
|
||||
#if !defined(HAVE_OPENSSL_CHECK_HOST)
|
||||
X509_NAME *x509sn;
|
||||
int cn_pos;
|
||||
X509_NAME_ENTRY *cn_entry;
|
||||
ASN1_STRING *cn_asn1;
|
||||
const char *cn_str;
|
||||
#endif
|
||||
if (!ctls || !ctls->ssl)
|
||||
return 1;
|
||||
ssl= (SSL *)ctls->ssl;
|
||||
|
||||
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||
pvio= mysql->net.pvio;
|
||||
|
||||
if (!mysql->host)
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR), "Invalid (empty) hostname");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(cert= SSL_get_peer_certificate(ssl)))
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate");
|
||||
return 1;
|
||||
}
|
||||
#ifdef HAVE_OPENSSL_CHECK_HOST
|
||||
if (X509_check_host(cert, mysql->host, 0, 0, 0) != 1
|
||||
&& X509_check_ip_asc(cert, mysql->host, 0) != 1)
|
||||
goto error;
|
||||
#else
|
||||
x509sn= X509_get_subject_name(cert);
|
||||
|
||||
if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0)
|
||||
goto error;
|
||||
|
||||
if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos)))
|
||||
goto error;
|
||||
|
||||
if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry)))
|
||||
goto error;
|
||||
|
||||
cn_str = (char *)ASN1_STRING_data(cn_asn1);
|
||||
|
||||
/* Make sure there is no embedded \0 in the CN */
|
||||
if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str))
|
||||
goto error;
|
||||
|
||||
if (strcmp(cn_str, mysql->host))
|
||||
goto error;
|
||||
#endif
|
||||
X509_free(cert);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
X509_free(cert);
|
||||
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR), "Validation of SSL server certificate failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
|
||||
{
|
||||
if (!ctls || !ctls->ssl)
|
||||
return NULL;
|
||||
return SSL_get_cipher_name(ctls->ssl);
|
||||
}
|
||||
|
||||
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
|
||||
{
|
||||
X509 *cert= NULL;
|
||||
MYSQL *mysql;
|
||||
unsigned int fp_len;
|
||||
|
||||
if (!ctls || !ctls->ssl)
|
||||
return 0;
|
||||
|
||||
mysql= SSL_get_app_data(ctls->ssl);
|
||||
|
||||
if (!(cert= SSL_get_peer_certificate(ctls->ssl)))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Unable to get server certificate");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (len < EVP_MAX_MD_SIZE)
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Finger print buffer too small");
|
||||
goto end;
|
||||
}
|
||||
if (!X509_digest(cert, EVP_sha1(), (unsigned char *)fp, &fp_len))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"invalid finger print of server certificate");
|
||||
goto end;
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
return (fp_len);
|
||||
end:
|
||||
X509_free(cert);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
|
||||
{
|
||||
if (!ctls || !ctls->ssl)
|
||||
return -1;
|
||||
|
||||
return SSL_version(ctls->ssl) & 0xFF;
|
||||
}
|
||||
|
88
vendor/MDBC/libmariadb/secure/openssl_crypt.c
vendored
Normal file
88
vendor/MDBC/libmariadb/secure/openssl_crypt.c
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright (C) 2018 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
|
||||
*/
|
||||
#include <ma_global.h>
|
||||
#include <ma_crypt.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
static const EVP_MD *ma_hash_get_algorithm(unsigned int alg)
|
||||
{
|
||||
switch(alg)
|
||||
{
|
||||
case MA_HASH_MD5:
|
||||
return EVP_md5();
|
||||
case MA_HASH_SHA1:
|
||||
return EVP_sha1();
|
||||
case MA_HASH_SHA224:
|
||||
return EVP_sha224();
|
||||
case MA_HASH_SHA256:
|
||||
return EVP_sha256();
|
||||
case MA_HASH_SHA384:
|
||||
return EVP_sha384();
|
||||
case MA_HASH_SHA512:
|
||||
return EVP_sha512();
|
||||
case MA_HASH_RIPEMD160:
|
||||
return EVP_ripemd160();
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MA_HASH_CTX *ma_hash_new(unsigned int algorithm, MA_HASH_CTX *unused __attribute__((unused)))
|
||||
{
|
||||
EVP_MD_CTX *ctx= NULL;
|
||||
const EVP_MD *evp_md= ma_hash_get_algorithm(algorithm);
|
||||
|
||||
/* unknown or unsupported hash algorithm */
|
||||
if (!evp_md)
|
||||
return NULL;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if (!(ctx= EVP_MD_CTX_new()))
|
||||
#else
|
||||
if (!(ctx= EVP_MD_CTX_create()))
|
||||
#endif
|
||||
return NULL;
|
||||
if (!EVP_DigestInit(ctx, evp_md))
|
||||
{
|
||||
ma_hash_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void ma_hash_free(MA_HASH_CTX *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
EVP_MD_CTX_free(ctx);
|
||||
#else
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ma_hash_input(MA_HASH_CTX *ctx,
|
||||
const unsigned char *buffer,
|
||||
size_t len)
|
||||
{
|
||||
EVP_DigestUpdate(ctx, buffer, len);
|
||||
}
|
||||
|
||||
void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
|
||||
{
|
||||
EVP_DigestFinal_ex(ctx, digest, NULL);
|
||||
}
|
562
vendor/MDBC/libmariadb/secure/schannel.c
vendored
Normal file
562
vendor/MDBC/libmariadb/secure/schannel.c
vendored
Normal file
@@ -0,0 +1,562 @@
|
||||
/************************************************************************************
|
||||
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
|
||||
|
||||
*************************************************************************************/
|
||||
#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 1
|
||||
#define PROT_TLS1_0 2
|
||||
#define PROT_TLS1_2 4
|
||||
#define PROT_TLS1_3 8
|
||||
|
||||
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,const CERT_CONTEXT **cert_ctx)
|
||||
{
|
||||
MYSQL *mysql= ctls->pvio->mysql;
|
||||
char *certfile= mysql->options.ssl_cert,
|
||||
*keyfile= mysql->options.ssl_key;
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
char errmsg[256];
|
||||
|
||||
if (!certfile && keyfile)
|
||||
certfile= keyfile;
|
||||
if (!keyfile && certfile)
|
||||
keyfile= certfile;
|
||||
|
||||
if (!certfile)
|
||||
return 0;
|
||||
|
||||
*cert_ctx = schannel_create_cert_context(certfile, keyfile, errmsg, sizeof(errmsg));
|
||||
if (!*cert_ctx)
|
||||
{
|
||||
pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: %s", 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.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;
|
||||
}
|
||||
|
||||
my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
{
|
||||
MYSQL *mysql;
|
||||
SCHANNEL_CRED Cred = {0};
|
||||
MARIADB_PVIO *pvio;
|
||||
my_bool rc= 1;
|
||||
SC_CTX *sctx;
|
||||
SECURITY_STATUS sRet;
|
||||
ALG_ID AlgId[MAX_ALG_ID];
|
||||
size_t i;
|
||||
DWORD protocol = 0;
|
||||
int verify_certs;
|
||||
const CERT_CONTEXT* cert_context = NULL;
|
||||
|
||||
if (!ctls)
|
||||
return 1;
|
||||
|
||||
pvio= ctls->pvio;
|
||||
sctx= (SC_CTX *)ctls->ssl;
|
||||
if (!pvio || !sctx)
|
||||
return 1;
|
||||
|
||||
mysql= pvio->mysql;
|
||||
if (!mysql)
|
||||
return 1;
|
||||
|
||||
/* Set cipher */
|
||||
if (mysql->options.ssl_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
|
||||
*/
|
||||
for (i = 0; i < sizeof(tls_version) / sizeof(tls_version[0]); i++)
|
||||
{
|
||||
if (!_stricmp(mysql->options.ssl_cipher, tls_version[i].tls_version))
|
||||
protocol |= tls_version[i].protocol;
|
||||
}
|
||||
memset(AlgId, 0, sizeof(AlgId));
|
||||
Cred.cSupportedAlgs = (DWORD)set_cipher(mysql->options.ssl_cipher, protocol, AlgId, MAX_ALG_ID);
|
||||
if (Cred.cSupportedAlgs)
|
||||
{
|
||||
Cred.palgSupportedAlgs = AlgId;
|
||||
}
|
||||
else if (!protocol)
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, SEC_E_ALGORITHM_MISMATCH);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
Cred.dwVersion= SCHANNEL_CRED_VERSION;
|
||||
|
||||
Cred.dwFlags = SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
|
||||
|
||||
if (mysql->options.extension && mysql->options.extension->tls_version)
|
||||
{
|
||||
if (strstr(mysql->options.extension->tls_version, "TLSv1.0"))
|
||||
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_0_CLIENT;
|
||||
if (strstr(mysql->options.extension->tls_version, "TLSv1.1"))
|
||||
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_1_CLIENT;
|
||||
if (strstr(mysql->options.extension->tls_version, "TLSv1.2"))
|
||||
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_2_CLIENT;
|
||||
}
|
||||
if (!Cred.grbitEnabledProtocols)
|
||||
Cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
|
||||
|
||||
|
||||
if (ma_tls_set_client_certs(ctls, &cert_context))
|
||||
goto end;
|
||||
|
||||
if (cert_context)
|
||||
{
|
||||
Cred.cCreds = 1;
|
||||
Cred.paCred = &cert_context;
|
||||
}
|
||||
sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
|
||||
NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL);
|
||||
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->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT);
|
||||
|
||||
if (verify_certs)
|
||||
{
|
||||
if (!ma_schannel_verify_certs(ctls, (mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)))
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
end:
|
||||
if (cert_context)
|
||||
schannel_free_cert_context(cert_context);
|
||||
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 = ma_schannel_read_decrypt(pvio, &sctx->hCtxt, &dlength, (uchar *)buffer, (DWORD)length);
|
||||
if (status == SEC_I_CONTEXT_EXPIRED)
|
||||
return 0; /* other side shut down the connection. */
|
||||
if (status == SEC_I_RENEGOTIATE)
|
||||
return -1; /* Do not handle renegotiate yet */
|
||||
|
||||
return (status == SEC_E_OK)? (ssize_t)dlength : -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;
|
||||
}
|
854
vendor/MDBC/libmariadb/secure/schannel_certs.c
vendored
Normal file
854
vendor/MDBC/libmariadb/secure/schannel_certs.c
vendored
Normal file
@@ -0,0 +1,854 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2019 MariaDB
|
||||
|
||||
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
|
||||
|
||||
*************************************************************************************/
|
||||
|
||||
/*
|
||||
This module contain X509 certificate handling on Windows.
|
||||
PEM parsing, loading client certificate and key, server certificate validation
|
||||
*/
|
||||
|
||||
/*
|
||||
CERT_CHAIN_ENGINE_CONFIG has additional members in Windows 8.1
|
||||
To allow client to be work on pre-8.1 Windows, compile
|
||||
with corresponding _WIN32_WINNT
|
||||
*/
|
||||
#ifdef _WIN32_WINNT
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#endif
|
||||
|
||||
#include <winsock2.h>
|
||||
#include "schannel_certs.h"
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <winhttp.h>
|
||||
#include <assert.h>
|
||||
#include "win32_errmsg.h"
|
||||
|
||||
/*
|
||||
Return GetLastError(), or, if this unexpectedly gives success,
|
||||
return ERROR_INTERNAL_ERROR.
|
||||
|
||||
Background - in several cases in this module we return GetLastError()
|
||||
after an Windows function fails. However, we do not want the function to
|
||||
return success, even if GetLastError() was suddenly 0.
|
||||
*/
|
||||
static DWORD get_last_error()
|
||||
{
|
||||
DWORD ret = GetLastError();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
// We generally expect last error to be set API fails.
|
||||
// thus the debug assertion-
|
||||
assert(0);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
#define FAIL(...) \
|
||||
do{\
|
||||
status = get_last_error();\
|
||||
ma_format_win32_error(errmsg, errmsg_len, status, __VA_ARGS__);\
|
||||
goto cleanup;\
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
Load file into memory. Add null terminator at the end, so it will be a valid C string.
|
||||
*/
|
||||
static char* pem_file_to_string(const char* file, char* errmsg, size_t errmsg_len)
|
||||
{
|
||||
LARGE_INTEGER file_size;
|
||||
size_t file_bufsize = 0;
|
||||
size_t total_bytes_read = 0;
|
||||
char* file_buffer = NULL;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
HANDLE file_handle = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FAIL("failed to open file '%s'", file);
|
||||
}
|
||||
|
||||
if (!GetFileSizeEx(file_handle, &file_size))
|
||||
{
|
||||
FAIL("GetFileSizeEx failed on '%s'", file);
|
||||
}
|
||||
|
||||
if (file_size.QuadPart > ULONG_MAX - 1)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("file '%s' too large", file);
|
||||
}
|
||||
|
||||
file_bufsize = (size_t)file_size.QuadPart;
|
||||
file_buffer = (char*)LocalAlloc(0,file_bufsize + 1);
|
||||
if (!file_buffer)
|
||||
{
|
||||
FAIL("LocalAlloc(0,%zu) failed", file_bufsize + 1);
|
||||
}
|
||||
|
||||
while (total_bytes_read < file_bufsize)
|
||||
{
|
||||
DWORD bytes_to_read = (DWORD)(file_bufsize - total_bytes_read);
|
||||
DWORD bytes_read = 0;
|
||||
|
||||
if (!ReadFile(file_handle, file_buffer + total_bytes_read,
|
||||
bytes_to_read, &bytes_read, NULL))
|
||||
{
|
||||
FAIL("ReadFile() failed to read file '%s'", file);
|
||||
}
|
||||
if (bytes_read == 0)
|
||||
{
|
||||
/* Premature EOF -- adjust the bufsize to the new value */
|
||||
file_bufsize = total_bytes_read;
|
||||
}
|
||||
else
|
||||
{
|
||||
total_bytes_read += bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
/* Null terminate the buffer */
|
||||
file_buffer[file_bufsize] = '\0';
|
||||
|
||||
cleanup:
|
||||
if (file_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(file_handle);
|
||||
}
|
||||
if (status)
|
||||
{
|
||||
/* Some error happened. */
|
||||
LocalFree(file_buffer);
|
||||
file_buffer = NULL;
|
||||
}
|
||||
return file_buffer;
|
||||
}
|
||||
|
||||
|
||||
// Structure for parsing BEGIN/END sections inside pem.
|
||||
typedef struct _pem_type_desc
|
||||
{
|
||||
const char* begin_tag;
|
||||
size_t begin_tag_len;
|
||||
const char* end_tag;
|
||||
size_t end_tag_len;
|
||||
} pem_type_desc;
|
||||
|
||||
#define BEGIN_TAG(x) "-----BEGIN " x "-----"
|
||||
#define END_TAG(x) "\n-----END " x "-----"
|
||||
#define PEM_SECTION(tag) {BEGIN_TAG(tag), sizeof(BEGIN_TAG(tag))-1, END_TAG(tag), sizeof(END_TAG(tag))-1}
|
||||
|
||||
typedef enum {
|
||||
PEM_TYPE_CERTIFICATE = 0,
|
||||
PEM_TYPE_X509_CRL,
|
||||
PEM_TYPE_RSA_PRIVATE_KEY,
|
||||
PEM_TYPE_PRIVATE_KEY
|
||||
} PEM_TYPE;
|
||||
|
||||
static const pem_type_desc pem_sections[] = {
|
||||
PEM_SECTION("CERTIFICATE"),
|
||||
PEM_SECTION("X509 CRL"),
|
||||
PEM_SECTION("RSA PRIVATE KEY"),
|
||||
PEM_SECTION("PRIVATE KEY")
|
||||
};
|
||||
|
||||
/*
|
||||
Locate a substring in pem for given type,
|
||||
e.g section between BEGIN CERTIFICATE and END CERTIFICATE
|
||||
in PEMs base64 format, with header and footer.
|
||||
|
||||
output parameters 'begin' and 'end' are set upon return.
|
||||
it is possible that functions returns 'begin' != NULL but
|
||||
'end' = NULL. This is generally a format error, meaning that
|
||||
the end tag was not found
|
||||
*/
|
||||
void pem_locate(char* pem_str,
|
||||
PEM_TYPE type,
|
||||
char** begin,
|
||||
char** end)
|
||||
{
|
||||
*begin = NULL;
|
||||
*end = NULL;
|
||||
char c;
|
||||
|
||||
const pem_type_desc* desc = &pem_sections[type];
|
||||
*begin = strstr(pem_str, desc->begin_tag);
|
||||
if (!(*begin))
|
||||
return;
|
||||
|
||||
// We expect newline after the
|
||||
// begin tag, LF or CRLF
|
||||
c = (*begin)[desc->begin_tag_len];
|
||||
|
||||
if (c != '\r' && c != '\n')
|
||||
{
|
||||
*begin = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*end = strstr(*begin + desc->begin_tag_len + 1, desc->end_tag);
|
||||
if (!*end)
|
||||
return; // error, end marker not found
|
||||
|
||||
(*end) += desc->end_tag_len;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add certificates, or CRLs from a PEM file to Wincrypt store
|
||||
*/
|
||||
static SECURITY_STATUS add_certs_to_store(
|
||||
HCERTSTORE trust_store,
|
||||
const char* file,
|
||||
PEM_TYPE type,
|
||||
char* errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
char* file_buffer = NULL;
|
||||
char* cur = NULL;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
CRL_CONTEXT* crl_context = NULL;
|
||||
CERT_CONTEXT* cert_context = NULL;
|
||||
char* begin;
|
||||
char* end;
|
||||
|
||||
file_buffer = pem_file_to_string(file, errmsg, errmsg_len);
|
||||
if (!file_buffer)
|
||||
goto cleanup;
|
||||
|
||||
for (cur = file_buffer; ; cur = end)
|
||||
{
|
||||
pem_locate(cur, type, &begin, &end);
|
||||
|
||||
if (!begin)
|
||||
break;
|
||||
|
||||
if (!end)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("Invalid PEM file '%s', missing end marker corresponding to begin marker '%s' at offset %zu",
|
||||
file, pem_sections[type].begin_tag, (size_t)(begin - file_buffer));
|
||||
}
|
||||
CERT_BLOB cert_blob;
|
||||
void* context = NULL;
|
||||
DWORD actual_content_type = 0;
|
||||
|
||||
cert_blob.pbData = (BYTE*)begin;
|
||||
cert_blob.cbData = (DWORD)(end - begin);
|
||||
if (!CryptQueryObject(
|
||||
CERT_QUERY_OBJECT_BLOB, &cert_blob,
|
||||
CERT_QUERY_CONTENT_FLAG_CERT | CERT_QUERY_CONTENT_FLAG_CRL,
|
||||
CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &actual_content_type,
|
||||
NULL, NULL, NULL, (const void**)&context))
|
||||
{
|
||||
FAIL("failed to extract certificate from PEM file '%s'",file);
|
||||
}
|
||||
|
||||
if (!context)
|
||||
{
|
||||
SetLastError(SEC_E_INTERNAL_ERROR);
|
||||
FAIL("unexpected result from CryptQueryObject(),cert_context is NULL"
|
||||
" after successful completion, file '%s'",
|
||||
file);
|
||||
}
|
||||
|
||||
if (actual_content_type == CERT_QUERY_CONTENT_CERT)
|
||||
{
|
||||
CERT_CONTEXT* cert_context = (CERT_CONTEXT*)context;
|
||||
if (!CertAddCertificateContextToStore(
|
||||
trust_store, cert_context,
|
||||
CERT_STORE_ADD_ALWAYS, NULL))
|
||||
{
|
||||
FAIL("CertAddCertificateContextToStore failed");
|
||||
}
|
||||
}
|
||||
else if (actual_content_type == CERT_QUERY_CONTENT_CRL)
|
||||
{
|
||||
CRL_CONTEXT* crl_context = (CRL_CONTEXT*)context;
|
||||
if (!CertAddCRLContextToStore(
|
||||
trust_store, crl_context,
|
||||
CERT_STORE_ADD_ALWAYS, NULL))
|
||||
{
|
||||
FAIL("CertAddCRLContextToStore() failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
LocalFree(file_buffer);
|
||||
if (cert_context)
|
||||
CertFreeCertificateContext(cert_context);
|
||||
if (crl_context)
|
||||
CertFreeCRLContext(crl_context);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
Add a directory to store, i.e try to load all files.
|
||||
(extract certificates and add them to store)
|
||||
|
||||
@return 0 on success, error only if directory is invalid.
|
||||
*/
|
||||
SECURITY_STATUS add_dir_to_store(HCERTSTORE trust_store, const char* dir,
|
||||
PEM_TYPE type, char* errmsg, size_t errmsg_len)
|
||||
{
|
||||
WIN32_FIND_DATAA ffd;
|
||||
char path[MAX_PATH];
|
||||
char pattern[MAX_PATH];
|
||||
DWORD dwAttr;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
if ((dwAttr = GetFileAttributes(dir)) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("directory '%s' does not exist", dir);
|
||||
}
|
||||
if (!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("'%s' is not a directory", dir);
|
||||
}
|
||||
sprintf_s(pattern, sizeof(pattern), "%s\\*", dir);
|
||||
hFind = FindFirstFile(pattern, &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FAIL("FindFirstFile(%s) failed",pattern);
|
||||
}
|
||||
do
|
||||
{
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
continue;
|
||||
sprintf_s(path, sizeof(path), "%s\\%s", dir, ffd.cFileName);
|
||||
|
||||
// ignore error from add_certs_to_store(), not all file
|
||||
// maybe PEM.
|
||||
add_certs_to_store(trust_store, path, type, errmsg,
|
||||
errmsg_len);
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
|
||||
cleanup:
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
FindClose(hFind);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Count certificates in store. */
|
||||
static int count_certificates(HCERTSTORE store)
|
||||
{
|
||||
int num_certs = 0;
|
||||
PCCERT_CONTEXT c = NULL;
|
||||
|
||||
while ((c = CertEnumCertificatesInStore(store, c)))
|
||||
num_certs++;
|
||||
|
||||
return num_certs;
|
||||
}
|
||||
|
||||
/**
|
||||
Creates certificate store with user defined CA chain and/or CRL.
|
||||
Loads PEM certificate from files or directories.
|
||||
|
||||
If only CRLFile/CRLPath is defined, the "system" store is duplicated,
|
||||
and new CRLs are added to it.
|
||||
|
||||
If CAFile/CAPAth is defined, then new empty store is created, and CAs
|
||||
(and CRLs, if defined), are added to it.
|
||||
|
||||
The function throws an error, if none of the files in CAFile/CAPath have a valid certificate.
|
||||
It is also an error if CRLFile does not exist.
|
||||
*/
|
||||
SECURITY_STATUS schannel_create_store(
|
||||
const char* CAFile,
|
||||
const char* CAPath,
|
||||
const char* CRLFile,
|
||||
const char* CRLPath,
|
||||
HCERTSTORE* out_store,
|
||||
char* errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
|
||||
HCERTSTORE store = NULL;
|
||||
HCERTSTORE system_store = NULL;
|
||||
int status = SEC_E_OK;
|
||||
|
||||
*out_store = NULL;
|
||||
if (!CAFile && !CAPath && !CRLFile && !CRLPath)
|
||||
{
|
||||
/* Nothing to do, caller will use default store*/
|
||||
*out_store = NULL;
|
||||
return SEC_E_OK;
|
||||
}
|
||||
if (CAFile || CAPath)
|
||||
{
|
||||
/* Open the certificate store */
|
||||
store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, (HCRYPTPROV)NULL,
|
||||
CERT_STORE_CREATE_NEW_FLAG, NULL);
|
||||
if (!store)
|
||||
{
|
||||
FAIL("CertOpenStore failed for memory store");
|
||||
}
|
||||
}
|
||||
else if (CRLFile || CRLPath)
|
||||
{
|
||||
/* Only CRL was provided, copy system store, add revocation list to
|
||||
* it. */
|
||||
system_store =
|
||||
CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY)NULL,
|
||||
CERT_SYSTEM_STORE_CURRENT_USER, L"MY");
|
||||
if (!system_store)
|
||||
{
|
||||
FAIL("CertOpenStore failed for system store");
|
||||
}
|
||||
|
||||
store = CertDuplicateStore(system_store);
|
||||
if (!store)
|
||||
{
|
||||
FAIL("CertDuplicateStore failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (CAFile)
|
||||
{
|
||||
status = add_certs_to_store(store, CAFile,
|
||||
PEM_TYPE_CERTIFICATE, errmsg, errmsg_len);
|
||||
if (status)
|
||||
goto cleanup;
|
||||
}
|
||||
if (CAPath)
|
||||
{
|
||||
status = add_dir_to_store(store, CAPath,
|
||||
PEM_TYPE_CERTIFICATE, errmsg, errmsg_len);
|
||||
if (status)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((CAFile || CAPath) && store && !count_certificates(store))
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("no valid certificates were found, CAFile='%s', CAPath='%s'",
|
||||
CAFile ? CAFile : "<not set>", CAPath ? CAPath : "<not set>");
|
||||
}
|
||||
|
||||
if (CRLFile)
|
||||
{
|
||||
status = add_certs_to_store(store, CRLFile, PEM_TYPE_X509_CRL,
|
||||
errmsg, errmsg_len);
|
||||
}
|
||||
if (CRLPath)
|
||||
{
|
||||
status = add_dir_to_store(store, CRLPath, PEM_TYPE_X509_CRL,
|
||||
errmsg, errmsg_len);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (system_store)
|
||||
CertCloseStore(system_store, 0);
|
||||
if (status && store)
|
||||
{
|
||||
CertCloseStore(store, 0);
|
||||
store = NULL;
|
||||
}
|
||||
*out_store = store;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
The main verification logic.
|
||||
Taken almost completely from Windows 2003 Platform SDK 2003
|
||||
(Samples\Security\SSPI\SSL\WebClient.c)
|
||||
|
||||
The only difference here is is usage of custom store
|
||||
and chain engine.
|
||||
*/
|
||||
static SECURITY_STATUS VerifyServerCertificate(
|
||||
PCCERT_CONTEXT pServerCert,
|
||||
HCERTSTORE hStore,
|
||||
LPWSTR pwszServerName,
|
||||
DWORD dwRevocationCheckFlags,
|
||||
DWORD dwVerifyFlags,
|
||||
LPSTR errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
SSL_EXTRA_CERT_CHAIN_POLICY_PARA polExtra;
|
||||
CERT_CHAIN_POLICY_PARA PolicyPara;
|
||||
CERT_CHAIN_POLICY_STATUS PolicyStatus;
|
||||
CERT_CHAIN_PARA ChainPara;
|
||||
HCERTCHAINENGINE hChainEngine = NULL;
|
||||
PCCERT_CHAIN_CONTEXT pChainContext = NULL;
|
||||
LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
|
||||
szOID_SERVER_GATED_CRYPTO,
|
||||
szOID_SGC_NETSCAPE };
|
||||
DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
if (pServerCert == NULL)
|
||||
{
|
||||
SetLastError(SEC_E_WRONG_PRINCIPAL);
|
||||
FAIL("Invalid parameter pServerCert passed to VerifyServerCertificate");
|
||||
}
|
||||
|
||||
ZeroMemory(&ChainPara, sizeof(ChainPara));
|
||||
ChainPara.cbSize = sizeof(ChainPara);
|
||||
ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
|
||||
ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
|
||||
ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
|
||||
|
||||
if (hStore)
|
||||
{
|
||||
CERT_CHAIN_ENGINE_CONFIG EngineConfig = { 0 };
|
||||
EngineConfig.cbSize = sizeof(EngineConfig);
|
||||
EngineConfig.hExclusiveRoot = hStore;
|
||||
if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine))
|
||||
{
|
||||
FAIL("CertCreateCertificateChainEngine failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (!CertGetCertificateChain(
|
||||
hChainEngine,
|
||||
pServerCert,
|
||||
NULL,
|
||||
pServerCert->hCertStore,
|
||||
&ChainPara,
|
||||
dwRevocationCheckFlags,
|
||||
NULL,
|
||||
&pChainContext))
|
||||
{
|
||||
FAIL("CertGetCertificateChain failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Validate certificate chain.
|
||||
ZeroMemory(&polExtra, sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
|
||||
polExtra.cbStruct = sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
|
||||
polExtra.dwAuthType = AUTHTYPE_SERVER;
|
||||
polExtra.fdwChecks = dwVerifyFlags;
|
||||
polExtra.pwszServerName = pwszServerName;
|
||||
|
||||
memset(&PolicyPara, 0, sizeof(PolicyPara));
|
||||
PolicyPara.cbSize = sizeof(PolicyPara);
|
||||
PolicyPara.pvExtraPolicyPara = &polExtra;
|
||||
|
||||
memset(&PolicyStatus, 0, sizeof(PolicyStatus));
|
||||
PolicyStatus.cbSize = sizeof(PolicyStatus);
|
||||
|
||||
if (!CertVerifyCertificateChainPolicy(
|
||||
CERT_CHAIN_POLICY_SSL,
|
||||
pChainContext,
|
||||
&PolicyPara,
|
||||
&PolicyStatus))
|
||||
{
|
||||
FAIL("CertVerifyCertificateChainPolicy failed");
|
||||
}
|
||||
|
||||
if (PolicyStatus.dwError)
|
||||
{
|
||||
SetLastError(PolicyStatus.dwError);
|
||||
FAIL("Server certificate validation failed");
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (hChainEngine)
|
||||
{
|
||||
CertFreeCertificateChainEngine(hChainEngine);
|
||||
}
|
||||
if (pChainContext)
|
||||
{
|
||||
CertFreeCertificateChain(pChainContext);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void schannel_free_store(HCERTSTORE store)
|
||||
{
|
||||
if (store)
|
||||
CertCloseStore(store, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Verify server certificate against a wincrypt store
|
||||
@return 0 - success, otherwise error occurred.
|
||||
*/
|
||||
SECURITY_STATUS schannel_verify_server_certificate(
|
||||
const CERT_CONTEXT* cert,
|
||||
HCERTSTORE store,
|
||||
BOOL check_revocation,
|
||||
const char* server_name,
|
||||
BOOL check_server_name,
|
||||
char* errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
wchar_t* wserver_name = NULL;
|
||||
DWORD dwVerifyFlags;
|
||||
DWORD dwRevocationFlags;
|
||||
|
||||
if (check_server_name)
|
||||
{
|
||||
int cchServerName = (int)strlen(server_name) + 1;
|
||||
wserver_name = (wchar_t*)LocalAlloc(0,sizeof(wchar_t) * cchServerName);
|
||||
if (!wserver_name)
|
||||
{
|
||||
FAIL("LocalAlloc() failed");
|
||||
}
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, server_name, cchServerName, wserver_name, cchServerName) < 0)
|
||||
{
|
||||
FAIL("MultiByteToWideChar() failed");
|
||||
}
|
||||
}
|
||||
|
||||
dwVerifyFlags = 0;
|
||||
dwRevocationFlags = 0;
|
||||
if (check_revocation)
|
||||
dwRevocationFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
|
||||
if (!check_server_name)
|
||||
dwVerifyFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
|
||||
|
||||
status = VerifyServerCertificate(cert, store, wserver_name ? wserver_name : L"SERVER_NAME",
|
||||
dwRevocationFlags, dwVerifyFlags, errmsg, errmsg_len);
|
||||
|
||||
cleanup:
|
||||
LocalFree(wserver_name);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* Attach private key (in PEM format) to client certificate */
|
||||
static SECURITY_STATUS load_private_key(CERT_CONTEXT* cert, char* private_key_str, size_t len, char* errmsg, size_t errmsg_len)
|
||||
{
|
||||
DWORD derlen = (DWORD)len;
|
||||
BYTE* derbuf = NULL;
|
||||
DWORD keyblob_len = 0;
|
||||
BYTE* keyblob = NULL;
|
||||
HCRYPTPROV hProv = 0;
|
||||
HCRYPTKEY hKey = 0;
|
||||
CERT_KEY_CONTEXT cert_key_context = { 0 };
|
||||
PCRYPT_PRIVATE_KEY_INFO pki = NULL;
|
||||
DWORD pki_len = 0;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
derbuf = LocalAlloc(0, derlen);
|
||||
if (!derbuf)
|
||||
{
|
||||
FAIL("LocalAlloc failed");
|
||||
}
|
||||
|
||||
if (!CryptStringToBinaryA(private_key_str, (DWORD)len, CRYPT_STRING_BASE64HEADER, derbuf, &derlen, NULL, NULL))
|
||||
{
|
||||
FAIL("Failed to convert BASE64 private key");
|
||||
}
|
||||
|
||||
/*
|
||||
To accommodate for both "BEGIN PRIVATE KEY" vs "BEGIN RSA PRIVATE KEY"
|
||||
sections in PEM, we try to decode with PKCS_PRIVATE_KEY_INFO first,
|
||||
and, if it fails, with PKCS_RSA_PRIVATE_KEY flag.
|
||||
*/
|
||||
if (CryptDecodeObjectEx(
|
||||
X509_ASN_ENCODING,
|
||||
PKCS_PRIVATE_KEY_INFO,
|
||||
derbuf, derlen,
|
||||
CRYPT_DECODE_ALLOC_FLAG,
|
||||
NULL, &pki, &pki_len))
|
||||
{
|
||||
// convert private key info to RSA private key blob
|
||||
if (!CryptDecodeObjectEx(
|
||||
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||
PKCS_RSA_PRIVATE_KEY,
|
||||
pki->PrivateKey.pbData,
|
||||
pki->PrivateKey.cbData,
|
||||
CRYPT_DECODE_ALLOC_FLAG,
|
||||
NULL, &keyblob, &keyblob_len))
|
||||
{
|
||||
FAIL("Failed to parse private key");
|
||||
}
|
||||
}
|
||||
else if (!CryptDecodeObjectEx(
|
||||
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||
PKCS_RSA_PRIVATE_KEY,
|
||||
derbuf, derlen,
|
||||
CRYPT_DECODE_ALLOC_FLAG, NULL,
|
||||
&keyblob, &keyblob_len))
|
||||
{
|
||||
FAIL("Failed to parse private key");
|
||||
}
|
||||
|
||||
if (!CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
||||
{
|
||||
FAIL("CryptAcquireContext failed");
|
||||
}
|
||||
|
||||
if (!CryptImportKey(hProv, keyblob, keyblob_len, 0, 0, (HCRYPTKEY*)&hKey))
|
||||
{
|
||||
FAIL("CryptImportKey failed");
|
||||
}
|
||||
cert_key_context.hCryptProv = hProv;
|
||||
cert_key_context.dwKeySpec = AT_KEYEXCHANGE;
|
||||
cert_key_context.cbSize = sizeof(cert_key_context);
|
||||
|
||||
/* assign private key to certificate context */
|
||||
if (!CertSetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID,
|
||||
CERT_STORE_NO_CRYPT_RELEASE_FLAG,
|
||||
&cert_key_context))
|
||||
{
|
||||
FAIL("CertSetCertificateContextProperty failed");
|
||||
}
|
||||
|
||||
cleanup:
|
||||
LocalFree(derbuf);
|
||||
LocalFree(keyblob);
|
||||
LocalFree(pki);
|
||||
if (hKey)
|
||||
CryptDestroyKey(hKey);
|
||||
if (status)
|
||||
{
|
||||
if (hProv)
|
||||
CryptReleaseContext(hProv, 0);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
Given PEM strings for certificate and private key,
|
||||
create a client certificate*
|
||||
*/
|
||||
static CERT_CONTEXT* create_client_certificate_mem(
|
||||
char* cert_file_content,
|
||||
char* key_file_content,
|
||||
char* errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
CERT_CONTEXT* ctx = NULL;
|
||||
char* begin;
|
||||
char* end;
|
||||
CERT_BLOB cert_blob;
|
||||
DWORD actual_content_type = 0;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
/* Parse certificate */
|
||||
pem_locate(cert_file_content, PEM_TYPE_CERTIFICATE,
|
||||
&begin, &end);
|
||||
|
||||
if (!begin || !end)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("Client certificate not found in PEM file");
|
||||
}
|
||||
|
||||
cert_blob.pbData = (BYTE*)begin;
|
||||
cert_blob.cbData = (DWORD)(end - begin);
|
||||
if (!CryptQueryObject(
|
||||
CERT_QUERY_OBJECT_BLOB, &cert_blob,
|
||||
CERT_QUERY_CONTENT_FLAG_CERT,
|
||||
CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &actual_content_type,
|
||||
NULL, NULL, NULL, (const void**)&ctx))
|
||||
{
|
||||
FAIL("Can't parse client certficate");
|
||||
}
|
||||
|
||||
/* Parse key */
|
||||
PEM_TYPE types[] = { PEM_TYPE_RSA_PRIVATE_KEY, PEM_TYPE_PRIVATE_KEY };
|
||||
for (int i = 0; i < sizeof(types) / sizeof(types[0]); i++)
|
||||
{
|
||||
pem_locate(key_file_content, types[i], &begin, &end);
|
||||
if (begin && end)
|
||||
{
|
||||
/* Assign key to certificate.*/
|
||||
status = load_private_key(ctx, begin, (end - begin), errmsg, errmsg_len);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!begin || !end)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("Client private key not found in PEM");
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (status && ctx)
|
||||
{
|
||||
CertFreeCertificateContext(ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
/* Given cert and key, as PEM file names, create a client certificate */
|
||||
CERT_CONTEXT* schannel_create_cert_context(char* cert_file, char* key_file, char* errmsg, size_t errmsg_len)
|
||||
{
|
||||
CERT_CONTEXT* ctx = NULL;
|
||||
char* key_file_content = NULL;
|
||||
char* cert_file_content = NULL;
|
||||
|
||||
cert_file_content = pem_file_to_string(cert_file, errmsg, errmsg_len);
|
||||
|
||||
if (!cert_file_content)
|
||||
goto cleanup;
|
||||
|
||||
if (cert_file == key_file)
|
||||
{
|
||||
key_file_content = cert_file_content;
|
||||
}
|
||||
else
|
||||
{
|
||||
key_file_content = pem_file_to_string(key_file, errmsg, errmsg_len);
|
||||
if (!key_file_content)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ctx = create_client_certificate_mem(cert_file_content, key_file_content, errmsg, errmsg_len);
|
||||
|
||||
cleanup:
|
||||
LocalFree(cert_file_content);
|
||||
if (cert_file != key_file)
|
||||
LocalFree(key_file_content);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
Free certificate, and all resources, created by schannel_create_cert_context()
|
||||
*/
|
||||
void schannel_free_cert_context(const CERT_CONTEXT* cert)
|
||||
{
|
||||
/* release provider handle which was acquires in load_private_key() */
|
||||
CERT_KEY_CONTEXT cert_key_context = { 0 };
|
||||
cert_key_context.cbSize = sizeof(cert_key_context);
|
||||
DWORD cbData = sizeof(CERT_KEY_CONTEXT);
|
||||
HCRYPTPROV hProv = 0;
|
||||
|
||||
if (CertGetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &cert_key_context, &cbData))
|
||||
{
|
||||
hProv = cert_key_context.hCryptProv;
|
||||
}
|
||||
CertFreeCertificateContext(cert);
|
||||
if (hProv)
|
||||
{
|
||||
CryptReleaseContext(cert_key_context.hCryptProv, 0);
|
||||
}
|
||||
}
|
53
vendor/MDBC/libmariadb/secure/schannel_certs.h
vendored
Normal file
53
vendor/MDBC/libmariadb/secure/schannel_certs.h
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2019 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
|
||||
|
||||
*************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
extern SECURITY_STATUS schannel_create_store(
|
||||
const char* CAFile,
|
||||
const char* CAPath,
|
||||
const char* CRLFile,
|
||||
const char* CRLPath,
|
||||
HCERTSTORE* store,
|
||||
char* errmsg,
|
||||
size_t errmsg_len
|
||||
);
|
||||
|
||||
extern SECURITY_STATUS schannel_verify_server_certificate(
|
||||
const CERT_CONTEXT* cert,
|
||||
HCERTSTORE store,
|
||||
BOOL check_revocation,
|
||||
const char* server_name,
|
||||
BOOL check_server_name,
|
||||
char* errmsg,
|
||||
size_t errmsg_len);
|
||||
|
||||
extern void schannel_free_store(HCERTSTORE store);
|
||||
|
||||
extern CERT_CONTEXT* schannel_create_cert_context(
|
||||
char* cert_file,
|
||||
char* key_file,
|
||||
char* errmsg,
|
||||
size_t errmsg_len);
|
||||
|
||||
extern void schannel_free_cert_context(const CERT_CONTEXT* cert);
|
||||
|
103
vendor/MDBC/libmariadb/secure/win_crypt.c
vendored
Normal file
103
vendor/MDBC/libmariadb/secure/win_crypt.c
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright (C) 2018 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
|
||||
*/
|
||||
#include <windows.h>
|
||||
#include <bcrypt.h>
|
||||
#include <ma_crypt.h>
|
||||
#include <malloc.h>
|
||||
|
||||
BCRYPT_ALG_HANDLE Sha256Prov= 0;
|
||||
BCRYPT_ALG_HANDLE Sha512Prov= 0;
|
||||
BCRYPT_ALG_HANDLE RsaProv= 0;
|
||||
|
||||
static LPCWSTR ma_hash_get_algorithm(unsigned int alg, BCRYPT_ALG_HANDLE *algHdl)
|
||||
{
|
||||
switch(alg)
|
||||
{
|
||||
case MA_HASH_SHA256:
|
||||
*algHdl= Sha256Prov;
|
||||
return BCRYPT_SHA256_ALGORITHM;
|
||||
case MA_HASH_SHA512:
|
||||
*algHdl= Sha512Prov;
|
||||
return BCRYPT_SHA512_ALGORITHM;
|
||||
default:
|
||||
*algHdl= 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MA_HASH_CTX *ma_hash_new(unsigned int algorithm, MA_HASH_CTX *ctx)
|
||||
{
|
||||
MA_HASH_CTX *newctx= ctx;
|
||||
DWORD cbObjSize, cbData;
|
||||
LPCWSTR alg;
|
||||
BCRYPT_ALG_HANDLE algHdl= 0;
|
||||
|
||||
alg= ma_hash_get_algorithm(algorithm, &algHdl);
|
||||
|
||||
if (!alg || !algHdl)
|
||||
return NULL;
|
||||
|
||||
if (BCryptGetProperty(algHdl, BCRYPT_OBJECT_LENGTH,
|
||||
(PBYTE)&cbObjSize, sizeof(DWORD),
|
||||
&cbData, 0) < 0)
|
||||
goto error;
|
||||
|
||||
if (!newctx)
|
||||
{
|
||||
newctx= (MA_HASH_CTX *)calloc(1, sizeof(MA_HASH_CTX));
|
||||
newctx->free_me= 1;
|
||||
}
|
||||
else
|
||||
memset(newctx, 0, sizeof(MA_HASH_CTX));
|
||||
|
||||
newctx->hashObject= (PBYTE)malloc(cbObjSize);
|
||||
newctx->digest_len= (DWORD)ma_hash_digest_size(algorithm);
|
||||
BCryptCreateHash(algHdl, &newctx->hHash, newctx->hashObject, cbObjSize, NULL, 0, 0);
|
||||
|
||||
return newctx;
|
||||
error:
|
||||
if (newctx && !ctx)
|
||||
free(newctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ma_hash_free(MA_HASH_CTX *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
{
|
||||
if (ctx->hHash)
|
||||
BCryptDestroyHash(ctx->hHash);
|
||||
if (ctx->hashObject)
|
||||
free(ctx->hashObject);
|
||||
if (ctx->free_me)
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ma_hash_input(MA_HASH_CTX *ctx,
|
||||
const unsigned char *buffer,
|
||||
size_t len)
|
||||
{
|
||||
BCryptHashData(ctx->hHash, (PUCHAR)buffer, (LONG)len, 0);
|
||||
}
|
||||
|
||||
void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
|
||||
{
|
||||
BCryptFinishHash(ctx->hHash, digest, ctx->digest_len, 0);
|
||||
}
|
Reference in New Issue
Block a user