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

CONC-312: Implementation for caching_sha2_password plugin

This commit is contained in:
Georg Richter
2018-12-01 08:56:24 +01:00
parent 84ab333bfa
commit 005195f9cb
8 changed files with 963 additions and 0 deletions

166
include/ma_crypt.h Normal file
View File

@@ -0,0 +1,166 @@
/*
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
*/
#ifndef _ma_hash_h_
#define _ma_hash_h_
#include <stddef.h>
#include <stdarg.h>
/*! Hash algorithms */
#define MA_HASH_MD5 1
#define MA_HASH_SHA1 2
#define MA_HASH_SHA224 3
#define MA_HASH_SHA256 4
#define MA_HASH_SHA384 5
#define MA_HASH_SHA512 6
#define MA_HASH_RIPEMD160 7
/*! Hash digest sizes */
#define MA_MD5_HASH_SIZE 16
#define MA_SHA1_HASH_SIZE 20
#define MA_SHA224_HASH_SIZE 28
#define MA_SHA256_HASH_SIZE 32
#define MA_SHA384_HASH_SIZE 48
#define MA_SHA512_HASH_SIZE 64
#define MA_RIPEMD160_HASH_SIZE 20
#define MA_MAX_HASH_SIZE 64
/** \typedef MRL hash context */
#if defined(HAVE_OPENSSL)
typedef void MA_HASH_CTX;
#elif defined(HAVE_GNUTLS)
typedef struct {
void *ctx;
const struct nettle_hash *hash;
} MA_HASH_CTX;
#elif defined(HAVE_SCHANNEL)
#include <windows.h>
#include <bcrypt.h>
typedef struct {
char free_me;
BCRYPT_ALG_HANDLE hAlg;
BCRYPT_HASH_HANDLE hHash;
PBYTE hashObject;
DWORD digest_len;
} MA_HASH_CTX;
#endif
/**
@brief acquire and initialize new hash context
@param[in] algorithm hash algorithm
@param[in] ctx pointer to a crypto context
@return hash context on success, NULL on error
*/
MA_HASH_CTX *ma_hash_new(unsigned int algorithm, MA_HASH_CTX *ctx);
/**
@brief release and deinitializes a hash context
@param[in] hash context
@return void
*/
void ma_hash_free(MA_HASH_CTX *ctx);
/**
@brief hashes len bytes of data into the hash context.
This function can be called several times on same context to
hash additional data.
@param[in] ctx hash context
@param[in] buffer data buffer
@param[in] len size of buffer
@return void
*/
void ma_hash_input(MA_HASH_CTX *ctx,
const unsigned char *buffer,
size_t len);
/**
@brief retrieves the hash value from hash context
@param[in] ctx hash context
@param[out] digest digest containing hash value
@return void
*/
void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest);
/**
@brief returns digest size for a given hash algorithm
@param[in] hash algorithm
@retuns digest size or 0 on error
*/
static inline size_t ma_hash_digest_size(unsigned int hash_alg)
{
switch(hash_alg) {
case MA_HASH_MD5:
return MA_MD5_HASH_SIZE;
case MA_HASH_SHA1:
return MA_SHA1_HASH_SIZE;
case MA_HASH_SHA224:
return MA_SHA224_HASH_SIZE;
case MA_HASH_SHA256:
return MA_SHA256_HASH_SIZE;
case MA_HASH_SHA384:
return MA_SHA384_HASH_SIZE;
case MA_HASH_SHA512:
return MA_SHA512_HASH_SIZE;
case MA_HASH_RIPEMD160:
return MA_RIPEMD160_HASH_SIZE;
default:
return 0;
}
}
/**
@brief function to compute hash from buffer.
@param[in] hash_alg hash algorithm
@param[in] buffer buffer
@param[in] buffer_leng length of buffer
@param[out] digest computed hash digest
@return void
*/
static inline void ma_hash(unsigned int algorithm,
const unsigned char *buffer,
size_t buffer_length,
unsigned char *digest)
{
MA_HASH_CTX *ctx= NULL;
#ifdef HAVE_SCHANNEL
MA_HASH_CTX dctx;
ctx= &dctx;
#endif
ctx= ma_hash_new(algorithm, ctx);
ma_hash_input(ctx, buffer, buffer_length);
ma_hash_result(ctx, digest);
ma_hash_free(ctx);
}
#endif /* _ma_hash_h_ */

View File

@@ -639,6 +639,7 @@ struct st_default_options mariadb_defaults[] =
{MARIADB_OPT_SSL_FP_LIST, MARIADB_OPTION_STR, "ssl-fplist"},
{MARIADB_OPT_TLS_PASSPHRASE, MARIADB_OPTION_STR, "ssl-passphrase"},
{MARIADB_OPT_TLS_VERSION, MARIADB_OPTION_STR, "tls_version"},
{MYSQL_SERVER_PUBLIC_KEY, MARIADB_OPTION_STR, "server_public_key"},
{MYSQL_OPT_BIND, MARIADB_OPTION_STR, "bind-address"},
{0, 0, NULL}
};

View 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);
}

View File

@@ -0,0 +1,87 @@
/*
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 <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);
}

View File

@@ -0,0 +1,101 @@
/*
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>
#pragma comment(lib, "bcrypt.lib")
BCRYPT_ALG_HANDLE Sha256Prov= 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;
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);
}

View File

@@ -18,6 +18,27 @@ REGISTER_PLUGIN(TARGET dialog
SOURCES ${CC_SOURCE_DIR}/plugins/auth/dialog.c
${CC_SOURCE_DIR}/libmariadb/get_password.c)
# SHA256 caching plugin for MySQL 8.0 connection
IF(WITH_SSL)
IF(${WITH_SSL} STREQUAL "OPENSSL")
SET(CRYPT_SOURCE ${CC_SOURCE_DIR}/libmariadb/secure/openssl_crypt.c)
SET(CACHING_SHA2_LIBS ${SSL_LIBRARIES})
ELSEIF(${WITH_SSL} STREQUAL "SCHANNEL")
SET(CRYPT_SOURCE ${CC_SOURCE_DIR}/libmariadb/secure/win_crypt.c)
SET(CACHING_SHA2_LIBS crypt32 bcrypt)
ELSEIF(${WITH_SSL} STREQUAL "GNUTLS")
SET(CRYPT_SOURCE ${CC_SOURCE_DIR}/libmariadb/secure/gnutls_crypt.c)
SET(CACHING_SHA2_LIBS ${SSL_LIBRARIES})
ENDIF()
REGISTER_PLUGIN(TARGET caching_sha2_password
TYPE MARIADB_CLIENT_PLUGIN_AUTH
CONFIGURATIONS DYNAMIC STATIC OFF
DEFAULT DYNAMIC
SOURCES ${CC_SOURCE_DIR}/plugins/auth/caching_sha2_pw.c
${CRYPT_SOURCE}
LIBRARIES ${CACHING_SHA2_LIBS})
ENDIF()
#GSSAPI client authentication plugin
IF(NOT WIN32)
INCLUDE(${CC_SOURCE_DIR}/cmake/FindGSSAPI.cmake)

View File

@@ -0,0 +1,470 @@
/************************************************************************************
Copyright (C) 2017 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
*************************************************************************************/
#ifndef _WIN32
#define _GNU_SOURCE 1
#endif
#ifdef _WIN32
#if !defined(HAVE_OPENSSL)
#define HAVE_WINCRYPT
#endif
#endif
#if defined(HAVE_OPENSSL) || defined(HAVE_SCHANNEL) || defined(HAVE_GNUTLS)
#include <ma_global.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <memory.h>
#include <errmsg.h>
#include <ma_global.h>
#include <ma_sys.h>
#include <ma_common.h>
#ifndef WIN32
#include <dlfcn.h>
#endif
#if defined(HAVE_OPENSSL)
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#elif defined(HAVE_GNUTLS)
#include <gnutls/gnutls.h>
#elif defined(HAVE_SCHANNEL)
#include <windows.h>
#include <wincrypt.h>
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
#pragma comment(lib, "crypt32.lib")
extern BCRYPT_ALG_HANDLE RsaProv;
extern BCRYPT_ALG_HANDLE Sha256Prov;
#endif
#include <ma_crypt.h>
#define MAX_PW_LEN 1024
#define REQUEST_PUBLIC_KEY 2
#define CACHED_LOGIN_SUCCEEDED 3
#define RSA_LOGIN_REQUIRED 4
/* MySQL server allows requesting public key only for non secure connections.
secure connections are:
- TLS/SSL connections
- unix_socket connections
*/
static unsigned char is_connection_secure(MYSQL *mysql)
{
if (mysql->options.use_ssl ||
mysql->net.pvio->type != PVIO_TYPE_SOCKET)
return 1;
return 0;
}
static int ma_sha256_scramble(unsigned char *scramble, size_t scramble_len,
unsigned char *source, size_t source_len,
unsigned char *salt, size_t salt_len)
{
unsigned char digest1[MA_SHA256_HASH_SIZE],
digest2[MA_SHA256_HASH_SIZE],
new_scramble[MA_SHA256_HASH_SIZE];
#ifdef HAVE_SCHANNEL
MA_HASH_CTX myctx;
MA_HASH_CTX *ctx= &myctx;
#else
MA_HASH_CTX *ctx = NULL;
#endif
size_t i;
/* check if all specified lenghts are valid */
if (!scramble_len || !source_len || !salt_len)
return 1;
/* Step1: create sha256 from source */
if (!(ctx= ma_hash_new(MA_HASH_SHA256, ctx)))
return 1;
ma_hash_input(ctx, source, source_len);
ma_hash_result(ctx, digest1);
ma_hash_free(ctx);
#ifndef HAVE_SCHANNEL
ctx = NULL;
#endif
/* Step2: create sha256 digest from digest1 */
if (!(ctx= ma_hash_new(MA_HASH_SHA256, ctx)))
return 1;
ma_hash_input(ctx, digest1, MA_SHA256_HASH_SIZE);
ma_hash_result(ctx, digest2);
ma_hash_free(ctx);
#ifndef HAVE_SCHANNEL
ctx = NULL;
#endif
/* Step3: create sha256 digest from digest2 + salt */
if (!(ctx= ma_hash_new(MA_HASH_SHA256, ctx)))
return 1;
ma_hash_input(ctx, digest2, MA_SHA256_HASH_SIZE);
ma_hash_input(ctx, salt, salt_len);
ma_hash_result(ctx, new_scramble);
ma_hash_free(ctx);
/* Step4: xor(digest1, scramble1) */
for (i= 0; i < scramble_len; i++)
scramble[i]= digest1[i] ^ new_scramble[i];
return 0;
}
/* function prototypes */
static int auth_caching_sha2_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
static int auth_caching_sha2_deinit();
static int auth_caching_sha2_init(char *unused1,
size_t unused2,
int unused3,
va_list);
#ifndef PLUGIN_DYNAMIC
struct st_mysql_client_plugin_AUTHENTICATION caching_sha2_password_client_plugin=
#else
struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ =
#endif
{
MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
"caching_sha2_password",
"Georg Richter",
"Caching SHA2 Authentication Plugin",
{0,1,0},
"LGPL",
NULL,
auth_caching_sha2_init,
auth_caching_sha2_deinit,
NULL,
auth_caching_sha2_client
};
#ifdef HAVE_SCHANNEL
static LPBYTE ma_load_pem(const char *buffer, DWORD *buffer_len)
{
LPBYTE der_buffer= NULL;
DWORD der_buffer_length;
if (buffer_len == NULL || *buffer_len == 0)
return NULL;
/* calculate the length of DER binary */
if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
NULL, &der_buffer_length, NULL, NULL))
goto end;
/* allocate DER binary buffer */
if (!(der_buffer= (LPBYTE)malloc(der_buffer_length)))
goto end;
/* convert to DER binary */
if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
der_buffer, &der_buffer_length, NULL, NULL))
goto end;
*buffer_len= der_buffer_length;
return der_buffer;
end:
if (der_buffer)
free(der_buffer);
*buffer_len= 0;
return NULL;
}
#endif
char *load_pub_key_file(const char *filename, int *pub_key_size)
{
FILE *fp= NULL;
char *buffer= NULL;
unsigned char error= 1;
if (!pub_key_size)
return NULL;
if (!(fp= fopen(filename, "r")))
goto end;
if (fseek(fp, 0, SEEK_END))
goto end;
*pub_key_size= ftell(fp);
rewind(fp);
if (!(buffer= malloc(*pub_key_size + 1)))
goto end;
if (!fread(buffer, *pub_key_size, 1, fp))
goto end;
error= 0;
end:
if (fp)
fclose(fp);
if (error && buffer)
{
free(buffer);
buffer= NULL;
}
return buffer;
}
static int auth_caching_sha2_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
{
unsigned char *packet;
int packet_length;
int rc= CR_ERROR;
#if !defined(HAVE_GNUTLS)
char passwd[MAX_PW_LEN];
unsigned char rsa_enc_pw[MAX_PW_LEN];
#ifdef HAVE_OPENSSL
int rsa_size;
#else
ULONG rsa_size;
#endif
unsigned int pwlen, i;
char *filebuffer= NULL;
#endif
unsigned char buf[MA_SHA256_HASH_SIZE];
#if defined(HAVE_OPENSSL)
RSA *pubkey= NULL;
BIO *bio;
#elif defined(HAVE_SCHANNEL)
BCRYPT_KEY_HANDLE pubkey= 0;
BCRYPT_OAEP_PADDING_INFO paddingInfo;
LPBYTE der_buffer= NULL;
DWORD der_buffer_len= 0;
CERT_PUBLIC_KEY_INFO *publicKeyInfo= NULL;
DWORD publicKeyInfoLen;
#endif
/* read error */
if ((packet_length= vio->read_packet(vio, &packet)) < 0)
return CR_ERROR;
if (packet_length != SCRAMBLE_LENGTH + 1)
return CR_SERVER_HANDSHAKE_ERR;
memmove(mysql->scramble_buff, packet, SCRAMBLE_LENGTH);
mysql->scramble_buff[SCRAMBLE_LENGTH]= 0;
/* if a tls session is active we need to send plain password */
if (mysql->client_flag & CLIENT_SSL)
{
if (vio->write_packet(vio, (unsigned char *)mysql->passwd, (int)strlen(mysql->passwd) + 1))
return CR_ERROR;
return CR_OK;
}
/* send empty packet if no password was provided */
if (!mysql->passwd || !mysql->passwd[0])
{
if (vio->write_packet(vio, 0, 0))
return CR_ERROR;
return CR_OK;
}
/* This is the normal authentication, if the host/user key is already in server
cache. In case authentication will fail, we will not return an error but will
try to connect via RSA encryption.
*/
if (ma_sha256_scramble(buf, MA_SHA256_HASH_SIZE,
(unsigned char *)mysql->passwd, strlen(mysql->passwd),
(unsigned char *)mysql->scramble_buff, SCRAMBLE_LENGTH))
return CR_ERROR;
if (vio->write_packet(vio, buf, MA_SHA256_HASH_SIZE))
return CR_ERROR;
if ((packet_length=vio->read_packet(vio, &packet)) == -1)
return CR_ERROR;
if (packet_length == 1)
{
switch (*packet) {
case CACHED_LOGIN_SUCCEEDED:
return CR_OK;
case RSA_LOGIN_REQUIRED:
break;
default:
return CR_ERROR;
}
}
if (!is_connection_secure(mysql))
{
#if defined(HAVE_GNUTLS)
mysql->methods->set_error(mysql, CR_AUTH_PLUGIN_ERR, "HY000",
"RSA Encrytion not supported - caching_sha2_password plugin was built with GnuTLS support");
return CR_ERROR;
#else
/* read public key file (if specified) */
if (mysql->options.extension &&
mysql->options.extension->server_public_key)
{
filebuffer= load_pub_key_file(mysql->options.extension->server_public_key,
&packet_length);
}
/* if no public key file was specified or if we couldn't read the file,
we ask server to send public key */
if (!filebuffer)
{
unsigned char request= REQUEST_PUBLIC_KEY;
if (vio->write_packet(vio, &request, 1) ||
(packet_length=vio->read_packet(vio, &packet)) == -1)
{
mysql->methods->set_error(mysql, CR_AUTH_PLUGIN_ERR, "HY000", "Couldn't read RSA public key from server");
return CR_ERROR;
}
}
#if defined(HAVE_OPENSSL)
bio= BIO_new_mem_buf(filebuffer ? (unsigned char *)filebuffer : packet,
packet_length);
if ((pubkey= PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL)))
rsa_size= RSA_size(pubkey);
BIO_free(bio);
ERR_clear_error();
#elif defined(HAVE_SCHANNEL)
der_buffer_len= packet_length;
/* Load pem and convert it to binary object. New length will be returned
in der_buffer_len */
if (!(der_buffer= ma_load_pem(filebuffer ? filebuffer : (char *)packet, &der_buffer_len)))
goto error;
/* Create context and load public key */
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
der_buffer, der_buffer_len,
CRYPT_DECODE_ALLOC_FLAG, NULL,
&publicKeyInfo, &publicKeyInfoLen))
goto error;
free(der_buffer);
/* Import public key as cng key */
if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, publicKeyInfo,
CRYPT_OID_INFO_PUBKEY_ENCRYPT_KEY_FLAG,
NULL, &pubkey))
goto error;
#endif
if (!pubkey)
return CR_ERROR;
pwlen= (unsigned int)strlen(mysql->passwd) + 1; /* include terminating zero */
if (pwlen > MAX_PW_LEN)
goto error;
memcpy(passwd, mysql->passwd, pwlen);
/* xor password with scramble */
for (i=0; i < pwlen; i++)
passwd[i]^= *(mysql->scramble_buff + i % SCRAMBLE_LENGTH);
/* encrypt scrambled password */
#if defined(HAVE_OPENSSL)
if (RSA_public_encrypt(pwlen, (unsigned char *)passwd, rsa_enc_pw, pubkey, RSA_PKCS1_OAEP_PADDING) < 0)
goto error;
#elif defined(HAVE_SCHANNEL)
ZeroMemory(&paddingInfo, sizeof(paddingInfo));
paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
if ((rc= BCryptEncrypt(pubkey, (PUCHAR)passwd, pwlen, &paddingInfo, NULL, 0, rsa_enc_pw,
MAX_PW_LEN, &rsa_size, BCRYPT_PAD_OAEP)))
goto error;
#endif
if (vio->write_packet(vio, rsa_enc_pw, rsa_size))
goto error;
rc= CR_OK;
#endif
}
else
{
if (vio->write_packet(vio, (unsigned char *)mysql->passwd, (int)strlen(mysql->passwd) + 1))
return CR_ERROR;
return CR_OK;
}
#if !defined(HAVE_GNUTLS)
error:
#if defined(HAVE_OPENSSL)
if (pubkey)
RSA_free(pubkey);
#elif defined(HAVE_SCHANNEL)
if (pubkey)
BCryptDestroyKey(pubkey);
if (publicKeyInfo)
LocalFree(publicKeyInfo);
#endif
free(filebuffer);
#endif
return rc;
}
/* }}} */
/* {{{ static int auth_caching_sha2_init */
/*
Initialization routine
SYNOPSIS
auth_sha256_init
unused1
unused2
unused3
unused4
DESCRIPTION
Init function checks if the caller provides own dialog function.
The function name must be mariadb_auth_dialog or
mysql_authentication_dialog_ask. If the function cannot be found,
we will use owr own simple command line input.
RETURN
0 success
*/
static int auth_caching_sha2_init(char *unused1 __attribute__((unused)),
size_t unused2 __attribute__((unused)),
int unused3 __attribute__((unused)),
va_list unused4 __attribute__((unused)))
{
#if defined(HAVE_SCHANNEL)
BCryptOpenAlgorithmProvider(&Sha256Prov, BCRYPT_SHA256_ALGORITHM, NULL, 0);
BCryptOpenAlgorithmProvider(&RsaProv, BCRYPT_RSA_ALGORITHM, NULL, 0);
#endif
return 0;
}
/* }}} */
/* {{{ auth_caching_sha2_deinit */
static int auth_caching_sha2_deinit()
{
#if defined(HAVE_SCHANNEL)
BCryptCloseAlgorithmProvider(Sha256Prov, 0);
BCryptCloseAlgorithmProvider(RsaProv, 0);
#endif
return 0;
}
/* }}} */
#endif /* defined(HAVE_OPENSSL) || defined(HAVE_WINCRYPT) || defined(HAVE_GNUTLS)*/

View File

@@ -1560,7 +1560,47 @@ static int test_conc351(MYSQL *unused __attribute__((unused)))
return OK;
}
static int test_conc312(MYSQL *my)
{
int rc;
char query[1024];
MYSQL *mysql;
sprintf(query, "DROP USER 'foo'@'%s'", this_host);
rc= mysql_query(my, query);
sprintf(query, "CREATE USER 'foo'@'%s' IDENTIFIED WITH caching_sha2_password BY 'foo'", this_host);
rc= mysql_query(my, query);
if (rc)
{
diag("caching_sha256_password not supported");
return SKIP;
}
sprintf(query, "GRANT ALL ON %s.* TO 'foo'@'%s'", schema, this_host);
rc= mysql_query(my, query);
check_mysql_rc(rc, my);
mysql= mysql_init(NULL);
if (!mysql_real_connect(mysql, hostname, "foo", "foo", schema, port, socketname, 0))
{
diag("Error: %s", mysql_error(mysql));
return FAIL;
}
mysql_close(mysql);
sprintf(query, "DROP USER 'foo'@'%s'", this_host);
rc= mysql_query(my, query);
check_mysql_rc(rc, mysql);
return OK;
}
struct my_tests_st my_tests[] = {
{"test_conc312", test_conc312, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_conc351", test_conc351, TEST_CONNECTION_NONE, 0, NULL, NULL},
{"test_conc332", test_conc332, TEST_CONNECTION_NONE, 0, NULL, NULL},
#ifndef WIN32