From f886562fb2c411bc7ff870d75a872d906674015b Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 6 Aug 2015 13:06:54 +0200 Subject: [PATCH 01/18] Initial cio implementation --- CMakeLists.txt | 43 +- include/errmsg.h | 16 +- include/mysql.h | 2 +- include/mysql/client_plugin.h | 32 +- include/mysql_async.h | 37 +- include/mysql_com.h | 6 +- include/violite.h | 167 ------- libmariadb/CMakeLists.txt | 14 +- libmariadb/client_plugin.c | 61 +-- libmariadb/errmsg.c | 38 +- libmariadb/libmariadb.c | 569 +++++------------------ libmariadb/my_stmt.c | 4 +- libmariadb/mysql_async.c | 76 +-- libmariadb/net.c | 228 +-------- libmariadb/violite.c | 713 ----------------------------- plugins/io/remote_io.c | 1 + unittest/libmariadb/CMakeLists.txt | 2 +- unittest/libmariadb/misc.c | 34 +- unittest/libmariadb/my_test.h | 2 + 19 files changed, 350 insertions(+), 1695 deletions(-) delete mode 100644 include/violite.h delete mode 100644 libmariadb/violite.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c561338..3499d1dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,9 +21,7 @@ ENDIF() ### Options ### IF(NOT WIN32) OPTION(WITH_MYSQLCOMPAT "creates libmysql* symbolic links" ON) - OPTION(WITH_OPENSSL "enables SSL support" ON) ELSE() - OPTION(WITH_OPENSSL "enables SSL support" OFF) OPTION(WITH_SIGNCODE "digitally sign files" OFF) OPTION(WITH_RTC "enables run time checks for debug builds" OFF) ENDIF() @@ -112,9 +110,9 @@ ENDIF() IF(UNIX) SEARCH_LIBRARY(DEFAULT_LIB inet_ntoa "c") - IF(NOT DEFAULT_LIB) + # IF(NOT DEFAULT_LIB) SEARCH_LIBRARY(LIBNSL inet_ntoa "nsl_r;nsl") - ENDIF() + # ENDIF() SEARCH_LIBRARY(DEFAULT_LIB bind "c") IF(NOT DEFAULT_LIB) SEARCH_LIBRARY(LIBBIND bind "bind;socket") @@ -159,18 +157,35 @@ INCLUDE(${CMAKE_SOURCE_DIR}/cmake/CheckFunctions.cmake) # check for various types INCLUDE(${CMAKE_SOURCE_DIR}/cmake/CheckTypes.cmake) -# Check for OpenSSL -IF(WITH_OPENSSL) +IF(WITH_SSL STREQUAL "OPENSSL") FIND_PACKAGE(OpenSSL) IF(OPENSSL_FOUND) - ADD_DEFINITIONS(-DHAVE_OPENSSL) - INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) - SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES}) - IF(OPENSSL_CRYPTO_LIBRARIES) - SET(SSL_LIBRARIES ${SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES}) - ENDIF() + ADD_DEFINITIONS(-DHAVE_OPENSSL -DHAVE_SSL) + ADD_DEFINITIONS(-DSSL_PLUGIN=cio_openssl_plugin) + SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_openssl.c") + SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES}) ELSE() - MESSAGE(FATAL_ERROR "OpenSSL not found. Please install OpenSSL or disable SSL support via option -DWITH_OPENSSL=Off") + MESSAGE(FATAL "OpenSSL not found") + ENDIF() +ENDIF() +IF(WITH_SSL STREQUAL "GNUTLS") + FIND_PACKAGE(GnuTLS) + IF(GNUTLS_FOUND) + ADD_DEFINITIONS(-DSSL_PLUGIN=cio_gnutls_plugin) + ADD_DEFINITIONS(-DHAVE_GNUTLS -DHAVE_SSL) + SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_gnutls.c") + SET(SSL_LIBRARIES ${GNUTLS_LIBRARIES}) + ELSE() + MESSAGE(FATAL "GnuTLS not found") + ENDIF() +ENDIF() +IF(WIN32) + IF(WITH_SSL STREQUAL "SCHANNEL") + ADD_DEFINITIONS(-DSSL_PLUGIN=cio_schannel_plugin) + MESSAGE(STATUS "SSL_TYPE ${SSL_TYPE}") + ADD_DEFINITIONS(-DHAVE_SCHANNEL -DHAVE_SSL) + SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_schannel.c" "${CMAKE_SOURCE_DIR}/plugins/builtin/ma_schannel.c") + INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/plugins/cio/") ENDIF() ENDIF() @@ -283,7 +298,7 @@ MESSAGE(STATUS "CPack generation: ${CPACK_GENERATOR}") IF(CLIENT_DOCS) MESSAGE(STATUS "Documentation included from ${CLIENT_DOCS}") ENDIF() -MESSAGE(STATUS "SSL support: ${WITH_OPENSSL}") +MESSAGE(STATUS "SSL support: ${WITH_SSL} Sources: ${SSL_SOURCES}") MESSAGE(STATUS "Experimental Sqlite support: ${WITH_SQLITE}") IF(WITH_EXTERNAL_ZLIB) MESSAGE(STATUS "Zlib support: ${WITH_EXTERNAL_ZLIB}") diff --git a/include/errmsg.h b/include/errmsg.h index 6bcf50ad..55c5a649 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -25,6 +25,7 @@ extern "C" { #endif void init_client_errs(void); extern const char *client_errors[]; /* Error messages */ +extern const char *mariadb_client_errors[]; /* Error messages */ #ifdef __cplusplus } #endif @@ -32,11 +33,10 @@ extern const char *client_errors[]; /* Error messages */ #define CR_MIN_ERROR 2000 /* For easier client code */ #define CR_MAX_ERROR 2999 -#if defined(OS2) && defined( MYSQL_SERVER) -#define CER(X) client_errors[(X)-CR_MIN_ERROR] -#else +#define CER_MIN_ERROR 5000 +#define CER_MAX_ERROR 5999 +#define CER(X) mariadb_client_errors[(X)-CER_MIN_ERROR] #define ER(X) client_errors[(X)-CR_MIN_ERROR] -#endif #define CLIENT_ERRMAP 2 /* Errormap used by my_error() */ #define CR_UNKNOWN_ERROR 2000 @@ -66,6 +66,11 @@ extern const char *client_errors[]; /* Error messages */ #define CR_PARAMS_NOT_BOUND 2031 #define CR_INVALID_PARAMETER_NO 2034 #define CR_UNSUPPORTED_PARAM_TYPE 2036 + +#define CR_SHARED_MEMORY_CONNECTION 2037 +#define CR_SHARED_MEMORY_CONNECT_ERROR 2038 + + #define CR_SECURE_AUTH 2049 #define CR_NO_DATA 2051 #define CR_NO_STMT_METADATA 2052 @@ -76,6 +81,9 @@ extern const char *client_errors[]; /* Error messages */ #define CR_ALREADY_CONNECTED 2059 #define CR_PLUGIN_FUNCTION_NOT_SUPPORTED 2060 +#define CR_EVENT_CREATE_FAILED 5000 +#define CR_BIND_ADDR_FAILED 5001 + #define SQLSTATE_UNKNOWN "HY000" #endif diff --git a/include/mysql.h b/include/mysql.h index 19856282..e2a10e69 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -251,7 +251,7 @@ struct st_mysql_options { my_bool compress,named_pipe; my_bool unused_1, unused_2, unused_3, unused_4; enum mysql_option methods_to_use; - char *client_ip; + char *bind_address; my_bool secure_auth; my_bool report_data_truncation; /* function pointers for local infile support */ diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index f0e04637..bcda3852 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -36,14 +36,17 @@ #endif /* known plugin types */ -#define MYSQL_CLIENT_DB_PLUGIN 0 -#define MYSQL_CLIENT_reserved 1 -#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2 -#define MYSQL_CLIENT_reserved22 3 +#define MYSQL_CLIENT_GENERIC_PLUGIN 0 +#define MYSQL_CLIENT_CIO_PLUGIN 1 /* communication IO */ +#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2 /* authentication */ +#define MYSQL_CLIENT_TRACE_PLUGIN 3 /* cio trace */ #define MYSQL_CLIENT_REMOTEIO_PLUGIN 4 #define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100 #define MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION 0x0100 +#define MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION 0x0100 +#define MYSQL_CLIENT_SSL_INTERFACE_VERSION 0x0100 +#define MYSQL_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION 0x01000 #define MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION 0x0100 #define MYSQL_CLIENT_MAX_PLUGINS 5 @@ -63,9 +66,9 @@ const char *author; \ const char *desc; \ unsigned int version[3]; \ + const char *license; \ int (*init)(char *, size_t, int, va_list); \ - int (*deinit)(void); - + int (*deinit)(); struct st_mysql_client_plugin { MYSQL_CLIENT_PLUGIN_HEADER @@ -83,6 +86,18 @@ typedef struct st_mariadb_client_plugin_DB #define MARIADB_DB_DRIVER(a) ((a)->ext_db) +/******************* Communication IO plugin *****************/ +#include +#include + +typedef struct st_mariadb_client_plugin_CIO +{ + MYSQL_CLIENT_PLUGIN_HEADER + struct st_ma_cio_methods *methods; + struct st_ma_cio_ssl_methods *ssl_methods; + void *compress_methods; +} MARIADB_CIO_PLUGIN; + /******** authentication plugin specific declarations *********/ #include @@ -92,6 +107,11 @@ struct st_mysql_client_plugin_AUTHENTICATION int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql); }; +/******** trace plugin *******/ +struct st_mysql_client_plugin_TRACE +{ + MYSQL_CLIENT_PLUGIN_HEADER +}; /** type of the mysql_authentication_dialog_ask function diff --git a/include/mysql_async.h b/include/mysql_async.h index 298f3f57..60ec1a29 100644 --- a/include/mysql_async.h +++ b/include/mysql_async.h @@ -1,40 +1,37 @@ /* Copyright (C) 2012 MariaDB Services and Kristian Nielsen - 2015 MariaDB Corporation - 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 program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. - This library is distributed in the hope that it will be useful, + This program 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA */ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Common definitions for MariaDB non-blocking client library. */ #ifndef MYSQL_ASYNC_H #define MYSQL_ASYNC_H -extern int my_connect_async(struct mysql_async_context *b, my_socket fd, +extern int my_connect_async(MARIADB_CIO *cio, const struct sockaddr *name, uint namelen, int vio_timeout); -extern ssize_t my_recv_async(struct mysql_async_context *b, int fd, - unsigned char *buf, size_t size, int timeout); -extern ssize_t my_send_async(struct mysql_async_context *b, int fd, +extern ssize_t my_recv_async(MARIADB_CIO *cio, + const unsigned char *buf, size_t size, int timeout); +extern ssize_t my_send_async(MARIADB_CIO *cio, const unsigned char *buf, size_t size, int timeout); extern my_bool my_io_wait_async(struct mysql_async_context *b, - enum enum_vio_io_event event, int timeout); -#ifdef HAVE_OPENSSL -extern int my_ssl_read_async(struct mysql_async_context *b, SSL *ssl, + enum enum_cio_io_event event, int timeout); +#ifdef HAVE_SSL +extern int my_ssl_read_async(struct mysql_async_context *b, MARIADB_SSL *ssl, void *buf, int size); -extern int my_ssl_write_async(struct mysql_async_context *b, SSL *ssl, +extern int my_ssl_write_async(struct mysql_async_context *b, MARIADB_SSL *ssl, const void *buf, int size); #endif diff --git a/include/mysql_com.h b/include/mysql_com.h index d4495571..b843dfe1 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -216,6 +216,8 @@ class Vio; /* Fill Vio class in C++ */ #else struct st_vio; /* Only C */ typedef struct st_vio Vio; +struct st_ma_cio; +typedef struct st_ma_cio MARIADB_CIO; #endif #endif @@ -233,7 +235,7 @@ typedef struct st_vio Vio; typedef struct st_net { - Vio *vio; + MARIADB_CIO *cio; unsigned char *buff; unsigned char *buff_end,*write_pos,*read_pos; my_socket fd; /* For Perl DBI/dbd */ @@ -324,7 +326,7 @@ extern unsigned long net_buffer_length; #define net_new_transaction(net) ((net)->pkt_nr=0) -int my_net_init(NET *net, Vio *vio); +int my_net_init(NET *net, MARIADB_CIO *cio); void net_end(NET *net); void net_clear(NET *net); int net_flush(NET *net); diff --git a/include/violite.h b/include/violite.h deleted file mode 100644 index cc66a775..00000000 --- a/include/violite.h +++ /dev/null @@ -1,167 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA */ - -/* - * Vio Lite. - * Purpose: include file for Vio that will work with C and C++ - */ - -#ifndef vio_violite_h_ -#define vio_violite_h_ - - - -#include "my_net.h" /* needed because of struct in_addr */ - -#ifdef HAVE_VIO -#include /* Full VIO interface */ -#else - -#ifdef HAVE_OPENSSL -#include -#endif - -enum enum_vio_io_event -{ - VIO_IO_EVENT_READ, - VIO_IO_EVENT_WRITE, - VIO_IO_EVENT_CONNECT -}; - -/* Simple vio interface in C; The functions are implemented in violite.c */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef Vio_defined -#define Vio_defined -struct st_vio; /* Only C */ -typedef struct st_vio Vio; -#endif - -#ifndef _WIN32 -#define HANDLE void * -#endif - -/* vio read-ahead cachine */ -#define VIO_CACHE_SIZE 16384 -#define VIO_CACHE_MIN_SIZE 2048 - -enum enum_vio_type { VIO_CLOSED, VIO_TYPE_TCPIP, VIO_TYPE_SOCKET, - VIO_TYPE_NAMEDPIPE, VIO_TYPE_SSL}; - -Vio* vio_new(my_socket sd, - enum enum_vio_type type, - my_bool localhost); -#ifdef _WIN32 -Vio* vio_new_win32pipe(HANDLE hPipe); -#endif -void vio_delete(Vio* vio); -void vio_reset(Vio* vio, enum enum_vio_type type, - my_socket sd, HANDLE hPipe, - my_bool localhost); - -/* - * vio_read and vio_write should have the same semantics - * as read(2) and write(2). - */ -size_t vio_read(Vio* vio, gptr buf, size_t size); -my_bool vio_read_peek(Vio *vio, size_t *bytes); -size_t vio_write(Vio* vio, const gptr buf, size_t size); -/* - * Whenever the socket is set to blocking mode or not. - */ -int vio_blocking( Vio* vio, - my_bool onoff, - my_bool *prevmode); -my_bool vio_is_blocking( Vio* vio); -/* - * setsockopt TCP_NODELAY at IPPROTO_TCP level, when possible. - */ - int vio_fastsend( Vio* vio); -/* - * setsockopt SO_KEEPALIVE at SOL_SOCKET level, when possible. - */ -int vio_keepalive( Vio* vio, - my_bool onoff); -/* - * Whenever we should retry the last read/write operation. - */ -my_bool vio_should_retry( Vio* vio); -/* - * When the workday is over... - */ -int vio_close( Vio* vio); -/* - * Short text description of the socket for those, who are curious.. - */ -const char* vio_description( Vio* vio); - -/* Return the type of the connection */ - enum enum_vio_type vio_type(Vio* vio); - -/* set timeout */ -void vio_read_timeout(Vio *vio, uint seconds); -void vio_write_timeout(Vio *vio, uint seconds); - -/* Return last error number */ -int vio_errno(Vio *vio); - -/* Get socket number */ -my_socket vio_fd(Vio *vio); - -/* - * Remote peer's address and name in text form. - */ -my_bool vio_peer_addr(Vio * vio, char *buf); - -/* Remotes in_addr */ - -void vio_in_addr(Vio *vio, struct in_addr *in); - - /* Return 1 if there is data to be read */ -my_bool vio_poll_read(Vio *vio,uint timeout); -int vio_wait_or_timeout(Vio *vio, my_bool is_read, int timeout); - - -struct st_vio -{ - my_socket sd; /* my_socket - real or imaginary */ - HANDLE hPipe; - my_bool localhost; /* Are we from localhost? */ - int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */ - struct sockaddr_in local; /* Local internet address */ - struct sockaddr_in remote; /* Remote internet address */ - struct mysql_async_context *async_context; /* For non-blocking API */ - int write_timeout; - int read_timeout; - enum enum_vio_type type; /* Type of connection */ - char desc[30]; /* String description */ -#ifdef HAVE_OPENSSL - SSL *ssl; -#endif - uchar *cache; /* read-ahead cache to reduce reads (see CONC-79) */ - uchar *cache_pos; /* position of read-ahead cached data */ - size_t cache_size; /* <= VIO_CACHE_SIZE */ -}; - -#ifdef __cplusplus -} -#endif -#endif /* HAVE_VIO */ -#endif /* vio_violite_h_ */ diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 3ea50db2..be2d3899 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -8,7 +8,9 @@ ADD_DEFINITIONS(-D LIBMARIADB) ADD_DEFINITIONS(-D THREAD) SET(EXPORT_SYMBOLS + client_errors load_defaults + ma_cio_register_callback mariadb_connection mariadb_convert_string mariadb_deinitialize_ssl @@ -247,7 +249,8 @@ bmove.c bmove_upp.c my_charset.c hash.c -violite.c +ma_cio.c +ma_ssl.c net.c charset.c ma_time.c @@ -321,10 +324,15 @@ my_stmt.c my_loaddata.c my_stmt_codec.c client_plugin.c -my_auth.c -ma_secure.c +# built in plugins +${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c +${CMAKE_SOURCE_DIR}/plugins/builtin/cio_socket.c ) +IF(SSL_SOURCES) + SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ${SSL_SOURCES}) +ENDIF() + IF(WIN32) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/win-iconv) SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} diff --git a/libmariadb/client_plugin.c b/libmariadb/client_plugin.c index e8a5ee2b..ee6b3410 100644 --- a/libmariadb/client_plugin.c +++ b/libmariadb/client_plugin.c @@ -61,28 +61,12 @@ static MEM_ROOT mem_root; static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= { MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */ - 0, /* these two are taken by Connector/C */ + MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */ MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, - 0, + MYSQL_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION, MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION }; -typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; -extern auth_plugin_t old_password_client_plugin; -extern auth_plugin_t native_password_client_plugin; - -/* built in plugins: - These plugins are part of Connector/C, so no need to - load them -*/ - -struct st_mysql_client_plugin *mysql_client_builtins[]= -{ - (struct st_mysql_client_plugin *)&old_password_client_plugin, - (struct st_mysql_client_plugin *)&native_password_client_plugin, - 0 -}; - /* Loaded plugins are stored in a linked list. The list is append-only, the elements are added to the head (like in a stack). @@ -96,6 +80,26 @@ struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS]; static pthread_mutex_t LOCK_load_client_plugin; #endif +extern struct st_mysql_client_plugin old_password_client_plugin; +extern struct st_mysql_client_plugin native_password_client_plugin; + +extern MARIADB_CIO_PLUGIN cio_socket_plugin; +#ifdef HAVE_SSL +extern MARIADB_CIO_PLUGIN SSL_PLUGIN; +#endif + +struct st_mysql_client_plugin *mysql_client_builtins[]= +{ + (struct st_mysql_client_plugin *)&old_password_client_plugin, + (struct st_mysql_client_plugin *)&native_password_client_plugin, + (struct st_mysql_client_plugin *)&cio_socket_plugin, +#ifdef HAVE_SSL + (struct st_mysql_client_plugin *)&SSL_PLUGIN, +#endif + 0 +}; + + static int is_not_initialized(MYSQL *mysql, const char *name) { if (initialized) @@ -103,7 +107,7 @@ static int is_not_initialized(MYSQL *mysql, const char *name) my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), - name ? name : "unknown" , "not initialized"); + name, "not initialized"); return 1; } @@ -130,9 +134,12 @@ static struct st_mysql_client_plugin *find_plugin(const char *name, int type) for (p= plugin_list[type]; p; p= p->next) { - if (!name) - return p->plugin; - if (strcmp(p->plugin->name, name) == 0) + if (name) + { + if (strcmp(p->plugin->name, name) == 0) + return p->plugin; + } + else if (p->plugin->type == type) return p->plugin; } return NULL; @@ -207,10 +214,10 @@ err2: if (plugin->deinit) plugin->deinit(); err1: - if (dlhandle) - (void)dlclose(dlhandle); my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name, errmsg); + if (dlhandle) + (void)dlclose(dlhandle); return NULL; } @@ -282,7 +289,6 @@ int mysql_client_plugin_init() initialized= 1; pthread_mutex_lock(&LOCK_load_client_plugin); - for (builtin= mysql_client_builtins; *builtin; builtin++) add_plugin(&mysql, *builtin, 0, 0, unused); @@ -386,7 +392,7 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, mysql->options.extension->plugin_dir : (env_plugin_dir) ? env_plugin_dir : PLUGINDIR, "/", name, SO_EXT, NullS); - + /* Open new dll handle */ if (!(dlhandle= dlopen((const char *)dlpath, RTLD_NOW))) { @@ -403,7 +409,8 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, goto err; } - if (!(sym= dlsym(dlhandle, plugin_declarations_sym))) + + if (!(sym= (int *)dlsym(dlhandle, plugin_declarations_sym))) { errmsg= "not a plugin"; (void)dlclose(dlhandle); diff --git a/libmariadb/errmsg.c b/libmariadb/errmsg.c index fd51c998..43433031 100644 --- a/libmariadb/errmsg.c +++ b/libmariadb/errmsg.c @@ -55,25 +55,25 @@ const char *client_errors[]= const char *client_errors[]= { "Erro desconhecido do MySQL", - "Não pode criar 'UNIX socket' (%d)", - "Não pode se conectar ao servidor MySQL local através do 'socket' '%-.64s' (%d)", - "Não pode se conectar ao servidor MySQL em '%-.64s' (%d)", - "Não pode criar 'socket TCP/IP' (%d)", + "N�o pode criar 'UNIX socket' (%d)", + "N�o pode se conectar ao servidor MySQL local atrav�s do 'socket' '%-.64s' (%d)", + "N�o pode se conectar ao servidor MySQL em '%-.64s' (%d)", + "N�o pode criar 'socket TCP/IP' (%d)", "'Host' servidor MySQL '%-.64s' (%d) desconhecido", "Servidor MySQL desapareceu", - "Incompatibilidade de protocolos. Versão do Servidor: %d - Versão do Cliente: %d", - "Cliente do MySQL com falta de memória", - "Informação inválida de 'host'", + "Incompatibilidade de protocolos. Vers�o do Servidor: %d - Vers�o do Cliente: %d", + "Cliente do MySQL com falta de mem�ria", + "Informa��o inv�lida de 'host'", "Localhost via 'UNIX socket'", "%-.64s via 'TCP/IP'", - "Erro na negociação de acesso ao servidor", - "Conexão perdida com servidor MySQL durante 'query'", - "Comandos fora de sincronismo. Você não pode executar este comando agora", + "Erro na negocia��o de acesso ao servidor", + "Conex�o perdida com servidor MySQL durante 'query'", + "Comandos fora de sincronismo. Voc� n�o pode executar este comando agora", "%-.64s via 'named pipe'", - "Não pode esperar pelo 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", - "Não pode abrir 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", - "Não pode estabelecer o estado do 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", - "Não pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)", + "N�o pode esperar pelo 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", + "N�o pode abrir 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", + "N�o pode estabelecer o estado do 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", + "N�o pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)", "Obteve pacote maior do que 'max_allowed_packet'" }; @@ -117,8 +117,8 @@ const char *client_errors[]= /* 2034 */ "", /* 2035 */ "", /* 2036 */ "Buffer type is not supported", -/* 2037 */ "", -/* 2038 */ "", +/* 2037 */ "Shared memory: %-.64s", +/* 2038 */ "Shared memory connection failed during %s. (%lu)", /* 2039 */ "", /* 2040 */ "", /* 2041 */ "", @@ -145,6 +145,12 @@ const char *client_errors[]= }; #endif +const char *mariadb_client_errors[] = +{ + /* 5000 */ "Creating an event failed (Errorcode: %d)", + /* 5001 */ "Bind to local interface '-.%64s' failed (Errorcode: %d)", + "" +}; void init_client_errs(void) { diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index 8fd87ba7..b32990c8 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -62,15 +62,11 @@ #define INADDR_NONE -1 #endif #include -#include -#ifdef HAVE_OPENSSL -#include -#endif +#include #ifndef _WIN32 #include #endif #include -#include #define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) @@ -91,6 +87,8 @@ extern const CHARSET_INFO * mysql_find_charset_name(const char * const name); extern int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, const char *data_plugin, const char *db); +extern LIST *cio_callback; + /* prepare statement methods from my_stmt.c */ extern my_bool mthd_supported_buffer_type(enum enum_field_types type); extern my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt); @@ -130,216 +128,11 @@ static void mysql_close_memory(MYSQL *mysql); void read_user_name(char *name); static void append_wild(char *to,char *end,const char *wild); static my_bool mysql_reconnect(MYSQL *mysql); -static sig_handler pipe_sig_handler(int sig); static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length); extern int mysql_client_plugin_init(); extern void mysql_client_plugin_deinit(); -/* - Let the user specify that we don't want SIGPIPE; This doesn't however work - with threaded applications as we can have multiple read in progress. -*/ - -#if !defined(_WIN32) && defined(SIGPIPE) && !defined(THREAD) -#define init_sigpipe_variables sig_return old_signal_handler=(sig_return) 0; -#define set_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE,pipe_sig_handler) -#define reset_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) signal(SIGPIPE,old_signal_handler); -#else -#define init_sigpipe_variables -#define set_sigpipe(mysql) -#define reset_sigpipe(mysql) -#endif - - - -/**************************************************************************** -* A modified version of connect(). connect2() allows you to specify -* a timeout value, in seconds, that we should wait until we -* derermine we can't connect to a particular host. If timeout is 0, -* connect2() will behave exactly like connect(). -* -* Base version coded by Steve Bernacki, Jr. -*****************************************************************************/ - -int socket_block(my_socket s,my_bool blocked) -{ -#ifdef _WIN32 - unsigned long socket_blocked= blocked ? 0 : 1; - return ioctlsocket(s, FIONBIO, &socket_blocked); -#else - int flags= fcntl(s, F_GETFL, 0); - if (blocked) - flags&= ~O_NONBLOCK; - else - flags|= O_NONBLOCK; - return fcntl(s, F_SETFL, flags); -#endif -} - -static int connect2(my_socket s, const struct sockaddr *name, size_t namelen, - uint timeout) -{ - int res, s_err; - socklen_t s_err_size = sizeof(uint); -#ifndef _WIN32 - struct pollfd poll_fd; -#else - FD_SET sfds, efds; - struct timeval tv; -#endif - - if (!timeout) - return connect(s, (struct sockaddr*) name, (int)namelen); - - /* set socket to non blocking */ - if (socket_block(s, 0) == SOCKET_ERROR) - return -1; - - res= connect(s, (struct sockaddr*) name, (int)namelen); - if (res == 0) - return res; - -#ifdef _WIN32 - if (GetLastError() != WSAEWOULDBLOCK && - GetLastError() != WSAEINPROGRESS) -#else - if (errno != EINPROGRESS) -#endif - return -1; -#ifndef _WIN32 - memset(&poll_fd, 0, sizeof(struct pollfd)); - poll_fd.events= POLLOUT | POLLERR; - poll_fd.fd= s; - - /* connection timeout in milliseconds */ - res= poll(&poll_fd, 1, timeout); - - switch(res) - { - /* Error= - 1, timeout = 0 */ - case -1: - break; - case 0: - errno= ETIMEDOUT; - break; - } -#else - FD_ZERO(&sfds); - FD_ZERO(&efds); - FD_SET(s, &sfds); - FD_SET(s, &efds); - - memset(&tv, 0, sizeof(struct timeval)); - tv.tv_sec= timeout; - - res= select((int)s+1, NULL, &sfds, &efds, &tv); - if (res < 1) - return -1; -#endif - - s_err=0; - if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0) - return(-1); - - if (s_err) - { /* getsockopt could succeed */ - errno = s_err; - return(-1); /* but return an error... */ - } - return (0); /* ok */ -} - -static int -connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd, - const struct sockaddr *name, uint namelen) -{ - int vio_timeout= (mysql->options.connect_timeout >= 0) ? - mysql->options.connect_timeout * 1000 : -1; - - if (mysql->options.extension && mysql->options.extension->async_context && - mysql->options.extension->async_context->active) - { - my_bool old_mode; - vio_blocking(net->vio, FALSE, &old_mode); - return my_connect_async(mysql->options.extension->async_context, fd, - name, namelen, vio_timeout); - } - - return connect2(fd, name, namelen, vio_timeout); -} -/* -** Create a named pipe connection -*/ - -#ifdef _WIN32 - -HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host, - char **arg_unix_socket) -{ - HANDLE hPipe=INVALID_HANDLE_VALUE; - char szPipeName [ 257 ]; - DWORD dwMode; - int i; - my_bool testing_named_pipes=0; - char *host= *arg_host, *unix_socket= *arg_unix_socket; - - if ( ! unix_socket || (unix_socket)[0] == 0x00) - unix_socket = mysql_unix_port; - if (!host || !strcmp(host,LOCAL_HOST)) - host=LOCAL_HOST_NAMEDPIPE; - - sprintf( szPipeName, "\\\\%s\\pipe\\%s", host, unix_socket); - DBUG_PRINT("info",("Server name: '%s'. Named Pipe: %s", - host, unix_socket)); - - for (i=0 ; i < 100 ; i++) /* Don't retry forever */ - { - if ((hPipe = CreateFile(szPipeName, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL )) != INVALID_HANDLE_VALUE) - break; - if (GetLastError() != ERROR_PIPE_BUSY) - { - net->last_errno=CR_NAMEDPIPEOPEN_ERROR; - sprintf(net->last_error,ER(net->last_errno),host, unix_socket, - (ulong) GetLastError()); - return INVALID_HANDLE_VALUE; - } - /* wait for for an other instance */ - if (! WaitNamedPipe(szPipeName, connect_timeout) ) - { - net->last_errno=CR_NAMEDPIPEWAIT_ERROR; - sprintf(net->last_error,ER(net->last_errno),host, unix_socket, - (ulong) GetLastError()); - return INVALID_HANDLE_VALUE; - } - } - if (hPipe == INVALID_HANDLE_VALUE) - { - net->last_errno=CR_NAMEDPIPEOPEN_ERROR; - sprintf(net->last_error,ER(net->last_errno),host, unix_socket, - (ulong) GetLastError()); - return INVALID_HANDLE_VALUE; - } - dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; - if ( !SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL) ) - { - CloseHandle( hPipe ); - net->last_errno=CR_NAMEDPIPESETSTATE_ERROR; - sprintf(net->last_error,ER(net->last_errno),host, unix_socket, - (ulong) GetLastError()); - return INVALID_HANDLE_VALUE; - } - *arg_host=host ; *arg_unix_socket=unix_socket; /* connect arg */ - return (hPipe); -} -#endif - /* net_get_error */ void net_get_error(char *buf, size_t buf_len, char *error, size_t error_len, @@ -382,14 +175,10 @@ net_safe_read(MYSQL *mysql) { NET *net= &mysql->net; ulong len=0; - init_sigpipe_variables restart: - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); - if (net->vio != 0) + if (net->cio != 0) len=my_net_read(net); - reset_sigpipe(mysql); if (len == packet_error || len == 0) { @@ -560,15 +349,12 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, { NET *net= &mysql->net; int result= -1; - init_sigpipe_variables DBUG_ENTER("mthd_my_send_command"); DBUG_PRINT("info", ("server_command: %d packet_size: %u", command, length)); - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); - if (mysql->net.vio == 0) + if (mysql->net.cio == 0) { /* Do reconnect if possible */ if (mysql_reconnect(mysql)) { @@ -617,7 +403,6 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, DBUG_PRINT("info", ("packet_length=%llu", mysql->packet_length)); } end: - reset_sigpipe(mysql); DBUG_RETURN(result); } @@ -782,13 +567,10 @@ static void end_server(MYSQL *mysql) { DBUG_ENTER("end_server"); - if (mysql->net.vio != 0) + if (mysql->net.cio != 0) { - init_sigpipe_variables - set_sigpipe(mysql); - vio_delete(mysql->net.vio); - reset_sigpipe(mysql); - mysql->net.vio= 0; /* Marker */ + ma_cio_close(mysql->net.cio); + mysql->net.cio= 0; /* Marker */ } net_end(&mysql->net); free_old_query(mysql); @@ -845,7 +627,7 @@ static const char *default_options[]= "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name", "multi-results", "multi-statements", "multi-queries", "secure-auth", "report-data-truncation", "plugin-dir", "default-auth", "database-type", - "ssl-fp", "ssl-fp-list", + "ssl-fp", "ssl-fp-list", "bind-address", NULL }; @@ -860,7 +642,7 @@ enum option_val OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name, OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth, OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, OPT_db_type, - OPT_ssl_fp, OPT_ssl_fp_list + OPT_ssl_fp, OPT_ssl_fp_list, OPT_bind_address }; #define CHECK_OPT_EXTENSION_SET(OPTS)\ @@ -869,13 +651,15 @@ enum option_val my_malloc(sizeof(struct st_mysql_options_extension), \ MYF(MY_WME | MY_ZEROFILL)); \ -#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL, IS_STRING) \ - CHECK_OPT_EXTENSION_SET(OPTS) \ - if (IS_STRING) { \ - my_free((OPTS)->extension->KEY); \ - (OPTS)->extension->KEY= my_strdup((char *)(VAL), MYF(MY_WME)); \ - } else \ - (OPTS)->extension->KEY= (VAL); +#define OPT_SET_EXTENDED_VALUE_STR(OPTS, KEY, VAL) \ + CHECK_OPT_EXTENSION_SET(OPTS) \ + my_free((gptr)(OPTS)->extension->KEY);\ + (OPTS)->extension->KEY= my_strdup((char *)(VAL), MYF(MY_WME)) + +#define OPT_SET_EXTENDED_VALUE_INT(OPTS, KEY, VAL) \ + CHECK_OPT_EXTENSION_SET(OPTS) \ ++ (OPTS)->extension->KEY= (VAL) + static TYPELIB option_types={array_elements(default_options)-1, "options",default_options}; @@ -993,7 +777,7 @@ static void mysql_read_default_options(struct st_mysql_options *options, case OPT_return_found_rows: options->client_flag|=CLIENT_FOUND_ROWS; break; -#ifdef HAVE_OPENSSL +#ifdef HAVE_SSL case OPT_ssl_key: my_free(options->ssl_key); options->ssl_key = my_strdup(opt_arg, MYF(MY_WME)); @@ -1013,10 +797,10 @@ static void mysql_read_default_options(struct st_mysql_options *options, case OPT_ssl_cipher: break; case OPT_ssl_fp: - OPT_SET_EXTENDED_VALUE(options, ssl_fp, opt_arg, 1); + OPT_SET_EXTENDED_VALUE_STR(options, ssl_fp, opt_arg); break; case OPT_ssl_fp_list: - OPT_SET_EXTENDED_VALUE(options, ssl_fp_list, opt_arg, 1); + OPT_SET_EXTENDED_VALUE_STR(options, ssl_fp_list, opt_arg); break; #else case OPT_ssl_key: @@ -1027,7 +811,7 @@ static void mysql_read_default_options(struct st_mysql_options *options, case OPT_ssl_fp: case OPT_ssl_fp_list: break; -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_SSL */ case OPT_charset_dir: my_free(options->charset_dir); options->charset_dir = my_strdup(opt_arg, MYF(MY_WME)); @@ -1088,12 +872,16 @@ static void mysql_read_default_options(struct st_mysql_options *options, if (strlen(opt_arg) >= FN_REFLEN) opt_arg[FN_REFLEN]= 0; if (!my_realpath(directory, opt_arg, 0)) - OPT_SET_EXTENDED_VALUE(options, plugin_dir, convert_dirname(directory), 1); + OPT_SET_EXTENDED_VALUE_STR(options, plugin_dir, convert_dirname(directory)); } break; case OPT_default_auth: - OPT_SET_EXTENDED_VALUE(options, default_auth, opt_arg, 1); + OPT_SET_EXTENDED_VALUE_STR(options, default_auth, opt_arg); break; + case OPT_bind_address: + my_free(options->bind_address); + options->bind_address= my_strdup(opt_arg, MYF(MY_WME)); + break; default: DBUG_PRINT("warning",("unknown option: %s",option[0])); } @@ -1346,7 +1134,7 @@ mysql_init(MYSQL *mysql) if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL)))) return 0; mysql->free_me=1; - mysql->net.vio= 0; + mysql->net.cio= 0; } else bzero((char*) (mysql),sizeof(*(mysql))); @@ -1391,10 +1179,10 @@ mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, const char * STDCALL mysql_get_ssl_cipher(MYSQL *mysql) { -#ifdef HAVE_OPENSSL - if (mysql->net.vio && mysql->net.vio->ssl) +#ifdef HAVE_SSL + if (mysql->net.cio && mysql->net.cio->cssl) { - return SSL_get_cipher_name(mysql->net.vio->ssl); + return ma_cio_ssl_cipher(mysql->net.cio->cssl); } #endif return(NULL); @@ -1516,18 +1304,12 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, char buff[NAME_LEN+USERNAME_LENGTH+100]; char *end, *end_pkt, *host_info, *charset_name= NULL; - my_socket sock; + MA_CIO_CINFO cinfo= {NULL, NULL, 0, -1, NULL}; + MARIADB_CIO *cio= NULL; char *scramble_data; const char *scramble_plugin; uint pkt_length, scramble_len, pkt_scramble_len= 0; NET *net= &mysql->net; -#ifdef _WIN32 - HANDLE hPipe=INVALID_HANDLE_VALUE; -#endif -#ifdef HAVE_SYS_UN_H - struct sockaddr_un UNIXaddr; -#endif - init_sigpipe_variables DBUG_ENTER("mysql_real_connect"); DBUG_PRINT("enter",("host: %s db: %s user: %s", @@ -1537,15 +1319,12 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, ma_set_connect_attrs(mysql); - if (net->vio) /* check if we are already connected */ + if (net->cio) /* check if we are already connected */ { SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0); DBUG_RETURN(NULL); } - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); - /* use default options */ if (mysql->options.my_cnf_file || mysql->options.my_cnf_group) { @@ -1583,192 +1362,75 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, mysql->reconnect=1; */ mysql->server_status=SERVER_STATUS_AUTOCOMMIT; + + /* try to connect via cio_init */ + cinfo.host= host; + cinfo.unix_socket= unix_socket; + cinfo.port= port; + cinfo.mysql= mysql; + /* ** Grab a socket and connect it to the server */ - +#ifndef _WIN32 #if defined(HAVE_SYS_UN_H) if ((!host || strcmp(host,LOCAL_HOST) == 0) && mysql->options.protocol != MYSQL_PROTOCOL_TCP && (unix_socket || mysql_unix_port)) { - host=LOCAL_HOST; - if (!unix_socket) - unix_socket=mysql_unix_port; - host_info=(char*) ER(CR_LOCALHOST_CONNECTION); - DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket)); - if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR) - { - net->last_errno=CR_SOCKET_CREATE_ERROR; - sprintf(net->last_error,ER(net->last_errno),socket_errno); - goto error; - } - net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE); - bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); - UNIXaddr.sun_family = AF_UNIX; - strmov(UNIXaddr.sun_path, unix_socket); - if (connect_sync_or_async(mysql, net, sock, - (struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr))) - { - DBUG_PRINT("error",("Got error %d on connect to local server",socket_errno)); - my_set_error(mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONNECTION_ERROR), - unix_socket, socket_errno); - goto error; - } - if (socket_block(sock, 1) == SOCKET_ERROR) - goto error; + cinfo.host= LOCAL_HOST; + cinfo.unix_socket= (unix_socket) ? unix_socket : mysql_unix_port; + cinfo.type= CIO_TYPE_UNIXSOCKET; + sprintf(host_info=buff,ER(CR_LOCALHOST_CONNECTION),cinfo.host); } else -#elif defined(_WIN32) +#endif +#else + /* named pipe */ + if ((unix_socket || + (!host && is_NT()) || + (host && strcmp(host,LOCAL_HOST_NAMEDPIPE) == 0) || + mysql->options.named_pipe || + !have_tcpip) && + mysql->options.protocol != MYSQL_PROTOCOL_TCP) { - if ((unix_socket || - (!host && is_NT()) || - (host && strcmp(host,LOCAL_HOST_NAMEDPIPE) == 0) || - mysql->options.named_pipe || - !have_tcpip) && - mysql->options.protocol != MYSQL_PROTOCOL_TCP) - { - sock=0; - if ((hPipe=create_named_pipe(net, mysql->options.connect_timeout, - (char**) &host, (char**) &unix_socket)) == - INVALID_HANDLE_VALUE) - { - DBUG_PRINT("error", - ("host: '%s' socket: '%s' named_pipe: %d have_tcpip: %d", - host ? host : "", - unix_socket ? unix_socket : "", - (int) mysql->options.named_pipe, - (int) have_tcpip)); - if (mysql->options.named_pipe || - (host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) || - (unix_socket && !strcmp(unix_socket,MYSQL_NAMEDPIPE))) - goto error; /* User only requested named pipes */ - /* Try also with TCP/IP */ - } - else - { - net->vio=vio_new_win32pipe(hPipe); - sprintf(host_info=buff, ER(CR_NAMEDPIPE_CONNECTION), host, - unix_socket); - } - } + cinfo.type= CIO_TYPE_NAMEDPIPE; + sprintf(host_info=buff,ER(CR_NAMEDPIPE_CONNECTION),cinfo.host); } - if (hPipe == INVALID_HANDLE_VALUE) + else #endif { - struct addrinfo hints, *save_res= 0, *res= 0; - char server_port[NI_MAXSERV]; - int gai_rc; - int rc; - - unix_socket=0; /* This is not used */ + cinfo.unix_socket=0; /* This is not used */ if (!port) port=mysql_port; if (!host) host=LOCAL_HOST; - sprintf(host_info=buff,ER(CR_TCP_CONNECTION),host); - bzero(&server_port, NI_MAXSERV); - - DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host,port)); - - my_snprintf(server_port, NI_MAXSERV, "%d", port); - - /* set hints for getaddrinfo */ - bzero(&hints, sizeof(hints)); - hints.ai_protocol= IPPROTO_TCP; /* TCP connections only */ - hints.ai_family= AF_UNSPEC; /* includes: IPv4, IPv6 or hostname */ - hints.ai_socktype= SOCK_STREAM; - - /* Get the address information for the server using getaddrinfo() */ - gai_rc= getaddrinfo(host, server_port, &hints, &res); - if (gai_rc != 0) - { - my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, - ER(CR_UNKNOWN_HOST), host, gai_rc); - goto error; - } - - /* res is a linked list of addresses. If connect to an address fails we will not return - an error, instead we will try the next address */ - for (save_res= res; save_res; save_res= save_res->ai_next) - { - sock= socket(save_res->ai_family, save_res->ai_socktype, - save_res->ai_protocol); - if (sock == SOCKET_ERROR) - /* we do error handling after for loop only for last call */ - continue; - if (!(net->vio= vio_new(sock, VIO_TYPE_TCPIP, FALSE))) - { - my_set_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); - closesocket(sock); - freeaddrinfo(res); - goto error; - } - rc= connect_sync_or_async(mysql, net, sock, - save_res->ai_addr, save_res->ai_addrlen); - if (!rc) - { - if (mysql->options.extension && mysql->options.extension->async_context && - mysql->options.extension->async_context->active) - break; - else if (socket_block(sock, 1) == SOCKET_ERROR) - { - closesocket(sock); - continue; - } - break; /* success! */ - } - vio_delete(mysql->net.vio); - mysql->net.vio= NULL; - } - - freeaddrinfo(res); - - if (sock == SOCKET_ERROR) - { - my_set_error(mysql, CR_IPSOCK_ERROR, SQLSTATE_UNKNOWN, ER(CR_IPSOCK_ERROR), - socket_errno); - goto error; - } - - /* last call to connect 2 failed */ - if (rc) - { - my_set_error(mysql, CR_CONN_HOST_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONN_HOST_ERROR), - host, socket_errno); - goto error; - } + cinfo.host= host; + cinfo.port= port; + cinfo.type= CIO_TYPE_SOCKET; + sprintf(host_info=buff,ER(CR_TCP_CONNECTION), cinfo.host); } + /* Initialize and load cio plugin */ + if (!(cio= ma_cio_init(&cinfo))) + goto error; - /* set timeouts */ - net->vio->read_timeout= mysql->options.read_timeout; - net->vio->write_timeout= mysql->options.write_timeout; - - if (mysql->options.extension && mysql->options.extension->async_context) - net->vio->async_context= mysql->options.extension->async_context; - - if (!net->vio || my_net_init(net, net->vio)) + /* try to connect */ + if (ma_cio_connect(cio, &cinfo) != 0) { - vio_delete(net->vio); - net->vio = 0; - SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + ma_cio_close(cio); goto error; } - vio_keepalive(net->vio,TRUE); + + if (my_net_init(net, cio)) + goto error; + + ma_cio_keepalive(net->cio); strmov(mysql->net.sqlstate, "00000"); - - /* set read timeout */ - if (mysql->options.read_timeout >= 0) - vio_read_timeout(net->vio, mysql->options.read_timeout); - - /* set write timeout */ - if (mysql->options.write_timeout >= 0) - vio_write_timeout(net->vio, mysql->options.read_timeout); /* Get version info */ mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */ - if (mysql->options.connect_timeout >= 0 && - vio_wait_or_timeout(net->vio, FALSE, mysql->options.connect_timeout * 1000) < 1) +/* + if (ma_cio_wait_io_or_timeout(net->cio, FALSE, 0) < 1) { my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, ER(CR_SERVER_LOST_EXTENDED), @@ -1776,6 +1438,7 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, errno); goto error; } + */ if ((pkt_length=net_safe_read(mysql)) == packet_error) { if (mysql->net.last_errno == CR_SERVER_LOST) @@ -1821,9 +1484,9 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, if (!my_multi_malloc(MYF(0), &mysql->host_info, (uint) strlen(host_info)+1, - &mysql->host, (uint) strlen(host)+1, - &mysql->unix_socket,unix_socket ? - (uint) strlen(unix_socket)+1 : (uint) 1, + &mysql->host, (uint) strlen(cinfo.host)+1, + &mysql->unix_socket, cinfo.unix_socket ? + (uint) strlen(cinfo.unix_socket)+1 : (uint) 1, &mysql->server_version, (uint) (end - (char*) net->read_pos), NullS) || !(mysql->user=my_strdup(user,MYF(0))) || @@ -1833,9 +1496,9 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, goto error; } strmov(mysql->host_info,host_info); - strmov(mysql->host,host); - if (unix_socket) - strmov(mysql->unix_socket,unix_socket); + strmov(mysql->host, cinfo.host); + if (cinfo.unix_socket) + strmov(mysql->unix_socket, cinfo.unix_socket); else mysql->unix_socket=0; mysql->port=port; @@ -1985,11 +1648,9 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, strmov(mysql->net.sqlstate, "00000"); DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); - reset_sigpipe(mysql); DBUG_RETURN(mysql); error: - reset_sigpipe(mysql); DBUG_PRINT("error",("message: %u (%s)",net->last_errno,net->last_error)); { /* Free alloced memory */ @@ -2002,7 +1663,6 @@ error: DBUG_RETURN(0); } - static my_bool mysql_reconnect(MYSQL *mysql) { MYSQL tmp_mysql; @@ -2025,13 +1685,13 @@ static my_bool mysql_reconnect(MYSQL *mysql) tmp_mysql.options.my_cnf_group= tmp_mysql.options.my_cnf_file= NULL; /* make sure that we reconnect with the same character set */ - if (!tmp_mysql.options.charset_name || + /* if (!tmp_mysql.options.charset_name || strcmp(tmp_mysql.options.charset_name, mysql->charset->csname)) { my_free(tmp_mysql.options.charset_name); tmp_mysql.options.charset_name= my_strdup(mysql->charset->csname, MYF(MY_WME)); } - +*/ tmp_mysql.reconnect= mysql->reconnect; if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, mysql->db, mysql->port, mysql->unix_socket, @@ -2252,7 +1912,7 @@ void my_set_error(MYSQL *mysql, void mysql_close_slow_part(MYSQL *mysql) { - if (mysql->net.vio) + if (mysql->net.cio) { free_old_query(mysql); mysql->status=MYSQL_STATUS_READY; /* Force command */ @@ -2288,7 +1948,7 @@ mysql_close(MYSQL *mysql) /* Clear pointers for better safety */ bzero((char*) &mysql->options,sizeof(mysql->options)); - mysql->net.vio= 0; + mysql->net.cio= 0; if (mysql->free_me) my_free(mysql); } @@ -2931,7 +2591,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) #else if (*(uint *)arg1 > MYSQL_PROTOCOL_SOCKET) #endif - DBUG_RETURN(-1); + goto end; mysql->options.protocol= *(uint *)arg1; break; case MYSQL_OPT_READ_TIMEOUT: @@ -2953,11 +2613,12 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg1; break; case MYSQL_PLUGIN_DIR: - OPT_SET_EXTENDED_VALUE(&mysql->options, plugin_dir, (char *)arg1, 1); + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, plugin_dir, (char *)arg1); break; case MYSQL_DEFAULT_AUTH: - OPT_SET_EXTENDED_VALUE(&mysql->options, default_auth, (char *)arg1, 1); + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, default_auth, (char *)arg1); break; + /* case MYSQL_DATABASE_DRIVER: { MARIADB_DB_PLUGIN *db_plugin; @@ -2981,6 +2642,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) } } break; + */ case MYSQL_OPT_NONBLOCK: if (mysql->options.extension && (ctxt = mysql->options.extension->async_context) != 0) @@ -2990,7 +2652,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) suspended (as the stack is then in use). */ if (ctxt->suspended) - DBUG_RETURN(1); + goto end; my_context_destroy(&ctxt->async_context); my_free(ctxt); } @@ -2998,7 +2660,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) my_malloc(sizeof(*ctxt), MYF(MY_ZEROFILL)))) { SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); - DBUG_RETURN(1); + goto end; } stacksize= 0; if (arg1) @@ -3008,7 +2670,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) if (my_context_init(&ctxt->async_context, stacksize)) { my_free(ctxt); - DBUG_RETURN(1); + goto end; } if (!mysql->options.extension) if(!(mysql->options.extension= (struct st_mysql_options_extension *) @@ -3016,11 +2678,11 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) MYF(MY_WME | MY_ZEROFILL)))) { SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); - DBUG_RETURN(1); + goto end; } mysql->options.extension->async_context= ctxt; - if (mysql->net.vio) - mysql->net.vio->async_context= ctxt; + if (mysql->net.cio) + mysql->net.cio->async_context= ctxt; break; case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: @@ -3050,16 +2712,10 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) mysql->options.ssl_cipher=my_strdup((char *)arg1,MYF(MY_WME)); break; case MYSQL_OPT_SSL_CRL: - OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crl, (char *)arg1, 1); + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1); break; case MYSQL_OPT_SSL_CRLPATH: - OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crlpath, (char *)arg1, 1); - break; - case MARIADB_OPT_SSL_FP: - OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp, (char *)arg1, 1); - break; - case MARIADB_OPT_SSL_FP_LIST: - OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp_list, (char *)arg1, 1); + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crlpath, (char *)arg1); break; case MYSQL_OPT_CONNECT_ATTR_DELETE: { @@ -3148,6 +2804,10 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) case MYSQL_SECURE_AUTH: mysql->options.secure_auth= *(my_bool *)arg1; break; + case MYSQL_OPT_BIND: + my_free(mysql->options.bind_address); + mysql->options.bind_address= my_strdup(arg1, MYF(MY_WME)); + break; default: va_end(ap); DBUG_RETURN(-1); @@ -3452,9 +3112,6 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)), mysql_unix_port = env; } mysql_debug(NullS); -#if defined(SIGPIPE) && !defined(THREAD) && !defined(_WIN32) - (void) signal(SIGPIPE,SIG_IGN); -#endif } #ifdef THREAD else @@ -3469,12 +3126,10 @@ void STDCALL mysql_server_end() { if (!mysql_client_init) return; -#ifdef HAVE_OPENSSL - my_ssl_end(); -#endif mysql_client_plugin_deinit(); + list_free(cio_callback, 0); if (my_init_done) my_end(0); mysql_client_init= 0; @@ -3550,9 +3205,13 @@ mysql_get_parameters(void) my_socket STDCALL mysql_get_socket(const MYSQL *mysql) { - if (mysql->net.vio) - return vio_fd(mysql->net.vio); - return MARIADB_INVALID_SOCKET; + my_socket sock; + if (mysql->net.cio) + { + ma_cio_get_handle(mysql->net.cio, &sock); + return sock; + } + return INVALID_SOCKET; } /* diff --git a/libmariadb/my_stmt.c b/libmariadb/my_stmt.c index 414be03f..a3225874 100644 --- a/libmariadb/my_stmt.c +++ b/libmariadb/my_stmt.c @@ -53,7 +53,7 @@ #include "mysql_version.h" #include "mysqld_error.h" #include "errmsg.h" -#include +#include #include #include #include @@ -988,7 +988,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) { DBUG_ENTER("mysql_stmt_close"); - if (stmt && stmt->mysql && stmt->mysql->net.vio) + if (stmt && stmt->mysql && stmt->mysql->net.cio) mysql_stmt_reset(stmt); net_stmt_close(stmt, 1); diff --git a/libmariadb/mysql_async.c b/libmariadb/mysql_async.c index e1424509..b384afe9 100644 --- a/libmariadb/mysql_async.c +++ b/libmariadb/mysql_async.c @@ -30,8 +30,9 @@ #include "ma_common.h" #endif #include "my_context.h" -#include "violite.h" +#include "ma_cio.h" #include "mysql_async.h" +#include #ifdef _WIN32 @@ -41,7 +42,7 @@ */ #define WIN_SET_NONBLOCKING(mysql) { \ my_bool old_mode; \ - if ((mysql)->net.vio) vio_blocking((mysql)->net.vio, FALSE, &old_mode); \ + if ((mysql)->net.cio) ma_cio_blocking((mysql)->net.cio, FALSE, &old_mode); \ } #else #define WIN_SET_NONBLOCKING(mysql) @@ -62,19 +63,18 @@ my_context_install_suspend_resume_hook(struct mysql_async_context *b, /* Asynchronous connect(); socket must already be set non-blocking. */ int -my_connect_async(struct mysql_async_context *b, my_socket fd, +my_connect_async(MARIADB_CIO *cio, const struct sockaddr *name, uint namelen, int vio_timeout) { int res; size_socket s_err_size; + struct mysql_async_context *b= cio->mysql->options.extension->async_context; + my_socket sock; + + ma_cio_get_handle(cio, &sock); /* Make the socket non-blocking. */ -#ifdef _WIN32 - ulong arg= 1; - ioctlsocket(fd, FIONBIO, (void *)&arg); -#else - fcntl(fd, F_SETFL, O_NONBLOCK); -#endif + ma_cio_blocking(cio, 0, 0); b->events_to_wait_for= 0; /* @@ -83,7 +83,7 @@ my_connect_async(struct mysql_async_context *b, my_socket fd, application context. The application will then resume us when the socket polls ready for write, indicating that the connection attempt completed. */ - res= connect(fd, name, namelen); + res= connect(sock, name, namelen); if (res != 0) { #ifdef _WIN32 @@ -113,7 +113,7 @@ my_connect_async(struct mysql_async_context *b, my_socket fd, return -1; s_err_size= sizeof(res); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*) &res, &s_err_size) != 0) + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*) &res, &s_err_size) != 0) return -1; if (res) { @@ -135,14 +135,15 @@ my_connect_async(struct mysql_async_context *b, my_socket fd, #endif ssize_t -my_recv_async(struct mysql_async_context *b, int fd, - unsigned char *buf, size_t size, int timeout) +my_recv_async(MARIADB_CIO *cio, const unsigned char *buf, size_t size, int timeout) { ssize_t res; - + struct mysql_async_context *b= cio->async_context; for (;;) { - res= recv(fd, buf, size, IF_WIN(0, MSG_DONTWAIT)); + /* todo: async */ + if (cio->methods->async_read) + res= cio->methods->async_read(cio, buf, size); if (res >= 0 || IS_BLOCKING_ERROR()) return res; b->events_to_wait_for= MYSQL_WAIT_READ; @@ -163,14 +164,15 @@ my_recv_async(struct mysql_async_context *b, int fd, ssize_t -my_send_async(struct mysql_async_context *b, int fd, - const unsigned char *buf, size_t size, int timeout) +my_send_async(MARIADB_CIO *cio, const unsigned char *buf, size_t size, int timeout) { ssize_t res; + struct mysql_async_context *b= cio->async_context; for (;;) { - res= send(fd, buf, size, IF_WIN(0, MSG_DONTWAIT)); + if (cio->methods->async_write) + res= cio->methods->async_write(cio, buf, size); if (res >= 0 || IS_BLOCKING_ERROR()) return res; b->events_to_wait_for= MYSQL_WAIT_WRITE; @@ -191,7 +193,7 @@ my_send_async(struct mysql_async_context *b, int fd, my_bool -my_io_wait_async(struct mysql_async_context *b, enum enum_vio_io_event event, +my_io_wait_async(struct mysql_async_context *b, enum enum_cio_io_event event, int timeout) { switch (event) @@ -221,9 +223,9 @@ my_io_wait_async(struct mysql_async_context *b, enum enum_vio_io_event event, } -#ifdef HAVE_OPENSSL +#ifdef HAVE_SSL_FIXME static my_bool -my_ssl_async_check_result(int res, struct mysql_async_context *b, SSL *ssl) +my_ssl_async_check_result(int res, struct mysql_async_context *b, MARIADB_SSL *cssl) { int ssl_err; b->events_to_wait_for= 0; @@ -496,18 +498,36 @@ MK_ASYNC_INTERNAL_BODY( int STDCALL mysql_real_query_start(int *ret, MYSQL *mysql, const char *stmt_str, unsigned long length) { -MK_ASYNC_START_BODY( - mysql_real_query, - mysql, + int res; + struct mysql_async_context *b; + struct mysql_real_query_params parms; + + b= mysql->options.extension->async_context; { WIN_SET_NONBLOCKING(mysql) parms.mysql= mysql; parms.stmt_str= stmt_str; parms.length= length; - }, - 1, - r_int, - /* Nothing */) + } + + b->active= 1; + res= my_context_spawn(&b->async_context, mysql_real_query_start_internal, &parms); + b->active= b->suspended= 0; + if (res > 0) + { + /* Suspended. */ + b->suspended= 1; + return b->events_to_wait_for; + } + if (res < 0) + { + set_mysql_error((mysql), CR_OUT_OF_MEMORY, unknown_sqlstate); + *ret= 1; + } + else + *ret= b->ret_result.r_int; + return 0; + } int STDCALL mysql_real_query_cont(int *ret, MYSQL *mysql, int ready_status) diff --git a/libmariadb/net.c b/libmariadb/net.c index ddf97321..26949a71 100644 --- a/libmariadb/net.c +++ b/libmariadb/net.c @@ -22,8 +22,10 @@ ** 3 byte length & 1 byte package-number. */ + #include -#include +#include +#include #include #include #include "mysql.h" @@ -31,6 +33,7 @@ #include #include #include +#include #ifndef _WIN32 #include #endif @@ -46,7 +49,7 @@ ulong max_allowed_packet=1024L * 1024L * 1024L; ulong net_read_timeout= NET_READ_TIMEOUT; ulong net_write_timeout= NET_WRITE_TIMEOUT; -ulong net_buffer_length=8192; /* Default length. Enlarged if necessary */ +ulong net_buffer_length= 8192; /* Default length. Enlarged if necessary */ #if !defined(_WIN32) && !defined(MSDOS) #include @@ -110,13 +113,13 @@ static int net_write_buff(NET *net,const char *packet, size_t len); /* Init with packet info */ -int my_net_init(NET *net, Vio* vio) +int my_net_init(NET *net, MARIADB_CIO* cio) { if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL)))) return 1; max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet); net->buff_end=net->buff+(net->max_packet=net_buffer_length); - net->vio = vio; + net->cio = cio; net->error=0; net->return_status=0; net->read_timeout=(uint) net_read_timeout; /* Timeout for read */ net->compress_pkt_nr= net->pkt_nr= 0; @@ -127,21 +130,18 @@ int my_net_init(NET *net, Vio* vio) net->where_b = net->remain_in_buf=0; net->last_errno=0; - if (vio != 0) /* If real connection */ + if (cio != 0) /* If real connection */ { - net->fd = vio_fd(vio); /* For perl DBI/DBD */ -#if defined(MYSQL_SERVER) && !defined(__WIN32) && !defined(__EMX__) && !defined(OS2) - if (!(test_flags & TEST_BLOCKING)) - vio_blocking(vio, FALSE, 0); -#endif - vio_fastsend(vio); + ma_cio_get_handle(cio, &net->fd); + ma_cio_blocking(cio, 1, 0); + ma_cio_fast_send(cio); } return 0; } void net_end(NET *net) { - my_free(net->buff); + my_free((gptr) net->buff); net->buff=0; } @@ -219,15 +219,17 @@ static my_bool net_check_socket_status(my_socket sock) void net_clear(NET *net) { + my_socket sock; DBUG_ENTER("net_clear"); + + ma_cio_get_handle(net->cio, &sock); /* see conc-71: we need to check the socket status first: if the socket is dead we set net->error, so net_flush will report an error */ - while (net_check_socket_status(net->vio->sd)) + while (net_check_socket_status(sock)) { - /* vio_read returns size_t. so casting to long is required to check for -1 */ - if ((long)vio_read(net->vio, (gptr)net->buff, (size_t) net->max_packet) <= 0) + if ((ssize_t)ma_cio_cache_read(net->cio, (gptr)net->buff, (size_t) net->max_packet) <= 0) { net->error= 2; DBUG_PRINT("info", ("socket disconnected")); @@ -384,12 +386,6 @@ net_real_write(NET *net,const char *packet,size_t len) { size_t length; char *pos,*end; - thr_alarm_t alarmed; -#if !defined(_WIN32) && !defined(__EMX__) && !defined(OS2) - ALARM alarm_buff; -#endif - uint retry_count=0; - my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); if (net->error == 2) @@ -426,79 +422,23 @@ net_real_write(NET *net,const char *packet,size_t len) } #endif /* HAVE_COMPRESS */ - alarmed=0; - pos=(char*) packet; end=pos+len; while (pos != end) { - if ((long) (length=vio_write(net->vio,pos,(size_t) (end-pos))) <= 0) + if ((ssize_t) (length=ma_cio_write(net->cio,pos,(size_t) (end-pos))) <= 0) { - my_bool interrupted = vio_should_retry(net->vio); -#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) - if ((interrupted || length==0) && !thr_alarm_in_use(&alarmed)) - { - if (!thr_alarm(&alarmed,(uint) net_write_timeout,&alarm_buff)) - { /* Always true for client */ - if (!vio_is_blocking(net->vio)) - { - while (vio_blocking(net->vio, TRUE, 0) < 0) - { - if (vio_should_retry(net->vio) && retry_count++ < RETRY_COUNT) - continue; -#ifdef EXTRA_DEBUG - fprintf(stderr, - "%s: my_net_write: fcntl returned error %d, aborting thread\n", - my_progname,vio_errno(net->vio)); -#endif /* EXTRA_DEBUG */ - net->error=2; /* Close socket */ - net->last_errno= (interrupted ? - ER_NET_WRITE_INTERRUPTED : ER_NET_ERROR_ON_WRITE); - goto end; - } - } - retry_count=0; - continue; - } - } - else -#endif /* (!defined(_WIN32) && !defined(__EMX__)) */ - if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) && - interrupted) - { - if (retry_count++ < RETRY_COUNT) - continue; -#ifdef EXTRA_DEBUG - fprintf(stderr, "%s: write looped, aborting thread\n", - my_progname); -#endif /* EXTRA_DEBUG */ - } -#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) - if (vio_errno(net->vio) == SOCKET_EINTR) - { - DBUG_PRINT("warning",("Interrupted write. Retrying...")); - continue; - } -#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */ net->error=2; /* Close socket */ - net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED : - ER_NET_ERROR_ON_WRITE); - break; + net->last_errno= ER_NET_ERROR_ON_WRITE; + net->reading_or_writing=0; + DBUG_RETURN(1); } pos+=length; statistic_add(bytes_sent,length,&LOCK_bytes_sent); } -#ifndef _WIN32 - end: -#endif #ifdef HAVE_COMPRESS if (net->compress) - my_free((void *)packet); + my_free((char*) packet); #endif - if (thr_alarm_in_use(&alarmed)) - { - thr_end_alarm(&alarmed); - vio_blocking(net->vio, net_blocking, 0); - } net->reading_or_writing=0; DBUG_RETURN(((int) (pos != end))); } @@ -507,66 +447,18 @@ net_real_write(NET *net,const char *packet,size_t len) /***************************************************************************** ** Read something from server/clinet *****************************************************************************/ - -#ifdef MYSQL_SERVER - -/* - Help function to clear the commuication buffer when we get a too - big packet -*/ - -static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed, - ALARM *alarm_buff) -{ - uint retry_count=0; - if (!thr_alarm_in_use(alarmed)) - { - if (thr_alarm(alarmed,net->timeout,alarm_buff) || - (!vio_is_blocking(net->vio) && vio_blocking(net->vio,TRUE, 0) < 0)) - return; /* Can't setup, abort */ - } - while (remain > 0) - { - ulong length; - if ((int) (length=vio_read(net->vio,(char*) net->buff,remain)) <= 0L) - { - my_bool interrupted = vio_should_retry(net->vio); - if (!thr_got_alarm(alarmed) && interrupted) - { /* Probably in MIT threads */ - if (retry_count++ < RETRY_COUNT) - continue; - } - return; - } - remain -=(ulong) length; - statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); - } -} -#endif /* MYSQL_SERVER */ - - static ulong my_real_read(NET *net, size_t *complen) { uchar *pos; size_t length; - uint i,retry_count=0; + uint i; ulong len=packet_error; - thr_alarm_t alarmed; -#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER) - ALARM alarm_buff; -#endif - my_bool net_blocking=vio_is_blocking(net->vio); size_t remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : NET_HEADER_SIZE); *complen = 0; net->reading_or_writing=1; - thr_alarm_init(&alarmed); -#ifdef MYSQL_SERVER - if (net_blocking) - thr_alarm(&alarmed,net->timeout,&alarm_buff); -#endif /* MYSQL_SERVER */ pos = net->buff + net->where_b; /* net->packet -4 */ for (i=0 ; i < 2 ; i++) @@ -574,68 +466,8 @@ my_real_read(NET *net, size_t *complen) while (remain > 0) { /* First read is done with non blocking mode */ - if ((long) (length=vio_read(net->vio,(char*) pos,remain)) <= 0L) + if ((ssize_t) (length=ma_cio_cache_read(net->cio,(char*) pos,remain)) <= 0L) { - my_bool interrupted = vio_should_retry(net->vio); - - DBUG_PRINT("info",("vio_read returned %d, errno: %d", - length, vio_errno(net->vio))); -#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER) - /* - We got an error that there was no data on the socket. We now set up - an alarm to not 'read forever', change the socket to non blocking - mode and try again - */ - if ((interrupted || length == 0) && !thr_alarm_in_use(&alarmed)) - { - if (!thr_alarm(&alarmed,net->read_timeout,&alarm_buff)) /* Don't wait too long */ - { - if (!vio_is_blocking(net->vio)) - { - while (vio_blocking(net->vio,TRUE, 0) < 0) - { - if (vio_should_retry(net->vio) && - retry_count++ < RETRY_COUNT) - continue; - DBUG_PRINT("error", - ("fcntl returned error %d, aborting thread", - vio_errno(net->vio))); -#ifdef EXTRA_DEBUG - fprintf(stderr, - "%s: read: fcntl returned error %d, aborting thread\n", - my_progname,vio_errno(net->vio)); -#endif /* EXTRA_DEBUG */ - len= packet_error; - net->error=2; /* Close socket */ -#ifdef MYSQL_SERVER - net->last_errno=ER_NET_FCNTL_ERROR; -#endif - goto end; - } - } - retry_count=0; - continue; - } - } -#endif /* (!defined(_WIN32) && !defined(__EMX__)) || defined(MYSQL_SERVER) */ - if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) && - interrupted) - { /* Probably in MIT threads */ - if (retry_count++ < RETRY_COUNT) - continue; -#ifdef EXTRA_DEBUG - fprintf(stderr, "%s: read looped with error %d, aborting thread\n", - my_progname,vio_errno(net->vio)); -#endif /* EXTRA_DEBUG */ - } -#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) - if (vio_should_retry(net->vio)) - { - DBUG_PRINT("warning",("Interrupted read. Retrying...")); - continue; - } -#endif - DBUG_PRINT("error",("Couldn't read packet: remain: %d errno: %d length: %d alarmed: %d", remain,vio_errno(net->vio),length,alarmed)); len= packet_error; net->error=2; /* Close socket */ goto end; @@ -662,9 +494,6 @@ my_real_read(NET *net, size_t *complen) #endif } len= packet_error; -#ifdef MYSQL_SERVER - net->last_errno=ER_NET_PACKETS_OUT_OF_ORDER; -#endif goto end; } net->compress_pkt_nr= ++net->pkt_nr; @@ -685,10 +514,6 @@ my_real_read(NET *net, size_t *complen) { if (net_realloc(net,helping)) { -#ifdef MYSQL_SERVER - if (i == 1) - my_net_skip_rest(net, len, &alarmed, &alarm_buff); -#endif len= packet_error; /* Return error */ goto end; } @@ -699,11 +524,6 @@ my_real_read(NET *net, size_t *complen) } end: - if (thr_alarm_in_use(&alarmed)) - { - thr_end_alarm(&alarmed); - vio_blocking(net->vio, net_blocking, 0); - } net->reading_or_writing=0; return(len); } diff --git a/libmariadb/violite.c b/libmariadb/violite.c deleted file mode 100644 index 1fbdcc33..00000000 --- a/libmariadb/violite.c +++ /dev/null @@ -1,713 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA */ - -/* - Note that we can't have assertion on file descriptors; The reason for - this is that during mysql shutdown, another thread can close a file - we are working on. In this case we should just return read errors from - the file descriptior. -*/ - -#ifndef HAVE_VIO /* is Vio suppored by the Vio lib ? */ - -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_POLL -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif - -#ifdef HAVE_OPENSSL -#include -#endif - -#ifdef _WIN32 -#define socklen_t int -#pragma comment (lib, "ws2_32") -#endif - -#if !defined(MSDOS) && !defined(_WIN32) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) && !defined(__FreeBSD__) -#include -#include -#if !defined(alpha_linux_port) -#include -#endif -#endif - -#if defined(__EMX__) || defined(OS2) -#define ioctlsocket ioctl -#endif /* defined(__EMX__) */ - -#if defined(MSDOS) || defined(_WIN32) -#define O_NONBLOCK 1 /* For emulation of fcntl() */ -#endif -#ifndef EWOULDBLOCK -#define SOCKET_EWOULDBLOCK SOCKET_EAGAIN -#endif - -#include -#include - -#ifdef _WIN32 -#define ma_get_error() WSAGetLastError() -#else -#define ma_get_error() errno -#endif - -typedef void *vio_ptr; -typedef char *vio_cstring; - -/* - * Helper to fill most of the Vio* with defaults. - */ - -void vio_reset(Vio* vio, enum enum_vio_type type, - my_socket sd, HANDLE hPipe, - my_bool localhost) -{ - uchar *save_cache= vio->cache; - int save_timeouts[2]= {vio->read_timeout, vio->write_timeout}; - bzero((char*) vio, sizeof(*vio)); - vio->type= type; - vio->sd= sd; - vio->hPipe= hPipe; - vio->localhost= localhost; - /* do not clear cache */ - vio->cache= vio->cache_pos= save_cache; - vio->cache_size= 0; - vio->read_timeout= save_timeouts[0]; - vio->write_timeout= save_timeouts[1]; -} - -void vio_timeout(Vio *vio, int type, uint timeval) -{ -#ifdef _WIN32 - uint timeout= timeval; /* milli secs */ -#else - struct timeval timeout; - timeout.tv_sec= timeval; - timeout.tv_usec= (timeval % 1000) * 1000; -#endif - - if (setsockopt(vio->sd, SOL_SOCKET, type, -#ifdef _WIN32 - (const char *)&timeout, -#else - (const void *)&timeout, -#endif - sizeof(timeout))) - { - DBUG_PRINT("error", ("setsockopt failed. Errno: %d", errno)); - } -} - -void vio_read_timeout(Vio *vio, uint timeout) -{ - vio->read_timeout= (timeout >= 0) ? timeout * 1000 : -1; - vio_timeout(vio, SO_RCVTIMEO, vio->read_timeout); -} - -void vio_write_timeout(Vio *vio, uint timeout) -{ - vio->write_timeout= (timeout >= 0) ? timeout * 1000 : -1; - vio_timeout(vio, SO_SNDTIMEO, vio->write_timeout); -} - -/* Open the socket or TCP/IP connection and read the fnctl() status */ - -Vio *vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost) -{ - Vio *vio; - DBUG_ENTER("vio_new"); - DBUG_PRINT("enter", ("sd=%d", sd)); - if ((vio = (Vio*) my_malloc(sizeof(*vio),MYF(MY_WME)))) - { - vio_reset(vio, type, sd, 0, localhost); - sprintf(vio->desc, - (vio->type == VIO_TYPE_SOCKET ? "socket (%d)" : "TCP/IP (%d)"), - vio->sd); -#if !defined(__WIN32) && !defined(__EMX__) && !defined(OS2) -#if !defined(NO_FCNTL_NONBLOCK) - vio->fcntl_mode = fcntl(sd, F_GETFL); -#elif defined(HAVE_SYS_IOCTL_H) /* hpux */ - /* Non blocking sockets doesn't work good on HPUX 11.0 */ - (void) ioctl(sd,FIOSNBIO,0); -#endif -#else /* !defined(_WIN32) && !defined(__EMX__) */ - { - /* set to blocking mode by default */ - ulong arg=0, r; - r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg/*, sizeof(arg)*/); - } -#endif - } - if (!(vio->cache= my_malloc(VIO_CACHE_SIZE, MYF(MY_WME)))) - { - my_free(vio); - vio= NULL; - } - vio->cache_size= 0; - vio->cache_pos= vio->cache; - DBUG_RETURN(vio); -} - - -#ifdef _WIN32 - -Vio *vio_new_win32pipe(HANDLE hPipe) -{ - Vio *vio; - DBUG_ENTER("vio_new_handle"); - if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_ZEROFILL)))) - { - vio_reset(vio, VIO_TYPE_NAMEDPIPE, 0, hPipe, TRUE); - strmov(vio->desc, "named pipe"); - } - DBUG_RETURN(vio); -} - -#endif - -void vio_delete(Vio * vio) -{ - /* It must be safe to delete null pointers. */ - /* This matches the semantics of C++'s delete operator. */ - if (vio) - { - if (vio->type != VIO_CLOSED) - vio_close(vio); - my_free(vio->cache); - my_free(vio); - } -} - -int vio_errno(Vio *vio __attribute__((unused))) -{ - return socket_errno; /* On Win32 this mapped to WSAGetLastError() */ -} - -int vio_wait_or_timeout(Vio *vio, my_bool is_read, int timeout) -{ - int rc; -#ifndef _WIN32 - struct pollfd p_fd; -#else - struct timeval tv= {0,0}; - fd_set fds, exc_fds; -#endif - - /* we don't support it via named pipes yet. - * maybe this could be handled via PeekNamedPipe somehow !? */ - if (vio->type == VIO_TYPE_NAMEDPIPE) - return 1; - - /* - Note that if zero timeout, then we will not block, so we do not need to - yield to calling application in the async case. - */ - if (timeout != 0 && vio->async_context && vio->async_context->active) - { - rc= my_io_wait_async(vio->async_context, - (is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE, - timeout); - return(rc); - } - else - { -#ifndef _WIN32 - p_fd.fd= vio->sd; - p_fd.events= (is_read) ? POLLIN : POLLOUT; - - do { - rc= poll(&p_fd, 1, timeout); - } while (rc == -1 || errno == EINTR); - - if (rc == 0) - errno= ETIMEDOUT; -#else - FD_ZERO(&fds); - FD_ZERO(&exc_fds); - - FD_SET(vio->sd, &fds); - FD_SET(vio->sd, &exc_fds); - - if (timeout >= 0) - { - tv.tv_sec= timeout / 1000; - tv.tv_usec= (timeout % 1000) * 1000; - } - - rc= select(0, (is_read) ? &fds : NULL, - (is_read) ? NULL : &fds, - &exc_fds, - (timeout >= 0) ? &tv : NULL); - if (rc == SOCKET_ERROR) - errno= WSAGetLastError(); - if (rc == 0) - errno= ETIMEDOUT; -#endif - } - return rc; -} - - -size_t vio_real_read(Vio *vio, gptr buf, size_t size) -{ - size_t r; - - switch(vio->type) { -#ifdef HAVE_OPENSSL - case VIO_TYPE_SSL: - return my_ssl_read(vio, (char *)buf, size); - break; -#endif -#ifdef _WIN32 - case VIO_TYPE_NAMEDPIPE: - { - DWORD length= 0; - if (!ReadFile(vio->hPipe, buf, (DWORD)size, &length, NULL)) - return -1; - return length; - } - break; -#endif - default: - if (vio->async_context && vio->async_context->active) - r= my_recv_async(vio->async_context, - vio->sd, - buf, size, vio->read_timeout); - else - { - if (vio->async_context) - { - /* - If switching from non-blocking to blocking API usage, set the socket - back to blocking mode. - */ - my_bool old_mode; - vio_blocking(vio, TRUE, &old_mode); - } -#ifndef _WIN32 - do { - r= read(vio->sd, buf, size); - } while (r == -1 && errno == EINTR); - - while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) - && vio->read_timeout > 0) - { - if (vio_wait_or_timeout(vio, TRUE, vio->write_timeout) < 1) - return 0; - do { - r= read(vio->sd, buf, size); - } while (r == -1 && errno == EINTR); - } -#else - { - WSABUF wsaData; - DWORD dwBytes = 0; - DWORD flags = 0; - - wsaData.len= size; - wsaData.buf= buf; - - if (WSARecv(vio->sd, &wsaData, 1, &dwBytes, &flags, NULL, NULL) == SOCKET_ERROR) - { - errno= WSAGetLastError(); - return 0; - } - r= (size_t)dwBytes; - } -#endif - } - break; - } - return r; -} - - -size_t vio_read(Vio * vio, gptr buf, size_t size) -{ - size_t r; - DBUG_ENTER("vio_read"); - DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size)); - - if (!vio->cache) - DBUG_RETURN(vio_real_read(vio, buf, size)); - - if (vio->cache + vio->cache_size > vio->cache_pos) - { - r= MIN(size, vio->cache + vio->cache_size - vio->cache_pos); - memcpy(buf, vio->cache_pos, r); - vio->cache_pos+= r; - } - else if (size >= VIO_CACHE_MIN_SIZE) - { - r= vio_real_read(vio, buf, size); - } - else - { - r= vio_real_read(vio, vio->cache, VIO_CACHE_SIZE); - if (r > 0) - { - if (size < r) - { - vio->cache_size= r; /* might be < VIO_CACHE_SIZE */ - vio->cache_pos= vio->cache + size; - r= size; - } - memcpy(buf, vio->cache, r); - } - } - -#ifndef DBUG_OFF - if ((size_t)r == -1) - { - DBUG_PRINT("vio_error", ("Got error %d during read",socket_errno)); - } -#endif /* DBUG_OFF */ - DBUG_PRINT("exit", ("%u", (uint)r)); - DBUG_RETURN(r); -} - -/* - Return data from the beginning of the receive queue without removing - that data from the queue. A subsequent receive call will return the same data. -*/ -my_bool vio_read_peek(Vio *vio, size_t *bytes) -{ -#ifdef _WIN32 - if (ioctlsocket(vio->sd, FIONREAD, (unsigned long*)bytes)) - return TRUE; -#else - char buffer[1024]; - ssize_t length; - - vio_blocking(vio, 0, 0); - length= recv(vio->sd, &buffer, sizeof(buffer), MSG_PEEK); - if (length < 0) - return TRUE; - *bytes= length; -#endif - return FALSE; -} - - -size_t vio_write(Vio * vio, const gptr buf, size_t size) -{ - size_t r; - DBUG_ENTER("vio_write"); - DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size)); -#ifdef HAVE_OPENSSL - if (vio->type == VIO_TYPE_SSL) - { - r= my_ssl_write(vio, (uchar *)buf, size); - DBUG_RETURN(r); - } -#endif -#ifdef _WIN32 - if ( vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; - if (!WriteFile(vio->hPipe, (char*) buf, (DWORD)size, &length, NULL)) - DBUG_RETURN(-1); - DBUG_RETURN(length); - } -#endif - if (vio->async_context && vio->async_context->active) - r= my_send_async(vio->async_context, vio->sd, buf, size, - vio->write_timeout); - else - { - if (vio->async_context) - { - /* - If switching from non-blocking to blocking API usage, set the socket - back to blocking mode. - */ - my_bool old_mode; - vio_blocking(vio, TRUE, &old_mode); - } -#ifndef _WIN32 - do { - r= send(vio->sd, buf, size, vio->write_timeout ? MSG_DONTWAIT : MSG_WAITALL); - } while (r == -1 && errno == EINTR); - - while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && - vio->write_timeout > 0) - { - if (vio_wait_or_timeout(vio, FALSE, vio->write_timeout) < 1) - return 0; - do { - r= send(vio->sd, buf, size, vio->write_timeout ? MSG_DONTWAIT : MSG_WAITALL); - } while (r == -1 && errno == EINTR); - } -#else - { - WSABUF wsaData; - DWORD dwBytes = 0; - - wsaData.len= size; - wsaData.buf= (char *)buf; - - if (WSASend(vio->sd, &wsaData, 1, &dwBytes, 0, NULL, NULL) == SOCKET_ERROR) - { - errno= WSAGetLastError(); - DBUG_RETURN(0); - } - r= (size_t)dwBytes; - } -#endif - } -#ifndef DBUG_OFF - if ((size_t)r == -1) - { - DBUG_PRINT("vio_error", ("Got error on write: %d",socket_errno)); - } -#endif /* DBUG_OFF */ - DBUG_PRINT("exit", ("%u", (uint)r)); - DBUG_RETURN(r); -} - - -int vio_blocking(Vio *vio, my_bool block, my_bool *previous_mode) -{ - int *sd_flags= &vio->fcntl_mode; - int save_flags= vio->fcntl_mode; - my_bool tmp; - my_socket sock= vio->sd; - - if (vio->type == VIO_TYPE_NAMEDPIPE) - return 0; - - if (!previous_mode) - previous_mode= &tmp; - -#ifdef _WIN32 - *previous_mode= (*sd_flags & O_NONBLOCK) != 0; - *sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK; - { - ulong arg= 1 - block; - if (ioctlsocket(sock, FIONBIO, (void *)&arg)) - { - vio->fcntl_mode= save_flags; - return(WSAGetLastError()); - } - } -#else -#if defined(O_NONBLOCK) - *previous_mode= (*sd_flags & O_NONBLOCK) != 0; - *sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK; -#elif defined(O_NDELAY) - *previous_mode= (*sd_flags & O_NODELAY) != 0; - *sd_flags = (block) ? *sd_flags & ~O_NODELAY : *sd_flags | O_NODELAY; -#elif defined(FNDELAY) - *previous_mode= (*sd_flags & O_FNDELAY) != 0; - *sd_flags = (block) ? *sd_flags & ~O_FNDELAY : *sd_flags | O_FNDELAY; -#else -#error socket blocking is not supported on this platform -#endif - if (fcntl(sock, F_SETFL, *sd_flags) == -1) - { - vio->fcntl_mode= save_flags; - return errno; - } -#endif - return 0; -} - -my_bool -vio_is_blocking(Vio * vio) -{ - my_bool r; - DBUG_ENTER("vio_is_blocking"); - r = !(vio->fcntl_mode & O_NONBLOCK); - DBUG_PRINT("exit", ("%d", (int) r)); - DBUG_RETURN(r); -} - - -int vio_fastsend(Vio * vio __attribute__((unused))) -{ - int r=0; - DBUG_ENTER("vio_fastsend"); - -#ifdef IPTOS_THROUGHPUT - { -#ifndef __EMX__ - int tos = IPTOS_THROUGHPUT; - if (!setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos))) -#endif /* !__EMX__ */ - { - int nodelay = 1; - if (setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY, (void *) &nodelay, - sizeof(nodelay))) { - DBUG_PRINT("warning", - ("Couldn't set socket option for fast send")); - r= -1; - } - } - } -#endif /* IPTOS_THROUGHPUT */ - DBUG_PRINT("exit", ("%d", r)); - DBUG_RETURN(r); -} - -int vio_keepalive(Vio* vio, my_bool set_keep_alive) -{ - int r=0; - uint opt = 0; - DBUG_ENTER("vio_keepalive"); - DBUG_PRINT("enter", ("sd=%d set_keep_alive=%d", vio->sd, (int) - set_keep_alive)); - if (vio->type != VIO_TYPE_NAMEDPIPE) - { - if (set_keep_alive) - opt = 1; - r = setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, - sizeof(opt)); - } - DBUG_RETURN(r); -} - - -my_bool -vio_should_retry(Vio * vio __attribute__((unused))) -{ - int en = socket_errno; - return en == SOCKET_EAGAIN || en == SOCKET_EINTR || en == SOCKET_EWOULDBLOCK; -} - - -int vio_close(Vio * vio) -{ - int r; - DBUG_ENTER("vio_close"); -#ifdef HAVE_OPENSSL - if (vio->type == VIO_TYPE_SSL) - { - r = my_ssl_close(vio); - } -#endif -#ifdef _WIN32 - if (vio->type == VIO_TYPE_NAMEDPIPE) - { - r=CloseHandle(vio->hPipe); - } - else if (vio->type != VIO_CLOSED) -#endif /* _WIN32 */ - { - r=0; - if (shutdown(vio->sd,2)) - r= -1; - if (closesocket(vio->sd)) - r= -1; - } - if (r) - { - DBUG_PRINT("vio_error", ("close() failed, error: %d",socket_errno)); - /* FIXME: error handling (not critical for MySQL) */ - } - vio->type= VIO_CLOSED; - vio->sd= -1; - DBUG_RETURN(r); -} - - -const char *vio_description(Vio * vio) -{ - return vio->desc; -} - -enum enum_vio_type vio_type(Vio* vio) -{ - return vio->type; -} - -my_socket vio_fd(Vio* vio) -{ - return vio->sd; -} - - -my_bool vio_peer_addr(Vio * vio, char *buf) -{ - DBUG_ENTER("vio_peer_addr"); - DBUG_PRINT("enter", ("sd=%d", vio->sd)); - if (vio->localhost) - { - strmov(buf,"127.0.0.1"); - } - else - { - socklen_t addrLen = sizeof(struct sockaddr); - if (getpeername(vio->sd, (struct sockaddr *) (& (vio->remote)), - &addrLen) != 0) - { - DBUG_PRINT("exit", ("getpeername, error: %d", socket_errno)); - DBUG_RETURN(1); - } - my_inet_ntoa(vio->remote.sin_addr,buf); - } - DBUG_PRINT("exit", ("addr=%s", buf)); - DBUG_RETURN(0); -} - - -void vio_in_addr(Vio *vio, struct in_addr *in) -{ - DBUG_ENTER("vio_in_addr"); - if (vio->localhost) - bzero((char*) in, sizeof(*in)); /* This should never be executed */ - else - *in=vio->remote.sin_addr; - DBUG_VOID_RETURN; -} - - -/* Return 0 if there is data to be read */ -/* -my_bool vio_poll_read(Vio *vio,uint timeout) -{ -#ifndef HAVE_POLL - return 0; -#else - struct pollfd fds; - int res; - DBUG_ENTER("vio_poll"); - fds.fd=vio->sd; - fds.events=POLLIN; - fds.revents=0; - if ((res=poll(&fds,1,(int) timeout*1000)) <= 0) - { - DBUG_RETURN(res < 0 ? 0 : 1); - } - DBUG_RETURN(fds.revents & POLLIN ? 0 : 1); -#endif -} -*/ - -#endif /* HAVE_VIO */ diff --git a/plugins/io/remote_io.c b/plugins/io/remote_io.c index 7a01b0e7..2b1f326f 100644 --- a/plugins/io/remote_io.c +++ b/plugins/io/remote_io.c @@ -94,6 +94,7 @@ mysql_declare_client_plugin(REMOTEIO) "Georg Richter", "Remote IO plugin", {0,1,0}, + "LGPL", ma_rio_init, ma_rio_deinit, &ma_rio_methods diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index 97fbd06f..22511027 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -62,7 +62,7 @@ ENDIF() FOREACH(API_TEST ${API_TESTS}) ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c) - TARGET_LINK_LIBRARIES(${API_TEST} mytap mariadbclient ${EXTRA_LIBS}) + TARGET_LINK_LIBRARIES(${API_TEST} mytap libmariadb ${EXTRA_LIBS}) ADD_TEST(${API_TEST} ${EXECUTABLE_OUTPUT_PATH}/${API_TEST}) SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 120) ENDFOREACH(API_TEST) diff --git a/unittest/libmariadb/misc.c b/unittest/libmariadb/misc.c index 499547dd..2555514c 100644 --- a/unittest/libmariadb/misc.c +++ b/unittest/libmariadb/misc.c @@ -177,35 +177,6 @@ static int test_bug31418(MYSQL *mysql) return OK; } -/* - Altough mysql_create_db(), mysql_rm_db() are deprecated since 4.0 they - should not crash server and should not hang in case of errors. - - Since those functions can't be seen in modern API (unless client library - was compiled with USE_OLD_FUNCTIONS define) we use simple_command() macro. -*/ -static int test_bug6081(MYSQL *mysql) -{ - int rc; - - if (mysql_get_server_version(mysql) < 50100) { - diag("Test requires MySQL Server version 5.1 or above"); - return SKIP; - } - - rc= simple_command(mysql, MYSQL_COM_DROP_DB, (char*) schema, - (ulong)strlen(schema), 0U, NULL); - FAIL_IF(!rc, "Error expected"); - - rc= simple_command(mysql, MYSQL_COM_CREATE_DB, (char*) schema, - (ulong)strlen(schema), 0U, NULL); - FAIL_IF(!rc, "Error expected"); - - rc= mysql_select_db(mysql, schema); - check_mysql_rc(rc, mysql); - return OK; -} - /* Query processing */ static int test_debug_example(MYSQL *mysql) @@ -1051,8 +1022,8 @@ static int test_remote2(MYSQL *my) struct my_tests_st my_tests[] = { {"test_remote1", test_remote1, TEST_CONNECTION_NEW, 0, NULL, NULL}, - {"test_remote2", test_remote2, TEST_CONNECTION_NEW, 0, NULL, NULL}, - {"test_conc117", test_conc117, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_remote2", test_remote2, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc117", test_conc117, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc_114", test_conc_114, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_connect_attrs", test_connect_attrs, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc49", test_conc49, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, @@ -1061,7 +1032,6 @@ struct my_tests_st my_tests[] = { {"test_debug_example", test_debug_example, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_bug29692", test_bug29692, TEST_CONNECTION_NEW, CLIENT_FOUND_ROWS, NULL, NULL}, {"test_bug31418", test_bug31418, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, - {"test_bug6081", test_bug6081, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_frm_bug", test_frm_bug, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_wl4166_1", test_wl4166_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_wl4166_2", test_wl4166_2, TEST_CONNECTION_NEW, 0, NULL, NULL}, diff --git a/unittest/libmariadb/my_test.h b/unittest/libmariadb/my_test.h index 2ad3feaf..8ed79d57 100644 --- a/unittest/libmariadb/my_test.h +++ b/unittest/libmariadb/my_test.h @@ -507,7 +507,9 @@ void run_tests(struct my_tests_st *test) { return; /* default doesn't work anymore */ } else if (mysql && !(test[i].connection & TEST_CONNECTION_DONT_CLOSE)) + { mysql_close(mysql); + } } else { skip(1, "%s", test[i].skipmsg); } From cc85e256666a826d3247e72ea39c5332bff721bf Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 6 Aug 2015 15:08:25 +0200 Subject: [PATCH 02/18] Added missing cio components --- libmariadb/ma_cio.c | 457 ++++++++ plugins/builtin/cio_gnutls.c | 405 +++++++ plugins/builtin/cio_openssl.c | 512 +++++++++ plugins/builtin/cio_schannel.c | 410 +++++++ plugins/builtin/cio_socket.c | 902 +++++++++++++++ plugins/builtin/ma_schannel.c | 687 +++++++++++ plugins/builtin/ma_schannel.h | 72 ++ {libmariadb => plugins/builtin}/my_auth.c | 105 +- plugins/cio/CMakeLists.txt | 28 + plugins/cio/cio_npipe.c | 301 +++++ plugins/cio/cio_plugin.def | 2 + plugins/cio/cio_shmem.c | 307 +++++ plugins/cio/tls_schannel.c | 1257 +++++++++++++++++++++ plugins/trace/.trace_example.c.swo | Bin 0 -> 16384 bytes plugins/trace/CMakeLists.txt | 16 + plugins/trace/trace_example.c | 440 ++++++++ plugins/trace/trace_example.so | Bin 0 -> 39895 bytes unittest/libmariadb/CMakeLists.txt | 2 +- 18 files changed, 5819 insertions(+), 84 deletions(-) create mode 100644 libmariadb/ma_cio.c create mode 100644 plugins/builtin/cio_gnutls.c create mode 100644 plugins/builtin/cio_openssl.c create mode 100644 plugins/builtin/cio_schannel.c create mode 100644 plugins/builtin/cio_socket.c create mode 100644 plugins/builtin/ma_schannel.c create mode 100644 plugins/builtin/ma_schannel.h rename {libmariadb => plugins/builtin}/my_auth.c (85%) create mode 100644 plugins/cio/CMakeLists.txt create mode 100644 plugins/cio/cio_npipe.c create mode 100644 plugins/cio/cio_plugin.def create mode 100644 plugins/cio/cio_shmem.c create mode 100644 plugins/cio/tls_schannel.c create mode 100644 plugins/trace/.trace_example.c.swo create mode 100644 plugins/trace/CMakeLists.txt create mode 100644 plugins/trace/trace_example.c create mode 100755 plugins/trace/trace_example.so diff --git a/libmariadb/ma_cio.c b/libmariadb/ma_cio.c new file mode 100644 index 00000000..f94a6f26 --- /dev/null +++ b/libmariadb/ma_cio.c @@ -0,0 +1,457 @@ +/************************************************************************************ + Copyright (C) 2015 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA +*************************************************************************************/ + +/* MariaDB Communication IO (CIO) interface + + CIO is the interface for client server communication and replaces former vio + component of the client library. + + CIO support various protcols like sockets, pipes and shared memory, which are + implemented as plugins and can be extended therfore easily. + + Interface function description: + + ma_cio_init allocates a new CIO object which will be used + for the current connection + + ma_cio_close frees all resources of previously allocated CIO object + and closes open connections + + ma_cio_read reads data from server + + ma_cio_write sends data to server + + ma_cio_set_timeout sets timeout for connection, read and write + + ma_cio_register_callback + register callback functions for read and write + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern pthread_mutex_t THR_LOCK_lock; + +/* callback functions for read/write */ +LIST *cio_callback= NULL; + +/* {{{ MARIADB_CIO *ma_cio_init */ +MARIADB_CIO *ma_cio_init(MA_CIO_CINFO *cinfo) +{ + /* check connection type and load the required plugin. + * Currently we support the following cio types: + * cio_socket + * cio_namedpipe + */ + char *cio_plugins[] = {"cio_socket", "cio_npipe"}; + int type; + MARIADB_CIO_PLUGIN *cio_plugin; + MARIADB_CIO *cio= NULL; + + switch (cinfo->type) + { + case CIO_TYPE_UNIXSOCKET: + case CIO_TYPE_SOCKET: + type= 0; + break; +#ifdef _WIN32 + case CIO_TYPE_NAMEDPIPE: + type= 1; + break; +#endif + default: + return NULL; + } + + if (!(cio_plugin= (MARIADB_CIO_PLUGIN *) + mysql_client_find_plugin(cinfo->mysql, + cio_plugins[type], + MYSQL_CLIENT_CIO_PLUGIN))) + { + /* error handling */ + return NULL; + } + + + if (!(cio= (MARIADB_CIO *)my_malloc(sizeof(MARIADB_CIO), + MYF(MY_WME | MY_ZEROFILL)))) + { + CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + return NULL; + } + + /* register error routine and methods */ + cio->methods= cio_plugin->methods; + cio->set_error= my_set_error; + + /* set tineouts */ + if (cio->methods->set_timeout) + { + cio->methods->set_timeout(cio, CIO_CONNECT_TIMEOUT, cinfo->mysql->options.connect_timeout); + cio->methods->set_timeout(cio, CIO_READ_TIMEOUT, cinfo->mysql->options.read_timeout); + cio->methods->set_timeout(cio, CIO_WRITE_TIMEOUT, cinfo->mysql->options.write_timeout); + } + + if (!(cio->cache= my_malloc(CIO_READ_AHEAD_CACHE_SIZE, MYF(MY_WME)))) + { + CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + return NULL; + } + cio->cache_size= 0; + cio->cache_pos= cio->cache; + + return cio; +} +/* }}} */ + +/* {{{ my_bool ma_cio_is_alive */ +my_bool ma_cio_is_alive(MARIADB_CIO *cio) +{ + if (cio->methods->is_alive) + return cio->methods->is_alive(cio); +} +/* }}} */ + +/* {{{ int ma_cio_fast_send */ +int ma_cio_fast_send(MARIADB_CIO *cio) +{ + if (!cio || !cio->methods->fast_send) + return 1; + return cio->methods->fast_send(cio); +} +/* }}} */ + +/* {{{ int ma_cio_keepalive */ +int ma_cio_keepalive(MARIADB_CIO *cio) +{ + if (!cio || !cio->methods->keepalive) + return 1; + return cio->methods->keepalive(cio); +} +/* }}} */ + +/* {{{ my_bool ma_cio_set_timeout */ +my_bool ma_cio_set_timeout(MARIADB_CIO *cio, + enum enum_cio_timeout type, + int timeout) +{ + if (!cio) + return 1; + + if (cio->methods->set_timeout) + return cio->methods->set_timeout(cio, type, timeout); + return 1; +} +/* }}} */ + +/* {{{ size_t ma_cio_read_async */ +static size_t ma_cio_read_async(MARIADB_CIO *cio, const uchar *buffer, size_t length) +{ + ssize_t res; + struct mysql_async_context *b= cio->async_context; + int timeout= cio->timeout[CIO_READ_TIMEOUT]; + + for (;;) + { + /* todo: async */ + if (cio->methods->async_read) + res= cio->methods->async_read(cio, buffer, length); + if (res >= 0 /* || IS_BLOCKING_ERROR()*/) + return res; + b->events_to_wait_for= MYSQL_WAIT_READ; + if (timeout >= 0) + { + b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; + b->timeout_value= timeout; + } + 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); + if (b->events_occured & MYSQL_WAIT_TIMEOUT) + return -1; + } +} +/* }}} */ + +/* {{{ size_t ma_cio_read */ +size_t ma_cio_read(MARIADB_CIO *cio, const uchar *buffer, size_t length) +{ + size_t r= -1; + if (!cio) + return -1; + + + if (cio && cio->async_context && cio->async_context->active) + { + goto end; + r= ma_cio_read_async(cio, buffer, length); + } + else + { + if (cio->async_context) + { + /* + If switching from non-blocking to blocking API usage, set the socket + back to blocking mode. + */ + my_bool old_mode; + ma_cio_blocking(cio, TRUE, &old_mode); + } + } + + /* secure connection */ +#ifdef HAVE_SSL + if (cio->cssl) + r= ma_cio_ssl_read(cio->cssl, buffer, length); + else +#endif + if (cio->methods->read) + r= cio->methods->read(cio, buffer, length); +end: + if (cio_callback) + { + void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length); + LIST *p= cio_callback; + while (p) + { + callback= p->data; + callback(0, cio->mysql, buffer, r); + p= p->next; + } + } + return r; +} +/* }}} */ + +/* {{{ size_t ma_cio_cache_read */ +size_t ma_cio_cache_read(MARIADB_CIO *cio, uchar *buffer, size_t length) +{ + size_t r; + + if (!cio) + return -1; + + if (!cio->cache) + return ma_cio_read(cio, buffer, length); + + if (cio->cache + cio->cache_size > cio->cache_pos) + { + r= MIN(length, cio->cache + cio->cache_size - cio->cache_pos); + memcpy(buffer, cio->cache_pos, r); + cio->cache_pos+= r; + } + else if (length >= CIO_READ_AHEAD_CACHE_MIN_SIZE) + { + r= ma_cio_read(cio, buffer, length); + } + else + { + r= ma_cio_read(cio, cio->cache, CIO_READ_AHEAD_CACHE_SIZE); + if ((ssize_t)r > 0) + { + if (length < r) + { + cio->cache_size= r; + cio->cache_pos= cio->cache + length; + r= length; + } + memcpy(buffer, cio->cache, r); + } + } + return r; +} +/* }}} */ + +/* {{{ size_t ma_cio_write */ +size_t ma_cio_write(MARIADB_CIO *cio, const uchar *buffer, size_t length) +{ + size_t r; + + if (!cio) + return -1; + + if (cio_callback) + { + void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length); + LIST *p= cio_callback; + while (p) + { + callback= p->data; + callback(1, cio->mysql, buffer, length); + p= p->next; + } + } + + /* secure connection */ +#ifdef HAVE_SSL + if (cio->cssl) + r= ma_cio_ssl_write(cio->cssl, buffer, length); + else +#endif + if (cio->methods->write) + r= cio->methods->write(cio, buffer, length); + if (cio->callback) + cio->callback(cio, 0, buffer, r); + return r; +} +/* }}} */ + +/* {{{ void ma_cio_close */ +void ma_cio_close(MARIADB_CIO *cio) +{ + /* free internal structures and close connection */ +#ifdef HAVE_SSL + if (cio && cio->cssl) + { + ma_cio_ssl_close(cio->cssl); + my_free((gptr)cio->cssl); + } +#endif + if (cio && cio->methods->close) + cio->methods->close(cio); + + if (cio->cache) + my_free((gptr)cio->cache); + + if (cio->fp) + my_fclose(cio->fp, MYF(0)); + + my_free((gptr)cio); +} +/* }}} */ + +/* {{{ my_bool ma_cio_get_handle */ +my_bool ma_cio_get_handle(MARIADB_CIO *cio, void *handle) +{ + if (cio && cio->methods->get_handle) + return cio->methods->get_handle(cio, handle); + return 1; +} +/* }}} */ + +/* {{{ ma_cio_wait_io_or_timeout */ +int ma_cio_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout) +{ + if (cio && cio->async_context && cio->async_context->active) + return my_io_wait_async(cio->async_context, + (is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE, + timeout); + + + if (cio && cio->methods->wait_io_or_timeout) + return cio->methods->wait_io_or_timeout(cio, is_read, timeout); + return 1; +} +/* }}} */ + +/* {{{ my_bool ma_cio_connect */ +my_bool ma_cio_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo) +{ + if (cio && cio->methods->connect) + return cio->methods->connect(cio, cinfo); + return 1; +} +/* }}} */ + +/* {{{ my_bool ma_cio_blocking */ +my_bool ma_cio_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode) +{ + if (cio && cio->methods->blocking) + return cio->methods->blocking(cio, block, previous_mode); + return 1; +} +/* }}} */ + +/* {{{ my_bool ma_cio_is_blocking */ +my_bool ma_cio_is_blocking(MARIADB_CIO *cio) +{ + if (cio && cio->methods->is_blocking) + return cio->methods->is_blocking(cio); + return 1; +} +/* }}} */ + +#ifdef HAVE_SSL +/* {{{ my_bool ma_cio_start_ssl */ +my_bool ma_cio_start_ssl(MARIADB_CIO *cio) +{ + if (!cio || !cio->mysql) + return 1; + CLEAR_CLIENT_ERROR(cio->mysql); + if (!(cio->cssl= ma_cio_ssl_init(cio->mysql))) + return 1; + if (ma_cio_ssl_connect(cio->cssl)) + { + my_free((gptr)cio->cssl); + cio->cssl= NULL; + return 1; + } + if ((cio->mysql->options.ssl_ca || cio->mysql->options.ssl_capath) && + (cio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && + ma_cio_ssl_verify_server_cert(cio->cssl)) + return 1; + + return 0; +} +/* }}} */ +#endif + +/* {{{ ma_cio_register_callback */ +int ma_cio_register_callback(my_bool register_callback, + void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length)) +{ + LIST *list; + + if (!callback_function) + return 1; + + /* plugin will unregister in it's deinit function */ + if (register_callback) + { + list= (LIST *)malloc(sizeof(LIST)); + + list->data= (void *)callback_function; + cio_callback= list_add(cio_callback, list); + } + else /* unregister callback function */ + { + LIST *p= cio_callback; + while (p) + { + if (p->data == callback_function) + { + list_delete(cio_callback, p); + break; + } + p= p->next; + } + } + return 0; +} +/* }}} */ diff --git a/plugins/builtin/cio_gnutls.c b/plugins/builtin/cio_gnutls.c new file mode 100644 index 00000000..657ed62f --- /dev/null +++ b/plugins/builtin/cio_gnutls.c @@ -0,0 +1,405 @@ +/************************************************************************************ + 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + *************************************************************************************/ +#ifdef HAVE_GNUTLS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +pthread_mutex_t LOCK_gnutls_config; +static my_bool my_gnutls_initialized= FALSE; + +static gnutls_certificate_credentials_t GNUTLS_xcred; + +#define MAX_SSL_ERR_LEN 100 + +int cio_gnutls_start(char *errmsg, size_t errmsg_len, int count, va_list); +int cio_gnutls_end(); +void *cio_gnutls_init(MARIADB_SSL *cssl, MYSQL *mysql); +my_bool cio_gnutls_connect(MARIADB_SSL *cssl); +size_t cio_gnutls_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length); +size_t cio_gnutls_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length); +my_bool cio_gnutls_close(MARIADB_SSL *cssl); +int cio_gnutls_verify_server_cert(MARIADB_SSL *cssl); +const char *cio_gnutls_cipher(MARIADB_SSL *cssl); +static int my_verify_callback(gnutls_session_t ssl); + +struct st_ma_cio_ssl_methods cio_gnutls_methods= { + cio_gnutls_init, + cio_gnutls_connect, + cio_gnutls_read, + cio_gnutls_write, + cio_gnutls_close, + cio_gnutls_verify_server_cert, + cio_gnutls_cipher +}; + +MARIADB_CIO_PLUGIN cio_gnutls_plugin= +{ + MYSQL_CLIENT_CIO_PLUGIN, + MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + "cio_gnutls", + "Georg Richter", + "MariaDB communication IO plugin for GnuTLS SSL communication", + {1, 0, 0}, + "LGPL", + NULL, + cio_gnutls_start, + cio_gnutls_end, + NULL, + &cio_gnutls_methods, + NULL +}; + +static void cio_gnutls_set_error(MYSQL *mysql, int ssl_errno) +{ + char ssl_error[MAX_SSL_ERR_LEN]; + const char *ssl_error_reason; + MARIADB_CIO *cio= mysql->net.cio; + + if (!ssl_errno) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); + return; + } + if ((ssl_error_reason= gnutls_strerror(ssl_errno))) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, + ssl_error_reason); + return; + } + my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset); + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ssl_error); +} + + +static void cio_gnutls_get_error(char *errmsg, size_t length, int ssl_errno) +{ + const char *ssl_error_reason; + + if (!ssl_errno) + { + strncpy(errmsg, "Unknown SSL error", length); + return; + } + if ((ssl_error_reason= gnutls_strerror(ssl_errno))) + { + strncpy(errmsg, ssl_error_reason, length); + return; + } + snprintf(errmsg, length, "SSL errno=%lu", ssl_errno); +} + +/* + Initializes SSL and allocate global + context SSL_context + + SYNOPSIS + my_gnutls_start + mysql connection handle + + RETURN VALUES + 0 success + 1 error +*/ +int cio_gnutls_start(char *errmsg, size_t errmsg_len, int count, va_list list) +{ + int rc= 0; + + pthread_mutex_init(&LOCK_gnutls_config,MY_MUTEX_INIT_FAST); + pthread_mutex_lock(&LOCK_gnutls_config); + + if (!my_gnutls_initialized) + { + if ((rc= gnutls_global_init()) != GNUTLS_E_SUCCESS) + { + cio_gnutls_get_error(errmsg, errmsg_len, rc); + goto end; + } + my_gnutls_initialized= TRUE; + } + /* Allocate a global context for credentials */ + rc= gnutls_certificate_allocate_credentials(&GNUTLS_xcred); +end: + pthread_mutex_unlock(&LOCK_gnutls_config); + return rc; +} + +/* + Release SSL and free resources + Will be automatically executed by + mysql_server_end() function + + SYNOPSIS + my_gnutls_end() + void + + RETURN VALUES + void +*/ +int cio_gnutls_end() +{ + pthread_mutex_lock(&LOCK_gnutls_config); + if (my_gnutls_initialized) + { + gnutls_certificate_free_keys(GNUTLS_xcred); + gnutls_certificate_free_cas(GNUTLS_xcred); + gnutls_certificate_free_crls(GNUTLS_xcred); + gnutls_certificate_free_ca_names(GNUTLS_xcred); + gnutls_certificate_free_credentials(GNUTLS_xcred); + gnutls_global_deinit(); + my_gnutls_initialized= FALSE; + } + pthread_mutex_unlock(&LOCK_gnutls_config); + pthread_mutex_destroy(&LOCK_gnutls_config); + return 0; +} + +static int cio_gnutls_set_certs(MYSQL *mysql, MARIADB_SSL *cssl) +{ + char *certfile= mysql->options.ssl_cert, + *keyfile= mysql->options.ssl_key; + char *cipher= NULL; + int ssl_error= 0; + + if (mysql->options.ssl_ca) + { + + ssl_error= gnutls_certificate_set_x509_trust_file(GNUTLS_xcred, + mysql->options.ssl_ca, + GNUTLS_X509_FMT_PEM); + if (ssl_error < 0) + goto error; + } + gnutls_certificate_set_verify_function(GNUTLS_xcred, + my_verify_callback); + + /* GNUTLS doesn't support ca_path */ + + if (keyfile && !certfile) + certfile= keyfile; + if (certfile && !keyfile) + keyfile= certfile; + + /* set key */ + if (certfile || keyfile) + { + if ((ssl_error= gnutls_certificate_set_x509_key_file(GNUTLS_xcred, + certfile, keyfile, + GNUTLS_X509_FMT_PEM)) < 0) + goto error; + } + return 1; + +error: + if (cipher) + my_free(cipher)); + return ssl_error; +} + +void *cio_gnutls_init(MARIADB_SSL *cssl, MYSQL *mysql) +{ + gnutls_session_t ssl= NULL; + int ssl_error= 0; + const char *err; + + pthread_mutex_lock(&LOCK_gnutls_config); + + if ((ssl_error= cio_gnutls_set_certs(mysql, cssl)) < 0) + goto error; + + if ((ssl_error = gnutls_init(&ssl, GNUTLS_CLIENT & GNUTLS_NONBLOCK)) < 0) + goto error; + gnutls_session_set_ptr(ssl, (void *)mysql); + + ssl_error= gnutls_priority_set_direct(ssl, "NORMAL:-DHE-RSA", &err); + if (ssl_error < 0) + goto error; + + if ((ssl_error= gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, GNUTLS_xcred)) < 0) + goto error; + + cssl->ssl= ssl; + pthread_mutex_unlock(&LOCK_gnutls_config); + return (void *)ssl; +error: + cio_gnutls_set_error(mysql, ssl_error); + if (ssl) + gnutls_deinit(ssl); + pthread_mutex_unlock(&LOCK_gnutls_config); + return NULL; +} + +my_bool cio_gnutls_connect(MARIADB_SSL *cssl) +{ + gnutls_session_t ssl = (gnutls_session_t)cssl->ssl; + my_bool blocking; + MYSQL *mysql; + MARIADB_CIO *cio; + int ret; + mysql= (MYSQL *)gnutls_session_get_ptr(ssl); + + if (!mysql) + return 1; + + cio= mysql->net.cio; + + /* Set socket to blocking if not already set */ + if (!(blocking= cio->methods->is_blocking(cio))) + cio->methods->blocking(cio, TRUE, 0); + + gnutls_transport_set_int(ssl, cio->methods->get_socket(cio)); + gnutls_handshake_set_timeout(ssl, mysql->options.connect_timeout); + + do { + ret = gnutls_handshake(ssl); + } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) + { + cio_gnutls_set_error(mysql, ret); + /* restore blocking mode */ + if (!blocking) + cio->methods->blocking(cio, FALSE, 0); + return 1; + } + cssl->ssl= (void *)ssl; + + return 0; +} + +size_t cio_gnutls_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ + return gnutls_record_recv((gnutls_session_t )cssl->ssl, (void *)buffer, length); +} + +size_t cio_gnutls_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ + return gnutls_record_send((gnutls_session_t )cssl->ssl, (void *)buffer, length); +} + +my_bool cio_gnutls_close(MARIADB_SSL *cssl) +{ + gnutls_bye((gnutls_session_t )cssl->ssl, GNUTLS_SHUT_WR); + gnutls_deinit((gnutls_session_t )cssl->ssl); + cssl->ssl= NULL; + + return 0; +} + +int cio_gnutls_verify_server_cert(MARIADB_SSL *cssl) +{ + /* server verification is already handled before */ + return 0; +} + +const char *cio_gnutls_cipher(MARIADB_SSL *cssl) +{ + if (!cssl || !cssl->ssl) + return NULL; + return gnutls_cipher_get_name (gnutls_cipher_get((gnutls_session_t )cssl->ssl)); +} + +static int my_verify_callback(gnutls_session_t ssl) +{ + unsigned int status; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + int ret; + MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl); + MARIADB_CIO *cio= mysql->net.cio; + gnutls_x509_crt_t cert; + const char *hostname; + + /* read hostname */ + hostname = mysql->host; + + /* skip verification if no ca_file/path was specified */ + if (!mysql->options.ssl_ca) + return 0; + + /* This verification function uses the trusted CAs in the credentials + * structure. So you must have installed one or more CA certificates. + */ + ret = gnutls_certificate_verify_peers2 (ssl, &status); + if (ret < 0) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CA verification failed"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + +// mysql->net.vio->status= status; + + if (status & GNUTLS_CERT_INVALID) + { + return GNUTLS_E_CERTIFICATE_ERROR; + } + /* Up to here the process is the same for X.509 certificates and + * OpenPGP keys. From now on X.509 certificates are assumed. This can + * be easily extended to work with openpgp keys as well. + */ + if (gnutls_certificate_type_get (ssl) != GNUTLS_CRT_X509) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Expected X509 certificate"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + if (gnutls_x509_crt_init (&cert) < 0) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Error during certificate initialization"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + cert_list = gnutls_certificate_get_peers (ssl, &cert_list_size); + if (cert_list == NULL) + { + gnutls_x509_crt_deinit (cert); + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "No certificate found"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) + { + gnutls_x509_crt_deinit (cert); + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && + gnutls_x509_crt_check_hostname (cert, hostname) < 0) + { + printf("Error: %s does not match\n", hostname); + gnutls_x509_crt_deinit (cert); + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Hostname in certificate doesn't match"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + gnutls_x509_crt_deinit (cert); + /* notify gnutls to continue handshake normally */ + + CLEAR_CLIENT_ERROR(mysql); + return 0; +} + +#endif /* HAVE_GNUTLS */ diff --git a/plugins/builtin/cio_openssl.c b/plugins/builtin/cio_openssl.c new file mode 100644 index 00000000..f6e836f3 --- /dev/null +++ b/plugins/builtin/cio_openssl.c @@ -0,0 +1,512 @@ +/************************************************************************************ + 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + *************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include /* SSL and SSL_CTX */ +#include /* error reporting */ +#include + +#ifndef HAVE_OPENSSL_DEFAULT +#include +#define my_malloc(A,B) malloc((A)) +#undef my_free +#define my_free(A,B) free((A)) +#define my_snprintf snprintf +#define my_vsnprintf vsnprintf +#undef SAFE_MUTEX +#endif +#include + +static my_bool my_openssl_initialized= FALSE; +static SSL_CTX *SSL_context= NULL; + +#define MAX_SSL_ERR_LEN 100 + +static pthread_mutex_t LOCK_openssl_config; +static pthread_mutex_t *LOCK_crypto= NULL; + +int cio_openssl_start(char *errmsg, size_t errmsg_len, int count, va_list); +int cio_openssl_end(); +void *cio_openssl_init(MARIADB_SSL *cssl, MYSQL *mysql); +my_bool cio_openssl_connect(MARIADB_SSL *cssl); +size_t cio_openssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length); +size_t cio_openssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length); +my_bool cio_openssl_close(MARIADB_SSL *cssl); +int cio_openssl_verify_server_cert(MARIADB_SSL *cssl); +const char *cio_openssl_cipher(MARIADB_SSL *cssl); + +struct st_ma_cio_ssl_methods cio_openssl_methods= { + cio_openssl_init, + cio_openssl_connect, + cio_openssl_read, + cio_openssl_write, + cio_openssl_close, + cio_openssl_verify_server_cert, + cio_openssl_cipher +}; + +MARIADB_CIO_PLUGIN cio_openssl_plugin= +{ + MYSQL_CLIENT_CIO_PLUGIN, + MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + "cio_openssl", + "Georg Richter", + "MariaDB communication IO plugin for OpenSSL communication", + {1, 0, 0}, + "LGPL", + cio_openssl_start, + cio_openssl_end, + NULL, + &cio_openssl_methods, + NULL +}; + +static void cio_openssl_set_error(MYSQL *mysql) +{ + ulong ssl_errno= ERR_get_error(); + char ssl_error[MAX_SSL_ERR_LEN]; + const char *ssl_error_reason; + MARIADB_CIO *cio= mysql->net.cio; + + if (!ssl_errno) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); + return; + } + if ((ssl_error_reason= ERR_reason_error_string(ssl_errno))) + { + cio->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, mysql->charset); + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, ssl_error); + return; +} + + +static void cio_openssl_get_error(char *errmsg, size_t length) +{ + ulong ssl_errno= ERR_get_error(); + const char *ssl_error_reason; + + if (!ssl_errno) + { + strncpy(errmsg, "Unknown SSL error", length); + return; + } + if ((ssl_error_reason= ERR_reason_error_string(ssl_errno))) + { + strncpy(errmsg, ssl_error_reason, length); + return; + } + snprintf(errmsg, length, "SSL errno=%lu", ssl_errno); +} + +/* + thread safe callbacks for OpenSSL + Crypto call back functions will be + set during ssl_initialization + */ +#if (OPENSSL_VERSION_NUMBER < 0x10000000) +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 + +static void my_cb_locking(int mode, int n, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(&LOCK_crypto[n]); + else + pthread_mutex_unlock(&LOCK_crypto[n]); +} + + +static int ssl_thread_init() +{ + int i, max= CRYPTO_num_locks(); + + if (LOCK_crypto == NULL) + { + if (!(LOCK_crypto= + (pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) * max, MYF(0)))) + return 1; + + for (i=0; i < max; i++) + pthread_mutex_init(&LOCK_crypto[i], NULL); + } + +#if (OPENSSL_VERSION_NUMBER < 0x10000000) + CRYPTO_set_id_callback(my_cb_threadid); +#else + CRYPTO_THREADID_set_callback(my_cb_threadid); +#endif + CRYPTO_set_locking_callback(my_cb_locking); + + return 0; +} + + +/* + Initializes SSL and allocate global + context SSL_context + + SYNOPSIS + my_ssl_start + mysql connection handle + + RETURN VALUES + 0 success + 1 error +*/ +int cio_openssl_start(char *errmsg, size_t errmsg_len, int count, va_list list) +{ + int rc= 1; + /* lock mutex to prevent multiple initialization */ + pthread_mutex_init(&LOCK_openssl_config,MY_MUTEX_INIT_FAST); + pthread_mutex_lock(&LOCK_openssl_config); + if (!my_openssl_initialized) + { + 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 + /* load errors */ + SSL_load_error_strings(); + /* digests and ciphers */ + OpenSSL_add_all_algorithms(); + + if (!(SSL_context= SSL_CTX_new(TLSv1_client_method()))) + { + cio_openssl_get_error(errmsg, errmsg_len); + goto end; + } + rc= 0; + my_openssl_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 +*/ +int cio_openssl_end() +{ + pthread_mutex_lock(&LOCK_openssl_config); + if (my_openssl_initialized) + { + int i; + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + for (i=0; i < CRYPTO_num_locks(); i++) + pthread_mutex_destroy(&LOCK_crypto[i]); + + my_free((gptr)LOCK_crypto, MYF(0)); + LOCK_crypto= NULL; + + if (SSL_context) + { + SSL_CTX_free(SSL_context); + SSL_context= NULL; + } + ERR_remove_state(0); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + //ENGINE_cleanup(); + CONF_modules_free(); + CONF_modules_unload(1); + sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); + my_openssl_initialized= FALSE; + } + pthread_mutex_unlock(&LOCK_openssl_config); + pthread_mutex_destroy(&LOCK_openssl_config); + return 0; +} + +static int cio_openssl_set_certs(MYSQL *mysql) +{ + char *certfile= mysql->options.ssl_cert, + *keyfile= mysql->options.ssl_key; + + /* add cipher */ + if ((mysql->options.ssl_cipher && + mysql->options.ssl_cipher[0] != 0) && + SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0) + goto error; + + /* ca_file and ca_path */ + if (SSL_CTX_load_verify_locations(SSL_context, + mysql->options.ssl_ca, + mysql->options.ssl_capath) == 0) + { + if (mysql->options.ssl_ca || mysql->options.ssl_capath) + goto error; + if (SSL_CTX_set_default_verify_paths(SSL_context) == 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_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1) + goto error; + + /* set key */ + if (keyfile && keyfile[0]) + { + if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1) + goto error; + } + /* verify key */ + if (certfile && !SSL_CTX_check_private_key(SSL_context)) + 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(SSL_context))) + { + if (X509_STORE_load_locations(certstore, mysql->options.ssl_ca, + mysql->options.ssl_capath) == 0 || + X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL) == 0) + goto error; + } + } + return 0; + +error: + cio_openssl_set_error(mysql); + return 1; +} + +static int my_verify_callback(int ok, X509_STORE_CTX *ctx) +{ + X509 *check_cert; + SSL *ssl; + MYSQL *mysql; + + ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + mysql= (MYSQL *)SSL_get_app_data(ssl); + + /* skip verification if no ca_file/path was specified */ + if (!mysql->options.ssl_ca && !mysql->options.ssl_capath) + { + ok= 1; + return 1; + } + + if (!ok) + { + uint depth; + if (!(check_cert= X509_STORE_CTX_get_current_cert(ctx))) + return 0; + depth= X509_STORE_CTX_get_error_depth(ctx); + if (depth == 0) + ok= 1; + } + + return ok; +} + +void *cio_openssl_init(MARIADB_SSL *cssl, MYSQL *mysql) +{ + int verify; + SSL *ssl= NULL; + + pthread_mutex_lock(&LOCK_openssl_config); + if (cio_openssl_set_certs(mysql)) + goto error; + + if (!(ssl= SSL_new(SSL_context))) + goto error; + + if (!SSL_set_app_data(ssl, mysql)) + goto error; + + verify= (!mysql->options.ssl_ca && !mysql->options.ssl_capath) ? + SSL_VERIFY_NONE : SSL_VERIFY_PEER; + + SSL_CTX_set_verify(SSL_context, verify, my_verify_callback); + SSL_CTX_set_verify_depth(SSL_context, 1); + + pthread_mutex_unlock(&LOCK_openssl_config); + return (void *)ssl; +error: + pthread_mutex_unlock(&LOCK_openssl_config); + if (ssl) + SSL_free(ssl); + return NULL; +} + +my_bool cio_openssl_connect(MARIADB_SSL *cssl) +{ + SSL *ssl = (SSL *)cssl->ssl; + my_bool blocking; + MYSQL *mysql; + MARIADB_CIO *cio; + + mysql= (MYSQL *)SSL_get_app_data(ssl); + cio= mysql->net.cio; + + /* Set socket to blocking if not already set */ + if (!(blocking= cio->methods->is_blocking(cio))) + cio->methods->blocking(cio, TRUE, 0); + + SSL_clear(ssl); + SSL_SESSION_set_timeout(SSL_get_session(ssl), + mysql->options.connect_timeout); + SSL_set_fd(ssl, mysql_get_socket(mysql)); + + if (SSL_connect(ssl) != 1) + { + cio_openssl_set_error(mysql); + /* restore blocking mode */ + if (!blocking) + cio->methods->blocking(cio, FALSE, 0); + return 1; + } + cio->cssl->ssl= cssl->ssl= (void *)ssl; + + return 0; +} + +size_t cio_openssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ + return SSL_read((SSL *)cssl->ssl, (void *)buffer, (int)length); +} + +size_t cio_openssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ + return SSL_write((SSL *)cssl->ssl, (void *)buffer, (int)length); +} + +my_bool cio_openssl_close(MARIADB_SSL *cssl) +{ + int i, rc; + SSL *ssl; + + if (!cssl || !cssl->ssl) + return 1; + ssl= (SSL *)cssl->ssl; + + SSL_set_quiet_shutdown(ssl, 1); + /* 2 x pending + 2 * data = 4 */ + for (i=0; i < 4; i++) + if ((rc= SSL_shutdown(ssl))) + break; + + SSL_free(ssl); + cssl->ssl= NULL; + + return rc; +} + +int cio_openssl_verify_server_cert(MARIADB_SSL *cssl) +{ + X509 *cert; + MYSQL *mysql; + MARIADB_CIO *cio; + SSL *ssl; + char *p1, *p2, buf[256]; + + if (!cssl || !cssl->ssl) + return 1; + ssl= (SSL *)cssl->ssl; + + mysql= (MYSQL *)SSL_get_app_data(ssl); + cio= mysql->net.cio; + + if (!mysql->host) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, + "Invalid (empty) hostname"); + return 1; + } + + if (!(cert= SSL_get_peer_certificate(ssl))) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, + "Unable to get server certificate"); + return 1; + } + + X509_NAME_oneline(X509_get_subject_name(cert), buf, 256); + X509_free(cert); + + /* Extract the server name from buffer: + Format: ....CN=/hostname/.... */ + if ((p1= strstr(buf, "/CN="))) + { + p1+= 4; + if ((p2= strchr(p1, '/'))) + *p2= 0; + if (!strcmp(mysql->host, p1)) + return(0); + } + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, + "Validation of SSL server certificate failed"); + return 1; +} + +const char *cio_openssl_cipher(MARIADB_SSL *cssl) +{ + if (!cssl || !cssl->ssl) + return NULL; + return SSL_get_cipher_name(cssl->ssl); +} diff --git a/plugins/builtin/cio_schannel.c b/plugins/builtin/cio_schannel.c new file mode 100644 index 00000000..26b49f99 --- /dev/null +++ b/plugins/builtin/cio_schannel.c @@ -0,0 +1,410 @@ +/************************************************************************************ + 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + *************************************************************************************/ +#include "ma_schannel.h" + +#pragma comment (lib, "crypt32.lib") +#pragma comment (lib, "secur32.lib") + +#define VOID void + +static my_bool my_schannel_initialized= FALSE; + +#define MAX_SSL_ERR_LEN 100 + +static pthread_mutex_t LOCK_schannel_config; +static pthread_mutex_t *LOCK_crypto= NULL; + +int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list); +int cio_schannel_end(); +void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql); +my_bool cio_schannel_connect(MARIADB_SSL *cssl); +size_t cio_schannel_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length); +size_t cio_schannel_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length); +my_bool cio_schannel_close(MARIADB_SSL *cssl); +int cio_schannel_verify_server_cert(MARIADB_SSL *cssl); +const char *cio_schannel_cipher(MARIADB_SSL *cssl); + +struct st_ma_cio_ssl_methods cio_schannel_methods= { + cio_schannel_init, + cio_schannel_connect, + cio_schannel_read, + cio_schannel_write, + cio_schannel_close, + cio_schannel_verify_server_cert, + cio_schannel_cipher +}; + +#ifndef HAVE_SCHANNEL_DEFAULT +MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_= +#else +MARIADB_CIO_PLUGIN cio_schannel_plugin= +#endif +{ + MYSQL_CLIENT_CIO_PLUGIN, + MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + "cio_schannel", + "Georg Richter", + "MariaDB communication IO plugin for Windows SSL/SChannel communication", + {1, 0, 0}, + "LGPL", + cio_schannel_start, + cio_schannel_end, + NULL, + &cio_schannel_methods, + NULL +}; + +static void cio_schannel_set_error(MYSQL *mysql) +{ + ulong ssl_errno= GetLastError(); + char ssl_error[MAX_SSL_ERR_LEN]; + char *ssl_error_reason= NULL; + MARIADB_CIO *cio= mysql->net.cio; + + if (!ssl_errno) + { + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); + return; + } + /* todo: obtain error messge */ + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ssl_errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &ssl_error_reason, 0, NULL ); + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error_reason); + + if (ssl_error_reason) + LocalFree(ssl_error_reason); + return; +} + + +static int ssl_thread_init() +{ + return 0; +} + + +/* + Initializes SSL and allocate global + context SSL_context + + SYNOPSIS + cio_schannel_start + + RETURN VALUES + 0 success + 1 error +*/ +int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list list) +{ + if (!my_schannel_initialized) + { + pthread_mutex_init(&LOCK_schannel_config,MY_MUTEX_INIT_FAST); + pthread_mutex_lock(&LOCK_schannel_config); + + // SecureZeroMemory(&SC_CTX, sizeof(struct st_schannel_global)); + + my_schannel_initialized= TRUE; + } + pthread_mutex_unlock(&LOCK_schannel_config); + return 0; +} + +/* + Release SSL and free resources + Will be automatically executed by + mysql_server_end() function + + SYNOPSIS + cio_schannel_end() + void + + RETURN VALUES + void +*/ +int cio_schannel_end() +{ + pthread_mutex_lock(&LOCK_schannel_config); + if (my_schannel_initialized) + { + + my_schannel_initialized= FALSE; + } + pthread_mutex_unlock(&LOCK_schannel_config); + pthread_mutex_destroy(&LOCK_schannel_config); + return 0; +} + +/* {{{ static int cio_schannel_set_client_certs(MARIADB_SSL *cssl) */ +static int cio_schannel_set_client_certs(MARIADB_SSL *cssl) +{ + MYSQL *mysql= cssl->cio->mysql; + char *certfile= mysql->options.ssl_cert, + *keyfile= mysql->options.ssl_key, + *cafile= mysql->options.ssl_ca; + + SC_CTX *sctx= (SC_CTX *)cssl->ssl; + + if (cafile) + if (!(sctx->client_ca_ctx = ma_schannel_create_cert_context(cafile))) + goto end; + + if (certfile) + { + if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(certfile))) + goto end; + if (keyfile) + if (!ma_schannel_load_private_key(sctx->client_cert_ctx, keyfile)) + goto end; + } + + if (mysql->options.extension && mysql->options.extension->ssl_crl) + { + sctx->client_crl_ctx= ma_schannel_create_crl_context(mysql->options.extension->ssl_crl); + + } + return 0; + +end: + if (sctx->client_ca_ctx) + CertFreeCertificateContext(sctx->client_ca_ctx); + if (sctx->client_cert_ctx) + CertFreeCertificateContext(sctx->client_cert_ctx); + if (sctx->client_crl_ctx) + CertFreeCRLContext(sctx->client_crl_ctx); + + cio_schannel_set_error(mysql); + return 1; +} +/* }}} */ + +/* {{{ void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql) */ +void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql) +{ + int verify; + SC_CTX *sctx; + + if (!(sctx= LocalAlloc(0, sizeof(SC_CTX)))) + return NULL; + ZeroMemory(sctx, sizeof(SC_CTX)); + + cssl->data= (void *)sctx; + + pthread_mutex_lock(&LOCK_schannel_config); + return (void *)sctx; +error: + pthread_mutex_unlock(&LOCK_schannel_config); + return NULL; +} +/* }}} */ + + + + +static my_bool VerifyServerCertificate(SC_CTX *sctx, PCCERT_CONTEXT pServerCert, PSTR pszServerName, DWORD dwCertFlags ) +{ + SECURITY_STATUS sRet; + DWORD flags; + char *szName= NULL; + int rc= 0; + + /* We perform a manually validation, as described at + http://msdn.microsoft.com/en-us/library/windows/desktop/aa378740%28v=vs.85%29.aspx + */ + + /* Check if + - The certificate chain is complete and the root is a certificate from a trusted certification authority (CA). + - The current time is not beyond the begin and end dates for each of the certificates in the certificate chain. + */ + flags= CERT_STORE_SIGNATURE_FLAG | + CERT_STORE_TIME_VALIDITY_FLAG; + + if (!(sRet= CertVerifySubjectCertificateContext(pServerCert, + sctx->client_ca_ctx, + &flags))) + { + /* todo: error handling */ + return 0; + } + + /* Check if none of the certificates in the certificate chain have been revoked. */ + if (sctx->client_crl_ctx) + { + PCRL_INFO Info[1]; + + Info[0]= sctx->client_crl_ctx->pCrlInfo; + if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + pServerCert->pCertInfo, + 1, Info)) ) + { + /* todo: error handling */ + return 0; + } + } + + /* check server name */ + if (pszServerName) + { + DWORD NameSize= 0; + char *p1, *p2; + + if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType, + &pServerCert->pCertInfo->Subject, + CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, + NULL, 0))) + { + /* todo: error handling */ + return 0; + } + + if (!(szName= LocalAlloc(0, NameSize + 1))) + { + /* error handling */ + return 0; + } + + if (!CertNameToStr(pServerCert->dwCertEncodingType, + &pServerCert->pCertInfo->Subject, + CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, + szName, NameSize)) + { + /* error handling */ + goto end; + } + if ((p1 = strstr(szName, "CN="))) + { + p1+= 3; + if ((p2= strstr(p1, ", "))) + *p2= 0; + if (!strcmp(pszServerName, p1)) + { + rc= 1; + goto end; + } + + } + + } + +end: + if (szName) + LocalFree(szName); + return rc; + +} + +my_bool cio_schannel_connect(MARIADB_SSL *cssl) +{ + my_bool blocking; + MYSQL *mysql; + SCHANNEL_CRED Cred; + MARIADB_CIO *cio; + SC_CTX *sctx; + SECURITY_STATUS sRet; + PCCERT_CONTEXT pRemoteCertContext = NULL; + + if (!cssl || !cssl->cio || !cssl->data) + return 1;; + + cio= cssl->cio; + sctx= (SC_CTX *)cssl->data; + + /* Set socket to blocking if not already set */ + if (!(blocking= cio->methods->is_blocking(cio))) + cio->methods->blocking(cio, TRUE, 0); + + mysql= cio->mysql; + + if (cio_schannel_set_client_certs(cssl)) + { + cio_schannel_set_error(mysql); + goto end; + } + + ZeroMemory(&Cred, sizeof(SCHANNEL_CRED)); + Cred.dwVersion= SCHANNEL_CRED_VERSION; + Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK | + SCH_CRED_NO_DEFAULT_CREDS | + SCH_CRED_MANUAL_CRED_VALIDATION; + if (sctx->client_cert_ctx) + { + Cred.cCreds = 1; + Cred.paCred = &sctx->client_cert_ctx; + } + Cred.grbitEnabledProtocols= SP_PROT_TLS1; + + /* We allocate 2 x net_buffer_length */ + if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, 0x4000))) + goto end; + + if (AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, + NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL) != SEC_E_OK) + goto end; + + if (ma_schannel_client_handshake(cssl)) + goto end; + + sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext); + if (sRet != SEC_E_OK) + goto end; + + if (!VerifyServerCertificate(sctx, + pRemoteCertContext, + mysql->host, + 0 )) + goto end; + + + return 0; +end: + /* todo: cleanup */ + if (sctx->IoBuffer) + LocalFree(sctx->IoBuffer); + if (sctx->client_ca_ctx) + CertFreeCertificateContext(sctx->client_ca_ctx); + if (sctx->client_cert_ctx) + CertFreeCertificateContext(sctx->client_cert_ctx); + return 1; +} + +size_t cio_schannel_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ +} + +size_t cio_schannel_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ +} + +my_bool cio_schannel_close(MARIADB_SSL *cssl) +{ + int i, rc; + + return rc; +} + +int cio_schannel_verify_server_cert(MARIADB_SSL *cssl) +{ +} + +const char *cio_schannel_cipher(MARIADB_SSL *cssl) +{ + if (!cssl || !cssl->ssl) + return NULL; +} diff --git a/plugins/builtin/cio_socket.c b/plugins/builtin/cio_socket.c new file mode 100644 index 00000000..a4060ab7 --- /dev/null +++ b/plugins/builtin/cio_socket.c @@ -0,0 +1,902 @@ +/************************************************************************************ + Copyright (C) 2015 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA +*************************************************************************************/ + +/* + MariaDB Communication IO (CIO) plugin for socket communication: + + The plugin handles connections via unix and network sockets. it is enabled by + default and compiled into Connector/C. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_POLL +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include +#include +#include +#else +#define O_NONBLOCK 1 +#endif + +#ifndef SOCKET_ERROR +#define SOCKET_ERROR -1 +#endif + + +/* Function prototypes */ +my_bool cio_socket_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); +int cio_socket_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type); +size_t cio_socket_read(MARIADB_CIO *cio, const uchar *buffer, size_t length); +size_t cio_socket_async_read(MARIADB_CIO *cio, const uchar *buffer, size_t length); +size_t cio_socket_write(MARIADB_CIO *cio, const uchar *buffer, size_t length); +size_t cio_socket_async_write(MARIADB_CIO *cio, const uchar *buffer, size_t length); +int cio_socket_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout); +my_bool cio_socket_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value); +my_bool cio_socket_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo); +my_bool cio_socket_close(MARIADB_CIO *cio); +int cio_socket_fast_send(MARIADB_CIO *cio); +int cio_socket_keepalive(MARIADB_CIO *cio); +my_bool cio_socket_get_handle(MARIADB_CIO *cio, void *handle); +my_bool cio_socket_is_blocking(MARIADB_CIO *cio); +my_bool cio_socket_is_alive(MARIADB_CIO *cio); + +static int cio_socket_init(char *unused1, + size_t unused2, + int unused3, + va_list); +static int cio_socket_end(void); + +struct st_ma_cio_methods cio_socket_methods= { + cio_socket_set_timeout, + cio_socket_get_timeout, + cio_socket_read, + cio_socket_async_read, + cio_socket_write, + cio_socket_async_write, + cio_socket_wait_io_or_timeout, + cio_socket_blocking, + cio_socket_connect, + cio_socket_close, + cio_socket_fast_send, + cio_socket_keepalive, + cio_socket_get_handle, + cio_socket_is_blocking, + cio_socket_is_alive +}; + +MARIADB_CIO_PLUGIN cio_socket_plugin= +{ + MYSQL_CLIENT_CIO_PLUGIN, + MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + "cio_socket", + "Georg Richter", + "MariaDB communication IO plugin for socket communication", + {1, 0, 0}, + "LGPL", + &cio_socket_init, + &cio_socket_end, + &cio_socket_methods, + NULL, + NULL +}; + +struct st_cio_socket { + my_socket socket; + int fcntl_mode; + MYSQL *mysql; +}; + +static my_bool cio_socket_initialized= FALSE; + + +static int cio_socket_init(char *errmsg, + size_t errmsg_length, + int unused, + va_list va) +{ + cio_socket_initialized= TRUE; + return 0; +} + +static int cio_socket_end(void) +{ + if (!cio_socket_initialized) + return 1; + return 0; +} + +/* {{{ cio_socket_set_timeout */ +/* + set timeout value + + SYNOPSIS + cio_socket_set_timeout + cio CIO + type timeout type (connect, read, write) + timeout timeout in seconds + + DESCRIPTION + Sets timeout values for connection-, read or write time out. + CIO internally stores all timeout values in milliseconds, but + accepts and returns all time values in seconds (like api does). + + RETURNS + 0 Success + 1 Error +*/ +my_bool cio_socket_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout) +{ + if (!cio) + return 1; + cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1; + return 0; +} +/* }}} */ + +/* {{{ cio_socket_get_timeout */ +/* + get timeout value + + SYNOPSIS + cio_socket_get_timeout + cio CIO + type timeout type (connect, read, write) + + DESCRIPTION + Returns timeout values for connection-, read or write time out. + CIO internally stores all timeout values in milliseconds, but + accepts and returns all time values in seconds (like api does). + + RETURNS + 0...n time out value + -1 error +*/ +int cio_socket_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type) +{ + if (!cio) + return -1; + return cio->timeout[type] / 1000; +} +/* }}} */ + +/* {{{ cio_socket_read */ +/* + read from socket + + SYNOPSIS + cio_socket_read() + cio CIO + buffer read buffer + length buffer length + + DESCRIPTION + reads up to length bytes into specified buffer. In the event of an + error erno is set to indicate it. + + RETURNS + 1..n number of bytes read + 0 peer has performed shutdown + -1 on error + +*/ +size_t cio_socket_read(MARIADB_CIO *cio, const uchar *buffer, size_t length) +{ + ssize_t r= -1; +#ifndef _WIN32 + /* don't ignore SIGPIPE globally like in libmysql!! */ + int read_flags= MSG_NOSIGNAL; +#endif + struct st_cio_socket *csock= NULL; + + if (!cio || !cio->data) + return -1; + + csock= (struct st_cio_socket *)cio->data; + +#ifndef _WIN32 + do { + r= recv(csock->socket, (void *)buffer, length, read_flags); + } while (r == -1 && errno == EINTR); + + while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) + && cio->timeout[CIO_READ_TIMEOUT] > 0) + { + if (cio_socket_wait_io_or_timeout(cio, TRUE, cio->timeout[CIO_READ_TIMEOUT]) < 1) + return -1; + do { + r= recv(csock->socket, (void *)buffer, length, read_flags); + } while (r == -1 && errno == EINTR); + } +#else + { + WSABUF wsaData; + DWORD flags= 0, + dwBytes= 0; + + /* clear error */ + errno= 0; + wsaData.len = (u_long)length; + wsaData.buf = (char*) buffer; + + r = WSARecv(csock->socket, &wsaData, 1, &dwBytes, &flags, NULL, NULL); + if (r == SOCKET_ERROR) + { + errno= WSAGetLastError(); + return -1; + } + r= dwBytes; + } +#endif + return r; +} +/* }}} */ + +/* {{{ cio_socket_async_read */ +/* + read from socket + + SYNOPSIS + cio_socket_async_read() + cio CIO + buffer read buffer + length buffer length + + DESCRIPTION + reads up to length bytes into specified buffer. In the event of an + error erno is set to indicate it. + + RETURNS + 1..n number of bytes read + 0 peer has performed shutdown + -1 on error + +*/ +size_t cio_socket_async_read(MARIADB_CIO *cio, const uchar *buffer, size_t length) +{ + ssize_t r= -1; +#ifndef _WIN32 + int read_flags= MSG_NOSIGNAL | MSG_DONTWAIT; +#endif + struct st_cio_socket *csock= NULL; + + if (!cio || !cio->data) + return -1; + + csock= (struct st_cio_socket *)cio->data; + +#ifndef _WIN32 + r= recv(csock->socket,(void *)buffer, length, read_flags); +#else + r= recv(csock->socket, buffer, length, 0); +#endif + return r; +} +/* }}} */ + +/* {{{ cio_socket_async_write */ +/* + write to socket + + SYNOPSIS + cio_socket_async_write() + cio CIO + buffer read buffer + length buffer length + + DESCRIPTION + writes up to length bytes to socket. In the event of an + error erno is set to indicate it. + + RETURNS + 1..n number of bytes read + 0 peer has performed shutdown + -1 on error + +*/ +size_t cio_socket_async_write(MARIADB_CIO *cio, const uchar *buffer, size_t length) +{ + ssize_t r= -1; +#ifndef WIN32 + int write_flags= MSG_NOSIGNAL | MSG_DONTWAIT; +#endif + struct st_cio_socket *csock= NULL; + + if (!cio || !cio->data) + return -1; + + csock= (struct st_cio_socket *)cio->data; + +#ifndef WIN32 + r= send(csock->socket, buffer, length, write_flags); +#else + r= send(csock->socket, buffer, length, 0); +#endif + return r; +} +/* }}} */ + +/* {{{ cio_socket_write */ +/* + write to socket + + SYNOPSIS + cio_socket_write() + cio CIO + buffer read buffer + length buffer length + + DESCRIPTION + writes up to length bytes to socket. In the event of an + error erno is set to indicate it. + + RETURNS + 1..n number of bytes read + 0 peer has performed shutdown + -1 on error + +*/ +size_t cio_socket_write(MARIADB_CIO *cio, const uchar *buffer, size_t length) +{ + ssize_t r= -1; +#ifndef _WIN32 + int send_flags= MSG_NOSIGNAL; +#endif + struct st_cio_socket *csock= NULL; + if (!cio || !cio->data) + return -1; + + csock= (struct st_cio_socket *)cio->data; + +#ifndef _WIN32 + do { + r= send(csock->socket, buffer, length, send_flags); + } while (r == -1 && errno == EINTR); + + while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && + cio->timeout[CIO_WRITE_TIMEOUT] != 0) + { + if (cio_socket_wait_io_or_timeout(cio, FALSE, cio->timeout[CIO_WRITE_TIMEOUT]) < 1) + return -1; + do { + r= send(csock->socket, buffer, length, send_flags); + } while (r == -1 && errno == EINTR); + } +#else + { + WSABUF wsaData; + DWORD dwBytes= 0; + + wsaData.len = (u_long)length; + wsaData.buf = (char*) buffer; + + r = WSASend(csock->socket, &wsaData, 1, &dwBytes, 0, NULL, NULL); + if (r == SOCKET_ERROR) { + errno= WSAGetLastError(); + return -1; + } + r= dwBytes; + } +#endif + return r; +} +/* }}} */ + +int cio_socket_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout) +{ + int rc; + struct st_cio_socket *csock= NULL; + +#ifndef _WIN32 + struct pollfd p_fd; +#else + struct timeval tv= {0,0}; + fd_set fds, exc_fds; +#endif + + if (!cio || !cio->data) + return 0; + + csock= (struct st_cio_socket *)cio->data; + { +#ifndef _WIN32 + p_fd.fd= csock->socket; + p_fd.events= (is_read) ? POLLIN : POLLOUT; + + do { + rc= poll(&p_fd, 1, timeout); + } while (rc == -1 || errno == EINTR); + + if (rc == 0) + errno= ETIMEDOUT; +#else + FD_ZERO(&fds); + FD_ZERO(&exc_fds); + + FD_SET(csock->socket, &fds); + FD_SET(csock->socket, &exc_fds); + + if (timeout >= 0) + { + tv.tv_sec= timeout / 1000; + tv.tv_usec= (timeout % 1000) * 1000; + } + + rc= select(0, (is_read) ? &fds : NULL, + (is_read) ? NULL : &fds, + &exc_fds, + (timeout >= 0) ? &tv : NULL); + if (rc == SOCKET_ERROR) + errno= WSAGetLastError(); + if (rc == 0) + errno= ETIMEDOUT; +#endif + } + return rc; +} + +my_bool cio_socket_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode) +{ + int *sd_flags, save_flags; + my_bool tmp; + struct st_cio_socket *csock= NULL; + + if (!cio || !cio->data) + return 1; + + csock= (struct st_cio_socket *)cio->data; + sd_flags= &csock->fcntl_mode; + save_flags= csock->fcntl_mode; + + if (!previous_mode) + previous_mode= &tmp; + +#ifdef _WIN32 + *previous_mode= (*sd_flags & O_NONBLOCK) != 0; + *sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK; + { + ulong arg= 1 - block; + if (ioctlsocket(csock->socket, FIONBIO, (void *)&arg)) + { + csock->fcntl_mode= save_flags; + return(WSAGetLastError()); + } + } +#else +#if defined(O_NONBLOCK) + *previous_mode= (*sd_flags & O_NONBLOCK) != 0; + *sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK; +#elif defined(O_NDELAY) + *previous_mode= (*sd_flags & O_NODELAY) != 0; + *sd_flags = (block) ? *sd_flags & ~O_NODELAY : *sd_flags | O_NODELAY; +#elif defined(FNDELAY) + *previous_mode= (*sd_flags & O_FNDELAY) != 0; + *sd_flags = (block) ? *sd_flags & ~O_FNDELAY : *sd_flags | O_FNDELAY; +#else +#error socket blocking is not supported on this platform +#endif + if (fcntl(csock->socket, F_SETFL, *sd_flags) == -1) + { + csock->fcntl_mode= save_flags; + return errno; + } +#endif + return 0; +} + +static int cio_socket_internal_connect(MARIADB_CIO *cio, + const struct sockaddr *name, + size_t namelen) +{ + int rc= 0; + struct st_cio_socket *csock= NULL; + int timeout; + + if (!cio || !cio->data) + return 1; + + csock= (struct st_cio_socket *)cio->data; + timeout= cio->timeout[CIO_CONNECT_TIMEOUT]; + + /* set non blocking */ + cio_socket_blocking(cio, 0, 0); + +#ifndef _WIN32 + + do { + rc= connect(csock->socket, (struct sockaddr*) name, (int)namelen); + } while (rc == -1 && errno == EINTR); + /* in case a timeout values was set we need to check error values + EINPROGRESS and EAGAIN */ + if (timeout != 0 && rc == -1 && + (errno == EINPROGRESS || errno == EAGAIN)) + { + rc= cio_socket_wait_io_or_timeout(cio, FALSE, timeout); + if (rc < 1) + return -1; + { + int error; + socklen_t error_len= sizeof(error); + if ((rc = getsockopt(csock->socket, SOL_SOCKET, SO_ERROR, + (char *)&error, &error_len)) < 0) + return errno; + else if (error) + return error; + } + } +#else + rc= connect(csock->socket, (struct sockaddr*) name, (int)namelen); + if (rc == SOCKET_ERROR) + { + if (WSAGetLastError() == WSAEWOULDBLOCK) + { + if (cio_socket_wait_io_or_timeout(cio, FALSE, timeout) < 0) + return -1; + rc= 0; + } + } +#endif + return rc; +} + +int cio_socket_keepalive(MARIADB_CIO *cio) +{ + int opt= 1; + struct st_cio_socket *csock= NULL; + + if (!cio || !cio->data) + return 1; + + csock= (struct st_cio_socket *)cio->data; + + return setsockopt(csock->socket, SOL_SOCKET, SO_KEEPALIVE, +#ifndef _WIN32 + (const void *)&opt, sizeof(opt)); +#else + (char *)&opt, (int)sizeof(opt)); +#endif +} + +int cio_socket_fast_send(MARIADB_CIO *cio) +{ + int r= 0; + struct st_cio_socket *csock= NULL; + + if (!cio || !cio->data) + return 1; + + csock= (struct st_cio_socket *)cio->data; + +/* Setting IP_TOS is not recommended on Windows. See + http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx +*/ +#ifndef _WIN32 +#ifdef IPTOS_THROUGHPUT + { + int tos = IPTOS_THROUGHPUT; + r= setsockopt(csock->socket, IPPROTO_IP, IP_TOS, + (const void *)&tos, sizeof(tos)); + } +#endif /* IPTOS_THROUGHPUT */ +#endif + if (!r) + { + int opt = 1; + /* turn off nagle algorithm */ + r= setsockopt(csock->socket, IPPROTO_TCP, TCP_NODELAY, +#ifdef _WIN32 + (const char *)&opt, (int)sizeof(opt)); +#else + (const void *)&opt, sizeof(opt)); +#endif + } + return r; +} + +static int +cio_socket_connect_sync_or_async(MARIADB_CIO *cio, + const struct sockaddr *name, uint namelen) +{ + MYSQL *mysql= cio->mysql; + if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->active) + { + cio_socket_blocking(cio,0, 0); + return my_connect_async(cio, name, namelen, cio->timeout[CIO_CONNECT_TIMEOUT]); + } + + return cio_socket_internal_connect(cio, name, namelen); +} + +my_bool cio_socket_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo) +{ + struct st_cio_socket *csock= NULL; + + if (!cio || !cinfo) + return 1; + + if (!(csock= (struct st_cio_socket *)my_malloc(sizeof(struct st_cio_socket), + MYF(MY_WME | MY_ZEROFILL)))) + { + CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, ""); + return 1; + } + cio->data= (void *)csock; + csock->socket= -1; + cio->mysql= cinfo->mysql; + cio->type= cinfo->type; + + if (cinfo->type == CIO_TYPE_UNIXSOCKET) + { +#ifndef _WIN32 +#ifdef HAVE_SYS_UN_H + struct sockaddr_un UNIXaddr; + if ((csock->socket = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR) + { + CIO_SET_ERROR(cinfo->mysql, CR_SOCKET_CREATE_ERROR, unknown_sqlstate, 0, errno); + goto error; + } + bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); + UNIXaddr.sun_family = AF_UNIX; + strmov(UNIXaddr.sun_path, cinfo->unix_socket); + if (cio_socket_connect_sync_or_async(cio, (struct sockaddr *) &UNIXaddr, + sizeof(UNIXaddr))) + { + CIO_SET_ERROR(cinfo->mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_CONNECTION_ERROR), cinfo->unix_socket, socket_errno); + goto error; + } + if (cio_socket_blocking(cio, 1, 0) == SOCKET_ERROR) + { + goto error; + } +#else +/* todo: error, not supported */ +#endif +#endif + } else if (cinfo->type == CIO_TYPE_SOCKET) + { + struct addrinfo hints, *save_res= 0, *bind_res= 0, *res= 0, *bres= 0; + char server_port[NI_MAXSERV]; + int gai_rc; + int rc= 0; + + bzero(&server_port, NI_MAXSERV); + my_snprintf(server_port, NI_MAXSERV, "%d", cinfo->port); + + /* set hints for getaddrinfo */ + bzero(&hints, sizeof(hints)); + hints.ai_protocol= IPPROTO_TCP; /* TCP connections only */ + hints.ai_family= AF_UNSPEC; /* includes: IPv4, IPv6 or hostname */ + hints.ai_socktype= SOCK_STREAM; + + /* if client has multiple interfaces, we will bind socket to given + * bind_address */ + if (cinfo->mysql->options.bind_address) + { + gai_rc= getaddrinfo(cinfo->mysql->options.bind_address, 0, + &hints, &res); + if (gai_rc != 0) + { + CIO_SET_ERROR(cinfo->mysql, CR_BIND_ADDR_FAILED, SQLSTATE_UNKNOWN, + CER(CR_BIND_ADDR_FAILED), cinfo->mysql->options.bind_address, gai_rc); + goto error; + } + } + /* Get the address information for the server using getaddrinfo() */ + gai_rc= getaddrinfo(cinfo->host, server_port, &hints, &res); + if (gai_rc != 0) + { + CIO_SET_ERROR(cinfo->mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), cinfo->host, gai_rc); + if (bres) + freeaddrinfo(bres); + goto error; + } + + /* res is a linked list of addresses for the given hostname. We loop until + we are able to connect to one address or all connect attempts failed */ + for (save_res= res; save_res; save_res= save_res->ai_next) + { + csock->socket= socket(save_res->ai_family, save_res->ai_socktype, + save_res->ai_protocol); + if (csock->socket == SOCKET_ERROR) + /* Errors will be handled after loop finished */ + continue; + + if (bind_res) + { + for (bind_res= bres; bind_res; bind_res= bind_res->ai_next) + { + if (!(rc= bind(csock->socket, bind_res->ai_addr, bind_res->ai_addrlen))) + break; + } + if (rc) + { + closesocket(csock->socket); + continue; + } + } + + rc= cio_socket_connect_sync_or_async(cio, save_res->ai_addr, save_res->ai_addrlen); + if (!rc) + { +/* if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->active) + break; */ + if (cio_socket_blocking(cio, 1, 0) == SOCKET_ERROR) + { + closesocket(csock->socket); + continue; + } + break; /* success! */ + } + } + + freeaddrinfo(res); + freeaddrinfo(bres); + + if (csock->socket == SOCKET_ERROR) + { + CIO_SET_ERROR(cinfo->mysql, CR_IPSOCK_ERROR, SQLSTATE_UNKNOWN, ER(CR_IPSOCK_ERROR), + socket_errno); + goto error; + } + + /* last call to connect 2 failed */ + if (rc) + { + CIO_SET_ERROR(cinfo->mysql, CR_CONN_HOST_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONN_HOST_ERROR), + cinfo->host, socket_errno); + goto error; + } + } +#ifdef _WIN32 + /* apply timeouts */ + if (cio->timeout[CIO_WRITE_TIMEOUT] > 0) + setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&cio->timeout[CIO_WRITE_TIMEOUT], sizeof(int)); + if (cio->timeout[CIO_READ_TIMEOUT] > 0) + setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&cio->timeout[CIO_READ_TIMEOUT], sizeof(int)); +#endif + return 0; +error: + if (cio->data) + { + my_free((gptr)cio->data); + cio->data= NULL; + } + return 1; +} + +/* {{{ my_bool cio_socket_close() */ +my_bool cio_socket_close(MARIADB_CIO *cio) +{ + struct st_cio_socket *csock= NULL; + int r= 0; + + if (!cio) + return 1; + + if (cio->data) + { + csock= (struct st_cio_socket *)cio->data; + if (csock && csock->socket != -1) + { + r= shutdown(csock->socket ,2); + r= closesocket(csock->socket); + csock->socket= -1; + } + my_free((gptr)cio->data); + cio->data= NULL; + } + return r; +} +/* }}} */ + +/* {{{ my_socket cio_socket_get_handle */ +my_bool cio_socket_get_handle(MARIADB_CIO *cio, void *handle) +{ + if (cio && cio->data && handle) + { + *(my_socket *)handle= ((struct st_cio_socket *)cio->data)->socket; + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ my_bool cio_socket_is_blocking(MARIADB_CIO *cio) */ +my_bool cio_socket_is_blocking(MARIADB_CIO *cio) +{ + struct st_cio_socket *csock= NULL; + my_bool r; + + if (!cio || !cio->data) + return 0; + + csock= (struct st_cio_socket *)cio->data; + r = !(csock->fcntl_mode & O_NONBLOCK); + return r; +} +/* }}} */ + +/* {{{ my_bool cio_socket_is_alive(MARIADB_CIO *cio) */ +my_bool cio_socket_is_alive(MARIADB_CIO *cio) +{ + struct st_cio_socket *csock= NULL; + #ifndef _WIN32 + struct pollfd poll_fd; +#else + FD_SET sfds; + struct timeval tv= {0,0}; +#endif + int res; + + if (!cio || !cio->data) + return 0; + + csock= (struct st_cio_socket *)cio->data; +#ifndef _WIN32 + memset(&poll_fd, 0, sizeof(struct pollfd)); + poll_fd.events= POLLPRI | POLLIN; + poll_fd.fd= csock->socket; + + res= poll(&poll_fd, 1, 0); + if (res <= 0) /* timeout or error */ + return FALSE; + if (!(poll_fd.revents & (POLLIN | POLLPRI))) + return FALSE; + return TRUE; +#else + /* We can't use the WSAPoll function, it's broken :-( + (see Windows 8 Bugs 309411 - WSAPoll does not report failed connections) + Instead we need to use select function: + If TIMEVAL is initialized to {0, 0}, select will return immediately; + this is used to poll the state of the selected sockets. + */ + FD_ZERO(&sfds); + FD_SET(csock->socket, &sfds); + + res= select(csock->socket + 1, &sfds, NULL, NULL, &tv); + if (res > 0 && FD_ISSET(csock->socket, &sfds)) + return TRUE; + return FALSE; +#endif +} +/* }}} */ diff --git a/plugins/builtin/ma_schannel.c b/plugins/builtin/ma_schannel.c new file mode 100644 index 00000000..c66684dd --- /dev/null +++ b/plugins/builtin/ma_schannel.c @@ -0,0 +1,687 @@ +/************************************************************************************ + 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Author: Georg Richter + + *************************************************************************************/ +#include "ma_schannel.h" + +#define SC_IO_BUFFER_SIZE 0x4000 + +/* {{{ LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) */ +/* + Load a pem or clr file and convert it to a binary DER object + + SYNOPSIS + ma_schannel_load_pem() + PemFileName name of the pem file (in) + buffer_len length of the converted DER binary + + DESCRIPTION + Loads a X509 file (ca, certification, key or clr) into memory and converts + it to a DER binary object. This object can be decoded and loaded into + a schannel crypto context. + If the function failed, error can be retrieved by GetLastError() + The returned binary object must be freed by caller. + + RETURN VALUE + NULL if the conversion failed or file was not found + LPBYTE * a pointer to a binary der object + buffer_len will contain the length of binary der object +*/ +static LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) +{ + HANDLE hfile; + char *buffer= NULL; + DWORD dwBytesRead= 0; + LPBYTE der_buffer= NULL; + DWORD der_buffer_length; + DWORD x; + + if (buffer_len == NULL) + return NULL; + + + if ((hfile= CreateFile(PemFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE) + return NULL; + + if (!(*buffer_len = GetFileSize(hfile, NULL))) + goto end; + + if (!(buffer= LocalAlloc(0, *buffer_len + 1))) + goto end; + + if (!ReadFile(hfile, buffer, *buffer_len, &dwBytesRead, NULL)) + goto end; + + CloseHandle(hfile); + + /* 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)LocalAlloc(0, 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; + LocalFree(buffer); + + return der_buffer; + +end: + if (hfile != INVALID_HANDLE_VALUE) + CloseHandle(hfile); + if (buffer) + LocalFree(buffer); + if (der_buffer) + LocalFree(der_buffer); + *buffer_len= 0; + return NULL; +} +/* }}} */ + +/* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file) */ +/* + Create a certification context from ca or cert file + + SYNOPSIS + ma_schannel_create_cert_context() + pem_file name of certificate or ca file + + DESCRIPTION + Loads a PEM file (certificate authority or certificate) creates a certification + context and loads the binary representation into context. + The returned context must be freed by caller. + If the function failed, error can be retrieved by GetLastError(). + + RETURNS + NULL If loading of the file or creating context failed + CERT_CONTEXT * A pointer to a certification context structure +*/ +CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file) +{ + DWORD der_buffer_length; + LPBYTE der_buffer= NULL; + + CERT_CONTEXT *ctx= NULL; + + /* create DER binary object from ca/certification file */ + if (!(der_buffer= ma_schannel_load_pem(pem_file, (DWORD *)&der_buffer_length))) + goto end; + ctx= CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + der_buffer, der_buffer_length); +end: + if (der_buffer) + LocalFree(der_buffer); + return ctx; +} +/* }}} */ + +/* {{{ PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file) */ +/* + Create a crl context from crlfile + + SYNOPSIS + ma_schannel_create_crl_context() + pem_file name of certificate or ca file + + DESCRIPTION + Loads a certification revocation list file, creates a certification + context and loads the binary representation into crl context. + The returned context must be freed by caller. + If the function failed, error can be retrieved by GetLastError(). + + RETURNS + NULL If loading of the file or creating context failed + PCCRL_CONTEXT A pointer to a certification context structure +*/ +PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file) +{ + DWORD der_buffer_length; + LPBYTE der_buffer= NULL; + + PCCRL_CONTEXT ctx= NULL; + + /* load ca pem file into memory */ + if (!(der_buffer= ma_schannel_load_pem(pem_file, (DWORD *)&der_buffer_length))) + goto end; + ctx= CertCreateCRLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + der_buffer, der_buffer_length); +end: + if (der_buffer) + LocalFree(der_buffer); + return ctx; +} +/* }}} */ + +/* {{{ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) */ +/* + Load privte key into context + + SYNOPSIS + ma_schannel_load_private_key() + ctx pointer to a certification context + pem_file name of certificate or ca file + + DESCRIPTION + Loads a certification revocation list file, creates a certification + context and loads the binary representation into crl context. + The returned context must be freed by caller. + If the function failed, error can be retrieved by GetLastError(). + + RETURNS + NULL If loading of the file or creating context failed + PCCRL_CONTEXT A pointer to a certification context structure +*/ + +my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) +{ + DWORD der_buffer_len= 0; + LPBYTE der_buffer= NULL; + DWORD priv_key_len= 0; + LPBYTE priv_key= NULL; + HCRYPTPROV crypt_prov= NULL; + HCRYPTKEY crypt_key= NULL; + CRYPT_KEY_PROV_INFO kpi; + my_bool rc= 0; + + /* load private key into der binary object */ + if (!(der_buffer= ma_schannel_load_pem(key_file, &der_buffer_len))) + return 0; + + /* determine required buffer size for decoded private key */ + if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + PKCS_RSA_PRIVATE_KEY, + der_buffer, der_buffer_len, + 0, NULL, + NULL, &priv_key_len)) + goto end; + + /* allocate buffer for decoded private key */ + if (!(priv_key= LocalAlloc(0, priv_key_len))) + goto end; + + /* decode */ + if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + PKCS_RSA_PRIVATE_KEY, + der_buffer, der_buffer_len, + 0, NULL, + priv_key, &priv_key_len)) + goto end; + + /* Acquire context */ + if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, 0)) + goto end; + + /* ... and import the private key */ + if (!CryptImportKey(crypt_prov, priv_key, priv_key_len, NULL, 0, &crypt_key)) + goto end; + + SecureZeroMemory(&kpi, sizeof(kpi)); + kpi.pwszContainerName = "cio-schanel"; + kpi.dwKeySpec = AT_KEYEXCHANGE; + kpi.dwFlags = CRYPT_MACHINE_KEYSET; + + /* assign private key to certificate context */ + if (CertSetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi)) + rc= 1; +end: + if (der_buffer) + LocalFree(der_buffer); + if (priv_key) + { + if (crypt_key) + CryptDestroyKey(crypt_key); + LocalFree(priv_key); + if (!rc) + if (crypt_prov) + CryptReleaseContext(crypt_prov, 0); + } + return rc; +} +/* }}} */ + +/* {{{ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData) */ +/* + perform handshake loop + + SYNOPSIS + ma_schannel_handshake_loop() + cio 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_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData) +{ + SecBufferDesc OutBuffer, InBuffer; + SecBuffer InBuffers[2], OutBuffers[1]; + DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer; + TimeStamp tsExpiry; + SECURITY_STATUS rc; + PUCHAR IoBuffer; + BOOL fDoRead; + MARIADB_SSL *cssl= cio->cssl; + SC_CTX *sctx= (SC_CTX *)cssl->data; + + + 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 a 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) + { + cbData = cio->methods->read(cio, IoBuffer + cbIoBuffer, SC_IO_BUFFER_SIZE - cbIoBuffer, 0); + if (cbData == SOCKET_ERROR || cbData == 0) + { + rc = SEC_E_INTERNAL_ERROR; + break; + } + 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[0].pvBuffer = NULL; + OutBuffers[0].BufferType= SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = 0; + + OutBuffer.cBuffers = 1; + OutBuffer.pBuffers = OutBuffers; + OutBuffer.ulVersion = SECBUFFER_VERSION; + + + rc = InitializeSecurityContextA(&sctx->CredHdl, + &sctx->ctxt, + 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[0].cbBuffer && OutBuffers[0].pvBuffer) + { + cbData= cio->methods->write(cio, (uchar *)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer); + if(cbData == SOCKET_ERROR || cbData == 0) + { + FreeContextBuffer(OutBuffers[0].pvBuffer); + DeleteSecurityContext(&sctx->ctxt); + return SEC_E_INTERNAL_ERROR; + } + + /* Free output context buffer */ + FreeContextBuffer(OutBuffers[0].pvBuffer); + OutBuffers[0].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; + } + + cbIoBuffer = 0; + } +loopend: + if (FAILED(rc)) + DeleteSecurityContext(&sctx->ctxt); + LocalFree(IoBuffer); + + return rc; +} +/* }}} */ + +/* {{{ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) */ +/* + performs client side handshake + + SYNOPSIS + ma_schannel_client_handshake() + cssl Pointer to a MARIADB_SSL 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_SSL *cssl) +{ + MARIADB_CIO *cio; + 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_ALLOCATE_MEMORY | ISC_REQ_STREAM; + + SecBufferDesc BufferIn, BufferOut; + SecBuffer BuffersOut[1], BuffersIn[2]; + + if (!cssl || !cssl->cio || !cssl->data) + return 1; + + cio= cssl->cio; + sctx= (SC_CTX *)cssl->data; + + /* Initialie securifty context */ + BuffersOut[0].BufferType= SECBUFFER_TOKEN; + BuffersOut[0].cbBuffer= 0; + BuffersOut[0].pvBuffer= NULL; + + BufferOut.cBuffers= 1; + BufferOut.pBuffers= BuffersOut; + BufferOut.ulVersion= SECBUFFER_VERSION; + + sRet = InitializeSecurityContext(&sctx->CredHdl, + NULL, + cio->mysql->host, + SFlags, + 0, + SECURITY_NATIVE_DREP, + NULL, + 0, + &sctx->ctxt, + &BufferOut, + &OutFlags, + NULL); + + if(sRet != SEC_I_CONTINUE_NEEDED) + return sRet; + + /* send client hello packaet */ + if(BuffersOut[0].cbBuffer != 0 && BuffersOut[0].pvBuffer != NULL) + { + r= cio->methods->write(cio, (uchar *)BuffersOut[0].pvBuffer, BuffersOut[0].cbBuffer); + if (r <= 0) + { + sRet= SEC_E_INTERNAL_ERROR; + goto end; + } + } + return ma_schannel_handshake_loop(cio, TRUE, &ExtraData); + +end: + FreeContextBuffer(BuffersOut[0].pvBuffer); + DeleteSecurityContext(&sctx->ctxt); + return sRet; +} +/* }}} */ + +/* {{{ static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLength) */ +/* + alloates an IO Buffer for ssl communication + + SYNOPSUS + ma_schannel_alloc_iobuffer() + Context SChannel context handle + BufferLength a pointer for the buffer length + + DESCRIPTION + calculates the required buffer length and allocates a memory buffer for en- and + decryption. The calculated buffer length will be returned in BufferLength pointer. + The allocated buffer needs to be freed at end of the connection. + + RETURN + NULL if an error occured + PUCHAR an IO Buffer +*/ +static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLength) +{ + SecPkgContext_StreamSizes StreamSizes; + + PUCHAR Buffer= NULL; + + if (!BufferLength || QueryContextAttributes(Context, SECPKG_ATTR_STREAM_SIZES, &StreamSizes) != SEC_E_OK) + return NULL; + + /* Calculate BufferLength */ + *BufferLength= StreamSizes.cbHeader + StreamSizes.cbTrailer + StreamSizes.cbMaximumMessage; + + Buffer= LocalAlloc(LMEM_FIXED, *BufferLength); + return Buffer; +} +/* }}} */ + +/* {{{ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext) */ +/* + Reads encrypted data from a SSL stream and decrypts it. + + SYNOPSIS + ma_schannel_read + cio 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 occured +*/ + +SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, + PCredHandle phCreds, + CtxtHandle * phContext, + DWORD *DecryptLength, + uchar *ReadBuffer, + DWORD ReadBufferSize) +{ + DWORD dwBytesRead= 0; + DWORD dwOffset= 0; + SC_CTX *sctx; + SECURITY_STATUS sRet= 0; + SecBuffersDesc Msg; + SecBuffer Buffers[4], + ExtraBuffer, + *pData, *pExtra; + int i; + + if (!cio || !cio->methods || !cio->methods->read || !cio->cssl || !cio->cssl->data | !DecryptLength) + return SEC_E_INTERNAL_ERROR; + + sctx= (SC_CTX *)cio->cssl->data; + *DecryptLength= 0; + + /* Allocate IoBuffer */ + if (!sctx->IoBuffer) + { + if (!(sctx->IoBuffer= ma_schannel_alloc_iobuffer(&sctx->ctxt, &sctx->IoBufferSize))) + { + /* todo: error */ + return NULL; + } + } + + while (1) + { + if (!dwOffset || sRet == SEC_E_INCOMPLETE_MESSAGE) + { + dwBytesRead= cio->methods->read(cio, sctx->IoBuffer + dwOffset, cbIoBufferLength - dwOffset); + if (dwBytesRead == 0) + { + /* server closed connection */ + // todo: error + printf("Server closed connection\n"); + return NULL; + } + if (dwBytesRead < 0) + { + printf("Socket error\n"); + return NULL; + } + } + ZeroMem(Buffers, sizeof(SecBuffer) * 4); + Buffers[0].pvBuffer= sctx->IoBuffer; + Buffers[0].cbBuffer= dwOffset; + + Buffers[0].BufferType= SECBUFFER_DATA; + Buffers[1].BufferType= + Buffers[2].BufferType= + Buffers[3].BufferType= SECBUFFER_EMPTY; + + Msg.ulVersion= SECBUFFER_VERSION; // Version number + Msg.cBuffers= 4; + Msg.pBuffers= Buffers; + + sRet = DecryptMessage(phContext, &Msg, 0, NULL); + + /* Check for possible errors: we continue in case context has + expired or renogitiation is required */ + if (sRet != SEC_E_OK && sRet != SEC_I_CONTEXT_EXPIRED && + sRet != SEC_I_RENEGOTIARE) + { + // set error + return sRet; + } + + pData= pExtra= NULL; + for (i=0; i < 4; i++) + { + if (!pData && Buffers[i].BufferType == SECBUFFER_DATA) + pData= &Buffers[i]; + if (!pExtra && Buffers[i].BufferType == SECBUFFER_EXTRA) + pExtra= &Buffers[i]; + if (pData && pExtra) + break; + } + + if (pData && pData->cbBuffer) + { + memcpy(ReadBuffer + *DecrypthLength, pData->pvBuffer, pData->cbBuffer); + *DecryptLength+= pData->cbBuffer; + } + + if (pExtra) + { + MoveMemory(sctx->IoBuffer, pExtra->pvBuffer, pExtra->cbBuffer); + dwOffset= pExtra->cbBuffer; + } + else + dwOffset= 0; + } +} +/* }}} */ diff --git a/plugins/builtin/ma_schannel.h b/plugins/builtin/ma_schannel.h new file mode 100644 index 00000000..006613ef --- /dev/null +++ b/plugins/builtin/ma_schannel.h @@ -0,0 +1,72 @@ +/************************************************************************************ + 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 + 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 +#include +#include +#include +#include +#include + +typedef void VOID; + +#include +#define SECURITY_WIN32 +#include +#include +#undef SECURITY_WIN32 +#include + +#define SC_IO_BUFFER_SIZE 0x4000 + +CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file); +SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData); +my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file); +PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file); + +#ifndef HAVE_SCHANNEL_DEFAULT +#define my_snprintf snprintf +#define my_vsnprintf vsnprintf +#undef SAFE_MUTEX +#endif +#include + +struct st_schannel { + HCERTSTORE cert_store; + CERT_CONTEXT *client_cert_ctx; + CERT_CONTEXT *client_ca_ctx; + CRL_CONTEXT *client_crl_ctx; + CredHandle CredHdl; + PUCHAR IoBuffer; + DWORD IoBufferSize; + PUCHAR DecryptBuffer; + DWORD DecryptBufferSize; + DWORD DecryptBufferLength; + CtxtHandle ctxt; +}; + +typedef struct st_schannel SC_CTX; + +#endif /* _ma_schannel_h_ */ diff --git a/libmariadb/my_auth.c b/plugins/builtin/my_auth.c similarity index 85% rename from libmariadb/my_auth.c rename to plugins/builtin/my_auth.c index 98c79678..2c8d3510 100644 --- a/libmariadb/my_auth.c +++ b/plugins/builtin/my_auth.c @@ -1,36 +1,10 @@ -/************************************************************************************ - Copyright (C) 2012-2015 Monty Program AB, 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 - or write to the Free Software Foundation, Inc., - 51 Franklin St., Fifth Floor, Boston, MA 02110, USA - - Part of this code includes code from the PHP project which - is freely available from http://www.php.net - - Originally written by Sergei Golubchik -*************************************************************************************/ #include #include #include #include #include #include -#include -#ifdef HAVE_OPENSSL -#include -#endif +#include typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t); @@ -39,10 +13,12 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); extern void read_user_name(char *name); extern uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer); +/* #define compile_time_assert(A) \ do {\ typedef char constraint[(A) ? 1 : -1];\ } while (0); +*/ auth_plugin_t native_password_client_plugin= { @@ -52,6 +28,7 @@ auth_plugin_t native_password_client_plugin= "R.J.Silk, Sergei Golubchik", "Native MySQL authentication", {1, 0, 0}, + "LGPL", NULL, NULL, native_password_auth_client @@ -65,6 +42,7 @@ auth_plugin_t old_password_client_plugin= "R.J.Silk, Sergei Golubchik", "Old MySQL-3.23 authentication", {1, 0, 0}, + "LGPL", NULL, NULL, old_password_auth_client @@ -254,30 +232,14 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (mysql->client_flag & CLIENT_MULTI_STATEMENTS) mysql->client_flag|= CLIENT_MULTI_RESULTS; -#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) +#if defined(HAVE_SSL) && !defined(EMBEDDED_LIBRARY) if (mysql->options.ssl_key || mysql->options.ssl_cert || mysql->options.ssl_ca || mysql->options.ssl_capath || mysql->options.ssl_cipher) mysql->options.use_ssl= 1; if (mysql->options.use_ssl) mysql->client_flag|= CLIENT_SSL; - - /* if server doesn't support SSL and verification of server certificate - was set to mandator, we need to return an error */ - if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL)) - { - if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) || - (mysql->options.extension && (mysql->options.extension->ssl_fp || - mysql->options.extension->ssl_fp_list))) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Server doesn't support SSL"); - goto error; - } - } - -#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/ +#endif /* HAVE_SSL && !EMBEDDED_LIBRARY*/ if (mpvio->db) mysql->client_flag|= CLIENT_CONNECT_WITH_DB; @@ -305,7 +267,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, int3store(buff+2, net->max_packet_size); end= buff+5; } -#ifdef HAVE_OPENSSL +#ifdef HAVE_SSL if (mysql->options.ssl_key || mysql->options.ssl_cert || mysql->options.ssl_ca || @@ -318,11 +280,9 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, #endif ) mysql->options.use_ssl= 1; - if (mysql->options.use_ssl && (mysql->client_flag & CLIENT_SSL)) { - SSL *ssl; /* Send mysql->client_flag, max_packet_size - unencrypted otherwise the server does not know we want to do SSL @@ -335,38 +295,15 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, errno); goto error; } - - /* Create SSL */ - if (!(ssl= my_ssl_init(mysql))) - goto error; - - /* Connect to the server */ - if (my_ssl_connect(ssl)) - { - SSL_free(ssl); - goto error; - } - - if (mysql->options.extension && - (mysql->options.extension->ssl_fp || mysql->options.extension->ssl_fp_list)) - { - if (ma_ssl_verify_fingerprint(ssl)) - goto error; - } - - if ((mysql->options.ssl_ca || mysql->options.ssl_capath) && - (mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && - my_ssl_verify_server_cert(ssl)) + if (ma_cio_start_ssl(mysql->net.cio)) goto error; } -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_SSL */ - DBUG_PRINT("info",("Server version = '%s' capabilities: %lu status: %u client_flag: %lu", + DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu", mysql->server_version, mysql->server_capabilities, mysql->server_status, mysql->client_flag)); - compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH); - /* This needs to be changed as it's not useful with big packets */ if (mysql->user[0]) strmake(end, mysql->user, USERNAME_LENGTH); @@ -527,18 +464,19 @@ static int client_mpvio_write_packet(struct st_plugin_vio *mpv, connection */ -void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) +void mpvio_info(MARIADB_CIO *cio, MYSQL_PLUGIN_VIO_INFO *info) { bzero(info, sizeof(*info)); - switch (vio->type) { - case VIO_TYPE_TCPIP: + switch (cio->type) { + case CIO_TYPE_SOCKET: info->protocol= MYSQL_VIO_TCP; - info->socket= vio->sd; + ma_cio_get_handle(cio, &info->socket); return; - case VIO_TYPE_SOCKET: + case CIO_TYPE_UNIXSOCKET: info->protocol= MYSQL_VIO_SOCKET; - info->socket= vio->sd; + ma_cio_get_handle(cio, &info->socket); return; + /* case VIO_TYPE_SSL: { struct sockaddr addr; @@ -550,11 +488,14 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) info->socket= vio->sd; return; } + */ #ifdef _WIN32 + /* case VIO_TYPE_NAMEDPIPE: info->protocol= MYSQL_VIO_PIPE; info->handle= vio->hPipe; return; + */ /* not supported yet case VIO_TYPE_SHARED_MEMORY: info->protocol= MYSQL_VIO_MEMORY; @@ -570,7 +511,7 @@ static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio, MYSQL_PLUGIN_VIO_INFO *info) { MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio; - mpvio_info(mpvio->mysql->net.vio, info); + mpvio_info(mpvio->mysql->net.cio, info); } /** @@ -637,8 +578,6 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); - compile_time_assert(CR_OK == -1); - compile_time_assert(CR_ERROR == 0); if (res > CR_OK && mysql->net.read_pos[0] != 254) { /* diff --git a/plugins/cio/CMakeLists.txt b/plugins/cio/CMakeLists.txt new file mode 100644 index 00000000..febc3d55 --- /dev/null +++ b/plugins/cio/CMakeLists.txt @@ -0,0 +1,28 @@ +IF(WIN32) + SET(EXPORT_FILE "cio_plugin.def") +ENDIF() + +IF(GNUTLS_FOUND) + IF (NOT ${DEFAULT_SSL} STREQUAL "GNUTLS") + SET(CMAKE_SHARED_LIBRARY_PREFIX "") + ADD_LIBRARY(cio_gnutls SHARED cio_gnutls.c ${EXPORT_FILE}) + TARGET_LINK_LIBRARIES(cio_gnutls ${GNUTLS_LIBRARIES}) + ENDIF() +ENDIF() + +IF(OPENSSL_FOUND) + IF (NOT ${DEFAULT_SSL} STREQUAL "OPENSSL") + SET(CMAKE_SHARED_LIBRARY_PREFIX "") + SET(source_files cio_openssl.c ${EXPORT_FILE}) + ADD_LIBRARY(cio_openssl SHARED ${source_files}) + TARGET_LINK_LIBRARIES(cio_openssl ${OPENSSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF() +ENDIF() + +IF(WIN32) + SET(CMAKE_SHARED_LIBRARY_PREFIX "") + IF (NOT ${DEFAULT_SSL} STREQUAL "SCHANNEL") + ADD_LIBRARY(cio_schannel SHARED cio_schannel.c ${EXPORT_FILE}) + ENDIF() + ADD_LIBRARY(cio_npipe SHARED cio_npipe.c ${EXPORT_FILE}) +ENDIF() diff --git a/plugins/cio/cio_npipe.c b/plugins/cio/cio_npipe.c new file mode 100644 index 00000000..7fa688e5 --- /dev/null +++ b/plugins/cio/cio_npipe.c @@ -0,0 +1,301 @@ +/* MariaDB Communication IO (CIO) plugin for named pipe communication */ + +#ifdef _WIN32 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_NPIPE_DEFAULT +#define my_malloc(A, B) malloc((A)) +#undef my_free +#define my_free(A,B) free(((A))) +#endif + + +my_bool cio_npipe_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); +int cio_npipe_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type); +size_t cio_npipe_read(MARIADB_CIO *cio, uchar *buffer, size_t length); +size_t cio_npipe_write(MARIADB_CIO *cio, uchar *buffer, size_t length); +int cio_npipe_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout); +my_bool cio_npipe_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value); +my_bool cio_npipe_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo); +my_bool cio_npipe_close(MARIADB_CIO *cio); +int cio_npipe_fast_send(MARIADB_CIO *cio); +int cio_npipe_keepalive(MARIADB_CIO *cio); +my_socket cio_npipe_get_socket(MARIADB_CIO *cio); +my_bool cio_npipe_is_blocking(MARIADB_CIO *cio); + +struct st_ma_cio_methods cio_npipe_methods= { + cio_npipe_set_timeout, + cio_npipe_get_timeout, + cio_npipe_read, + cio_npipe_write, + cio_npipe_wait_io_or_timeout, + cio_npipe_blocking, + cio_npipe_connect, + cio_npipe_close, + cio_npipe_fast_send, + cio_npipe_keepalive, + cio_npipe_get_socket, + cio_npipe_is_blocking +}; + +MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = +{ + MYSQL_CLIENT_CIO_PLUGIN, + MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + "cio_npipe", + "Georg Richter", + "MariaDB communication IO plugin for named pipe communication", + {1, 0, 0}, + NULL, + NULL, + &cio_npipe_methods, + NULL, + NULL +}; + +struct st_cio_npipe { + HANDLE pipe; + OVERLAPPED overlapped; + size_t rw_size; + int fcntl_mode; + MYSQL *mysql; +}; + +my_bool cio_npipe_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout) +{ + if (!cio) + return 1; + cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1; + return 0; +} + +int cio_npipe_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type) +{ + if (!cio) + return -1; + return cio->timeout[type] / 1000; +} + +size_t cio_npipe_read(MARIADB_CIO *cio, uchar *buffer, size_t length) +{ + DWORD dwRead= 0; + size_t r= -1; + struct st_cio_npipe *cpipe= NULL; + + if (!cio || !cio->data) + return -1; + + cpipe= (struct st_cio_npipe *)cio->data; + + if (ReadFile(cpipe->pipe, buffer, length, &dwRead, &cpipe->overlapped)) + { + r= (size_t)dwRead; + goto end; + } + if (GetLastError() == ERROR_IO_PENDING) + r= cio_npipe_wait_io_or_timeout(cio, 1, 0); + + if (!r) + r= cpipe->rw_size; +end: + return r; +} + +size_t cio_npipe_write(MARIADB_CIO *cio, uchar *buffer, size_t length) +{ + DWORD dwWrite= 0; + size_t r= -1; + struct st_cio_npipe *cpipe= NULL; + + if (!cio || !cio->data) + return -1; + + cpipe= (struct st_cio_npipe *)cio->data; + + if (WriteFile(cpipe->pipe, buffer, length, &dwWrite, &cpipe->overlapped)) + { + r= (size_t)dwWrite; + goto end; + } + if (GetLastError() == ERROR_IO_PENDING) + r= cio_npipe_wait_io_or_timeout(cio, 1, 0); + + if (!r) + r= cpipe->rw_size; +end: + return r; +} + +int cio_npipe_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout) +{ + int r= -1; + DWORD status; + int save_error; + struct st_cio_npipe *cpipe= NULL; + + cpipe= (struct st_cio_npipe *)cio->data; + + if (!timeout) + timeout= (is_read) ? cio->timeout[CIO_READ_TIMEOUT] : cio->timeout[CIO_WRITE_TIMEOUT]; + + status= WaitForSingleObject(cpipe->overlapped.hEvent, timeout); + if (status == WAIT_OBJECT_0) + { + if (GetOverlappedResult(cpipe->pipe, &cpipe->overlapped, &cpipe->rw_size, FALSE)) + return 0; + } + /* other status codes are: WAIT_ABANDONED, WAIT_TIMEOUT and WAIT_FAILED */ + save_error= GetLastError(); + CancelIo(cpipe->pipe); + SetLastError(save_error); + return -1; +} + +my_bool cio_npipe_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode) +{ + /* not supported */ + return 0; +} + +int cio_npipe_keepalive(MARIADB_CIO *cio) +{ + /* not supported */ + return 0; +} + +int cio_npipe_fast_send(MARIADB_CIO *cio) +{ + /* not supported */ + return 0; +} +my_bool cio_npipe_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo) +{ + struct st_cio_npipe *cpipe= NULL; + + if (!cio || !cinfo) + return 1; + + if (!(cpipe= (struct st_cio_npipe *)my_malloc(sizeof(struct st_cio_npipe), MYF(0)))) + { + CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, ""); + return 1; + } + bzero(cpipe, sizeof(struct st_cio_npipe)); + cio->data= (void *)cpipe; + cpipe->pipe= INVALID_HANDLE_VALUE; + cio->mysql= cinfo->mysql; + cio->type= cinfo->type; + + if (cinfo->type == CIO_TYPE_NAMEDPIPE) + { + my_bool has_timedout= 0; + char szPipeName[MAX_PATH]; + DWORD dwMode; + + if ( ! cinfo->unix_socket || (cinfo->unix_socket)[0] == 0x00) + cinfo->unix_socket = MYSQL_NAMEDPIPE; + if (!cinfo->host || !strcmp(cinfo->host,LOCAL_HOST)) + cinfo->host=LOCAL_HOST_NAMEDPIPE; + + szPipeName[MAX_PATH - 1]= 0; + snprintf(szPipeName, MAX_PATH - 1, "\\\\%s\\pipe\\%s", cinfo->host, cinfo->unix_socket); + + while (1) + { + if ((cpipe->pipe = CreateFile(szPipeName, + GENERIC_READ | + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes */ + OPEN_EXISTING, + 0, /* default attributes */ + NULL)) != INVALID_HANDLE_VALUE) + break; + + if (GetLastError() != ERROR_PIPE_BUSY) + { + cio->set_error(cio, CR_NAMEDPIPEOPEN_ERROR, SQLSTATE_UNKNOWN, 0, + cinfo->host, cinfo->unix_socket, GetLastError()); + goto end; + } + + if (has_timedout || !WaitNamedPipe(szPipeName, cio->timeout[CIO_CONNECT_TIMEOUT])) + { + cio->set_error(cio, CR_NAMEDPIPEWAIT_ERROR, SQLSTATE_UNKNOWN, 0, + cinfo->host, cinfo->unix_socket, GetLastError()); + goto end; + } + has_timedout= 1; + } + + dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; + if (!SetNamedPipeHandleState(cpipe->pipe, &dwMode, NULL, NULL)) + { + cio->set_error(cio, CR_NAMEDPIPESETSTATE_ERROR, SQLSTATE_UNKNOWN, 0, + cinfo->host, cinfo->unix_socket, (ulong) GetLastError()); + goto end; + } + + /* Register event handler for overlapped IO */ + if (!(cpipe->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL))) + { + cio->set_error(cio, CR_EVENT_CREATE_FAILED, SQLSTATE_UNKNOWN, 0, + GetLastError()); + goto end; + } + return 0; + } +end: + if (cpipe) + { + if (cpipe->pipe != INVALID_HANDLE_VALUE) + CloseHandle(cpipe->pipe); + my_free((gptr)cpipe, MYF(0)); + cio->data= NULL; + } + return 1; +} + +my_bool cio_npipe_close(MARIADB_CIO *cio) +{ + struct st_cio_npipe *cpipe= NULL; + int r= 0; + + if (!cio) + return 1; + + if (cio->data) + { + cpipe= (struct st_cio_npipe *)cio->data; + CloseHandle(cpipe->overlapped.hEvent); + if (cpipe->pipe != INVALID_HANDLE_VALUE) + { + CloseHandle(cpipe->pipe); + cpipe->pipe= INVALID_HANDLE_VALUE; + } + my_free((gptr)cio->data, MYF(0)); + cio->data= NULL; + } + return r; +} + +my_socket cio_npipe_get_socket(MARIADB_CIO *cio) +{ + if (cio && cio->data) + return (my_socket)((struct st_cio_npipe *)cio->data)->pipe; + return INVALID_SOCKET; +} + +my_bool cio_npipe_is_blocking(MARIADB_CIO *cio) +{ + return 1; +} + +#endif diff --git a/plugins/cio/cio_plugin.def b/plugins/cio/cio_plugin.def new file mode 100644 index 00000000..70af9256 --- /dev/null +++ b/plugins/cio/cio_plugin.def @@ -0,0 +1,2 @@ +EXPORTS + _mysql_client_plugin_declaration_ DATA diff --git a/plugins/cio/cio_shmem.c b/plugins/cio/cio_shmem.c new file mode 100644 index 00000000..70deedbf --- /dev/null +++ b/plugins/cio/cio_shmem.c @@ -0,0 +1,307 @@ +/* MariaDB Communication IO (CIO) plugin for shate memory communication + * + * During initialization MariaDB serve creates a named file mapping + * object, named : * + * + * */ + +#ifdef _WIN32 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_NPIPE_DEFAULT +#define my_malloc(A, B) malloc((A)) +#undef my_free +#define my_free(A,B) free(((A))) +#endif + + + +my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); +int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type); +size_t cio_shm_read(MARIADB_CIO *cio, uchar *buffer, size_t length); +size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length); +int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout); +my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value); +my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo); +my_bool cio_shm_close(MARIADB_CIO *cio); +int cio_shm_fast_send(MARIADB_CIO *cio); +int cio_shm_keepalive(MARIADB_CIO *cio); +my_socket cio_shm_get_socket(MARIADB_CIO *cio); +my_bool cio_shm_is_blocking(MARIADB_CIO *cio); + +struct st_ma_cio_methods cio_shm_methods= { + cio_shm_set_timeout, + cio_shm_get_timeout, + cio_shm_read, + cio_shm_write, + cio_shm_wait_io_or_timeout, + cio_shm_blocking, + cio_shm_connect, + cio_shm_close, + cio_shm_fast_send, + cio_shm_keepalive, + cio_shm_get_socket, + cio_shm_is_blocking +}; + +MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = +{ + MYSQL_CLIENT_CIO_PLUGIN, + MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + "cio_shm", + "Georg Richter", + "MariaDB communication IO plugin for named pipe communication", + {1, 0, 0}, + NULL, + NULL, + &cio_shm_methods, + NULL, + NULL +}; + +struct st_cio_shm { + HANDLE pipe; + OVERLAPPED overlapped; + size_t rw_size; + int fcntl_mode; + MYSQL *mysql; +}; + +my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout) +{ + if (!cio) + return 1; + cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1; + return 0; +} + +int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type) +{ + if (!cio) + return -1; + return cio->timeout[type] / 1000; +} + +size_t cio_shm_read(MARIADB_CIO *cio, uchar *buffer, size_t length) +{ + DWORD dwRead= 0; + size_t r= -1; + struct st_cio_shm *cpipe= NULL; + + if (!cio || !cio->data) + return -1; + + cpipe= (struct st_cio_shm *)cio->data; + + if (ReadFile(cpipe->pipe, buffer, length, &dwRead, &cpipe->overlapped)) + { + r= (size_t)dwRead; + goto end; + } + if (GetLastError() == ERROR_IO_PENDING) + r= cio_shm_wait_io_or_timeout(cio, 1, 0); + + if (!r) + r= cpipe->rw_size; +end: + return r; +} + +size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length) +{ + DWORD dwWrite= 0; + size_t r= -1; + struct st_cio_shm *cpipe= NULL; + + if (!cio || !cio->data) + return -1; + + cpipe= (struct st_cio_shm *)cio->data; + + if (WriteFile(cpipe->pipe, buffer, length, &dwWrite, &cpipe->overlapped)) + { + r= (size_t)dwWrite; + goto end; + } + if (GetLastError() == ERROR_IO_PENDING) + r= cio_shm_wait_io_or_timeout(cio, 1, 0); + + if (!r) + r= cpipe->rw_size; +end: + return r; +} + +int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout) +{ + int r= -1; + DWORD status; + int save_error; + struct st_cio_shm *cpipe= NULL; + + cpipe= (struct st_cio_shm *)cio->data; + + if (!timeout) + timeout= (is_read) ? cio->timeout[CIO_READ_TIMEOUT] : cio->timeout[CIO_WRITE_TIMEOUT]; + + status= WaitForSingleObject(cpipe->overlapped.hEvent, timeout); + if (status == WAIT_OBJECT_0) + { + if (GetOverlappedResult(cpipe->pipe, &cpipe->overlapped, &cpipe->rw_size, FALSE)) + return 0; + } + /* other status codes are: WAIT_ABANDONED, WAIT_TIMEOUT and WAIT_FAILED */ + save_error= GetLastError(); + CancelIo(cpipe->pipe); + SetLastError(save_error); + return -1; +} + +my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode) +{ + /* not supported */ + return 0; +} + +int cio_shm_keepalive(MARIADB_CIO *cio) +{ + /* not supported */ + return 0; +} + +int cio_shm_fast_send(MARIADB_CIO *cio) +{ + /* not supported */ + return 0; +} +my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo) +{ + struct st_cio_shm *cpipe= NULL; + + if (!cio || !cinfo) + return 1; + + if (!(cpipe= (struct st_cio_shm *)my_malloc(sizeof(struct st_cio_shm), MYF(0)))) + { + CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, ""); + return 1; + } + bzero(cpipe, sizeof(struct st_cio_shm)); + cio->data= (void *)cpipe; + cpipe->pipe= INVALID_HANDLE_VALUE; + cio->mysql= cinfo->mysql; + cio->type= cinfo->type; + + if (cinfo->type == CIO_TYPE_NAMEDPIPE) + { + my_bool has_timedout= 0; + char szPipeName[MAX_PATH]; + DWORD dwMode; + + if ( ! cinfo->unix_socket || (cinfo->unix_socket)[0] == 0x00) + cinfo->unix_socket = MYSQL_NAMEDPIPE; + if (!cinfo->host || !strcmp(cinfo->host,LOCAL_HOST)) + cinfo->host=LOCAL_HOST_NAMEDPIPE; + + szPipeName[MAX_PATH - 1]= 0; + snprintf(szPipeName, MAX_PATH - 1, "\\\\%s\\pipe\\%s", cinfo->host, cinfo->unix_socket); + + while (1) + { + if ((cpipe->pipe = CreateFile(szPipeName, + GENERIC_READ | + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes */ + OPEN_EXISTING, + 0, /* default attributes */ + NULL)) != INVALID_HANDLE_VALUE) + break; + + if (GetLastError() != ERROR_PIPE_BUSY) + { + cio->set_error(cio, CR_NAMEDPIPEOPEN_ERROR, SQLSTATE_UNKNOWN, 0, + cinfo->host, cinfo->unix_socket, GetLastError()); + goto end; + } + + if (has_timedout || !WaitNamedPipe(szPipeName, cio->timeout[CIO_CONNECT_TIMEOUT])) + { + cio->set_error(cio, CR_NAMEDPIPEWAIT_ERROR, SQLSTATE_UNKNOWN, 0, + cinfo->host, cinfo->unix_socket, GetLastError()); + goto end; + } + has_timedout= 1; + } + + dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; + if (!SetNamedPipeHandleState(cpipe->pipe, &dwMode, NULL, NULL)) + { + cio->set_error(cio, CR_NAMEDPIPESETSTATE_ERROR, SQLSTATE_UNKNOWN, 0, + cinfo->host, cinfo->unix_socket, (ulong) GetLastError()); + goto end; + } + + /* Register event handler for overlapped IO */ + if (!(cpipe->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL))) + { + cio->set_error(cio, CR_CREATE_EVENT_FAILED, SQLSTATE_UNKNOWN, 0, + GetLastError()); + goto end; + } + return 0; + } +end: + if (cpipe) + { + if (cpipe->pipe != INVALID_HANDLE_VALUE) + CloseHandle(cpipe->pipe); + my_free((gptr)cpipe, MYF(0)); + cio->data= NULL; + } + return 1; +} + +my_bool cio_shm_close(MARIADB_CIO *cio) +{ + struct st_cio_shm *cpipe= NULL; + int r= 0; + + if (!cio) + return 1; + + if (cio->data) + { + cpipe= (struct st_cio_shm *)cio->data; + CloseHandle(cpipe->overlapped.hEvent); + if (cpipe->pipe != INVALID_HANDLE_VALUE) + { + CloseHandle(cpipe->pipe); + cpipe->pipe= INVALID_HANDLE_VALUE; + } + my_free((gptr)cio->data, MYF(0)); + cio->data= NULL; + } + return r; +} + +my_socket cio_shm_get_socket(MARIADB_CIO *cio) +{ + if (cio && cio->data) + return (my_socket)((struct st_cio_shm *)cio->data)->pipe; + return INVALID_SOCKET; +} + +my_bool cio_shm_is_blocking(MARIADB_CIO *cio) +{ + return 1; +} + +#endif diff --git a/plugins/cio/tls_schannel.c b/plugins/cio/tls_schannel.c new file mode 100644 index 00000000..6fb27536 --- /dev/null +++ b/plugins/cio/tls_schannel.c @@ -0,0 +1,1257 @@ + * SSL/TLS interface functions for Microsoft Schannel + + * Copyright (c) 2005-2009, Jouni Malinen + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License version 2 as + + * published by the Free Software Foundation. + + * + + * Alternatively, this software may be distributed under the terms of BSD + + * license. + + * + + * See README and COPYING for more details. + + */ + +/* + + * FIX: Go through all SSPI functions and verify what needs to be freed + + * FIX: session resumption + + * TODO: add support for server cert chain validation + + * TODO: add support for CA cert validation + + * TODO: add support for EAP-TLS (client cert/key conf) + + */ + +#include "includes.h" + +#include + +#include + +#include + +#define SECURITY_WIN32 + +#include + +#include + +#include "common.h" + +#include "tls.h" + +struct tls_global { + + HMODULE hsecurity; + + PSecurityFunctionTable sspi; + + HCERTSTORE my_cert_store; + +}; + +struct tls_connection { + + int established, start; + + int failed, read_alerts, write_alerts; + + SCHANNEL_CRED schannel_cred; + + CredHandle creds; + + CtxtHandle context; + + u8 eap_tls_prf[128]; + + int eap_tls_prf_set; + +}; + +static int schannel_load_lib(struct tls_global *global) + +{ + + INIT_SECURITY_INTERFACE pInitSecurityInterface; + + global->hsecurity = LoadLibrary(TEXT("Secur32.dll")); + + if (global->hsecurity == NULL) { + + wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", + + __func__, (unsigned int) GetLastError()); + + return -1; + + } + + pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( + + global->hsecurity, "InitSecurityInterfaceA"); + + if (pInitSecurityInterface == NULL) { + + wpa_printf(MSG_ERROR, "%s: Could not find " + + "InitSecurityInterfaceA from Secur32.dll", + + __func__); + + FreeLibrary(global->hsecurity); + + global->hsecurity = NULL; + + return -1; + + } + + global->sspi = pInitSecurityInterface(); + + if (global->sspi == NULL) { + + wpa_printf(MSG_ERROR, "%s: Could not read security " + + "interface - 0x%x", + + __func__, (unsigned int) GetLastError()); + + FreeLibrary(global->hsecurity); + + global->hsecurity = NULL; + + return -1; + + } + + return 0; + +} + +void * tls_init(const struct tls_config *conf) + +{ + + struct tls_global *global; + + global = os_zalloc(sizeof(*global)); + + if (global == NULL) + + return NULL; + + if (schannel_load_lib(global)) { + + os_free(global); + + return NULL; + + } + + return global; + +} + +void tls_deinit(void *ssl_ctx) + +{ + + struct tls_global *global = ssl_ctx; + + if (global->my_cert_store) + + CertCloseStore(global->my_cert_store, 0); + + FreeLibrary(global->hsecurity); + + os_free(global); + +} + +int tls_get_errors(void *ssl_ctx) + +{ + + return 0; + +} + +struct tls_connection * tls_connection_init(void *ssl_ctx) + +{ + + struct tls_connection *conn; + + conn = os_zalloc(sizeof(*conn)); + + if (conn == NULL) + + return NULL; + + conn->start = 1; + + return conn; + +} + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) + +{ + + if (conn == NULL) + + return; + + os_free(conn); + +} + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) + +{ + + return conn ? conn->established : 0; + +} + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) + +{ + + struct tls_global *global = ssl_ctx; + + if (conn == NULL) + + return -1; + + conn->eap_tls_prf_set = 0; + + conn->established = conn->failed = 0; + + conn->read_alerts = conn->write_alerts = 0; + + global->sspi->DeleteSecurityContext(&conn->context); + + /* FIX: what else needs to be reseted? */ + + return 0; + +} + +int tls_global_set_params(void *tls_ctx, + + const struct tls_connection_params *params) + +{ + + return -1; + +} + +int tls_global_set_verify(void *ssl_ctx, int check_crl) + +{ + + return -1; + +} + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + + int verify_peer) + +{ + + return -1; + +} + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + + struct tls_keys *keys) + +{ + + /* Schannel does not export master secret or client/server random. */ + + return -1; + +} + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + + const char *label, int server_random_first, + + u8 *out, size_t out_len) + +{ + + /* + + * Cannot get master_key from Schannel, but EapKeyBlock can be used to + + * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and + + * EAP-TTLS cannot use this, though, since they are using different + + * labels. The only option could be to implement TLSv1 completely here + + * and just use Schannel or CryptoAPI for low-level crypto + + * functionality.. + + */ + + if (conn == NULL || !conn->eap_tls_prf_set || server_random_first || + + os_strcmp(label, "client EAP encryption") != 0 || + + out_len > sizeof(conn->eap_tls_prf)) + + return -1; + + os_memcpy(out, conn->eap_tls_prf, out_len); + + return 0; + +} + +static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, + + struct tls_connection *conn) + +{ + + DWORD sspi_flags, sspi_flags_out; + + SecBufferDesc outbuf; + + SecBuffer outbufs[1]; + + SECURITY_STATUS status; + + TimeStamp ts_expiry; + + sspi_flags = ISC_REQ_REPLAY_DETECT | + + ISC_REQ_CONFIDENTIALITY | + + ISC_RET_EXTENDED_ERROR | + + ISC_REQ_ALLOCATE_MEMORY | + + ISC_REQ_MANUAL_CRED_VALIDATION; + + wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__); + + outbufs[0].pvBuffer = NULL; + + outbufs[0].BufferType = SECBUFFER_TOKEN; + + outbufs[0].cbBuffer = 0; + + outbuf.cBuffers = 1; + + outbuf.pBuffers = outbufs; + + outbuf.ulVersion = SECBUFFER_VERSION; + +#ifdef UNICODE + + status = global->sspi->InitializeSecurityContextW( + + &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, + + SECURITY_NATIVE_DREP, NULL, 0, &conn->context, + + &outbuf, &sspi_flags_out, &ts_expiry); + +#else /* UNICODE */ + + status = global->sspi->InitializeSecurityContextA( + + &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, + + SECURITY_NATIVE_DREP, NULL, 0, &conn->context, + + &outbuf, &sspi_flags_out, &ts_expiry); + +#endif /* UNICODE */ + + if (status != SEC_I_CONTINUE_NEEDED) { + + wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " + + "failed - 0x%x", + + __func__, (unsigned int) status); + + return NULL; + + } + + if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { + + struct wpabuf *buf; + + wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", + + outbufs[0].pvBuffer, outbufs[0].cbBuffer); + + conn->start = 0; + + buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, + + outbufs[0].cbBuffer); + + if (buf == NULL) + + return NULL; + + global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); + + return buf; + + } + + wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello"); + + return NULL; + +} + +#ifndef SECPKG_ATTR_EAP_KEY_BLOCK + +#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b + +typedef struct _SecPkgContext_EapKeyBlock { + + BYTE rgbKeys[128]; + + BYTE rgbIVs[64]; + +} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock; + +#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */ + +static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) + +{ + + SECURITY_STATUS status; + + SecPkgContext_EapKeyBlock kb; + + /* Note: Windows NT and Windows Me/98/95 do not support getting + + * EapKeyBlock */ + + status = global->sspi->QueryContextAttributes( + + &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb); + + if (status != SEC_E_OK) { + + wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes(" + + "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)", + + __func__, (int) status); + + return -1; + + } + + wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys", + + kb.rgbKeys, sizeof(kb.rgbKeys)); + + wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs", + + kb.rgbIVs, sizeof(kb.rgbIVs)); + + os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); + + conn->eap_tls_prf_set = 1; + + return 0; + +} + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + + struct tls_connection *conn, + + const struct wpabuf *in_data, + + struct wpabuf **appl_data) + +{ + + struct tls_global *global = tls_ctx; + + DWORD sspi_flags, sspi_flags_out; + + SecBufferDesc inbuf, outbuf; + + SecBuffer inbufs[2], outbufs[1]; + + SECURITY_STATUS status; + + TimeStamp ts_expiry; + + struct wpabuf *out_buf = NULL; + + if (appl_data) + + *appl_data = NULL; + + if (conn->start) + + return tls_conn_hs_clienthello(global, conn); + + wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", + + (int) wpabuf_len(in_data)); + + sspi_flags = ISC_REQ_REPLAY_DETECT | + + ISC_REQ_CONFIDENTIALITY | + + ISC_RET_EXTENDED_ERROR | + + ISC_REQ_ALLOCATE_MEMORY | + + ISC_REQ_MANUAL_CRED_VALIDATION; + + /* Input buffer for Schannel */ + + inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data); + + inbufs[0].cbBuffer = wpabuf_len(in_data); + + inbufs[0].BufferType = SECBUFFER_TOKEN; + + /* Place for leftover data from Schannel */ + + inbufs[1].pvBuffer = NULL; + + inbufs[1].cbBuffer = 0; + + inbufs[1].BufferType = SECBUFFER_EMPTY; + + inbuf.cBuffers = 2; + + inbuf.pBuffers = inbufs; + + inbuf.ulVersion = SECBUFFER_VERSION; + + /* Output buffer for Schannel */ + + outbufs[0].pvBuffer = NULL; + + outbufs[0].cbBuffer = 0; + + outbufs[0].BufferType = SECBUFFER_TOKEN; + + outbuf.cBuffers = 1; + + outbuf.pBuffers = outbufs; + + outbuf.ulVersion = SECBUFFER_VERSION; + +#ifdef UNICODE + + status = global->sspi->InitializeSecurityContextW( + + &conn->creds, &conn->context, NULL, sspi_flags, 0, + + SECURITY_NATIVE_DREP, &inbuf, 0, NULL, + + &outbuf, &sspi_flags_out, &ts_expiry); + +#else /* UNICODE */ + + status = global->sspi->InitializeSecurityContextA( + + &conn->creds, &conn->context, NULL, sspi_flags, 0, + + SECURITY_NATIVE_DREP, &inbuf, 0, NULL, + + &outbuf, &sspi_flags_out, &ts_expiry); + +#endif /* UNICODE */ + + wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> " + + "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " + + "intype[1]=%d outlen[0]=%d", + + (int) status, (int) inbufs[0].cbBuffer, + + (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, + + (int) inbufs[1].BufferType, + + (int) outbufs[0].cbBuffer); + + if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || + + (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { + + if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { + + wpa_hexdump(MSG_MSGDUMP, "SChannel - output", + + outbufs[0].pvBuffer, outbufs[0].cbBuffer); + + out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, + + outbufs[0].cbBuffer); + + global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); + + outbufs[0].pvBuffer = NULL; + + if (out_buf == NULL) + + return NULL; + + } + + } + + switch (status) { + + case SEC_E_INCOMPLETE_MESSAGE: + + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); + + break; + + case SEC_I_CONTINUE_NEEDED: + + wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); + + break; + + case SEC_E_OK: + + /* TODO: verify server certificate chain */ + + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " + + "completed successfully"); + + conn->established = 1; + + tls_get_eap(global, conn); + + /* Need to return something to get final TLS ACK. */ + + if (out_buf == NULL) + + out_buf = wpabuf_alloc(0); + + if (inbufs[1].BufferType == SECBUFFER_EXTRA) { + + wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " + + "application data", + + inbufs[1].pvBuffer, inbufs[1].cbBuffer); + + if (appl_data) { + + *appl_data = wpabuf_alloc_copy( + + outbufs[1].pvBuffer, + + outbufs[1].cbBuffer); + + } + + global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); + + inbufs[1].pvBuffer = NULL; + + } + + break; + + case SEC_I_INCOMPLETE_CREDENTIALS: + + wpa_printf(MSG_DEBUG, + + "Schannel: SEC_I_INCOMPLETE_CREDENTIALS"); + + break; + + case SEC_E_WRONG_PRINCIPAL: + + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL"); + + break; + + case SEC_E_INTERNAL_ERROR: + + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR"); + + break; + + } + + if (FAILED(status)) { + + wpa_printf(MSG_DEBUG, "Schannel: Handshake failed " + + "(out_buf=%p)", out_buf); + + conn->failed++; + + global->sspi->DeleteSecurityContext(&conn->context); + + return out_buf; + + } + + if (inbufs[1].BufferType == SECBUFFER_EXTRA) { + + /* TODO: Can this happen? What to do with this data? */ + + wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data", + + inbufs[1].pvBuffer, inbufs[1].cbBuffer); + + global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); + + inbufs[1].pvBuffer = NULL; + + } + + return out_buf; + +} + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + + struct tls_connection *conn, + + const struct wpabuf *in_data, + + struct wpabuf **appl_data) + +{ + + return NULL; + +} + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + + struct tls_connection *conn, + + const struct wpabuf *in_data) + +{ + + struct tls_global *global = tls_ctx; + + SECURITY_STATUS status; + + SecBufferDesc buf; + + SecBuffer bufs[4]; + + SecPkgContext_StreamSizes sizes; + + int i; + + struct wpabuf *out; + + status = global->sspi->QueryContextAttributes(&conn->context, + + SECPKG_ATTR_STREAM_SIZES, + + &sizes); + + if (status != SEC_E_OK) { + + wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", + + __func__); + + return NULL; + + } + + wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", + + __func__, + + (unsigned int) sizes.cbHeader, + + (unsigned int) sizes.cbTrailer); + + out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + + + sizes.cbTrailer); + + os_memset(&bufs, 0, sizeof(bufs)); + + bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); + + bufs[0].cbBuffer = sizes.cbHeader; + + bufs[0].BufferType = SECBUFFER_STREAM_HEADER; + + bufs[1].pvBuffer = wpabuf_put(out, 0); + + wpabuf_put_buf(out, in_data); + + bufs[1].cbBuffer = wpabuf_len(in_data); + + bufs[1].BufferType = SECBUFFER_DATA; + + bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); + + bufs[2].cbBuffer = sizes.cbTrailer; + + bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; + + buf.ulVersion = SECBUFFER_VERSION; + + buf.cBuffers = 3; + + buf.pBuffers = bufs; + + status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0); + + wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> " + + "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " + + "len[2]=%d type[2]=%d", + + (int) status, + + (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, + + (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, + + (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); + + wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " + + "out_data=%p bufs %p %p %p", + + wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, + + bufs[2].pvBuffer); + + for (i = 0; i < 3; i++) { + + if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY) + + { + + wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs", + + bufs[i].pvBuffer, bufs[i].cbBuffer); + + } + + } + + if (status == SEC_E_OK) { + + wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); + + wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data " + + "from EncryptMessage", out); + + return out; + + } + + wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", + + __func__, (int) status); + + wpabuf_free(out); + + return NULL; + +} + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + + struct tls_connection *conn, + + const struct wpabuf *in_data) + +{ + + struct tls_global *global = tls_ctx; + + SECURITY_STATUS status; + + SecBufferDesc buf; + + SecBuffer bufs[4]; + + int i; + + struct wpabuf *out, *tmp; + + wpa_hexdump_buf(MSG_MSGDUMP, + + "Schannel: Encrypted data to DecryptMessage", in_data); + + os_memset(&bufs, 0, sizeof(bufs)); + + tmp = wpabuf_dup(in_data); + + if (tmp == NULL) + + return NULL; + + bufs[0].pvBuffer = wpabuf_mhead(tmp); + + bufs[0].cbBuffer = wpabuf_len(in_data); + + bufs[0].BufferType = SECBUFFER_DATA; + + bufs[1].BufferType = SECBUFFER_EMPTY; + + bufs[2].BufferType = SECBUFFER_EMPTY; + + bufs[3].BufferType = SECBUFFER_EMPTY; + + buf.ulVersion = SECBUFFER_VERSION; + + buf.cBuffers = 4; + + buf.pBuffers = bufs; + + status = global->sspi->DecryptMessage(&conn->context, &buf, 0, + + NULL); + + wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> " + + "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " + + "len[2]=%d type[2]=%d len[3]=%d type[3]=%d", + + (int) status, + + (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, + + (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, + + (int) bufs[2].cbBuffer, (int) bufs[2].BufferType, + + (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); + + wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " + + "out_data=%p bufs %p %p %p %p", + + wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, + + bufs[2].pvBuffer, bufs[3].pvBuffer); + + switch (status) { + + case SEC_E_INCOMPLETE_MESSAGE: + + wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE", + + __func__); + + break; + + case SEC_E_OK: + + wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); + + for (i = 0; i < 4; i++) { + + if (bufs[i].BufferType == SECBUFFER_DATA) + + break; + + } + + if (i == 4) { + + wpa_printf(MSG_DEBUG, "%s: No output data from " + + "DecryptMessage", __func__); + + wpabuf_free(tmp); + + return NULL; + + } + + wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " + + "DecryptMessage", + + bufs[i].pvBuffer, bufs[i].cbBuffer); + + out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer); + + wpabuf_free(tmp); + + return out; + + } + + wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", + + __func__, (int) status); + + wpabuf_free(tmp); + + return NULL; + +} + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) + +{ + + return 0; + +} + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + + u8 *ciphers) + +{ + + return -1; + +} + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + + char *buf, size_t buflen) + +{ + + return -1; + +} + +int tls_connection_enable_workaround(void *ssl_ctx, + + struct tls_connection *conn) + +{ + + return 0; + +} + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + + int ext_type, const u8 *data, + + size_t data_len) + +{ + + return -1; + +} + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) + +{ + + if (conn == NULL) + + return -1; + + return conn->failed; + +} + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) + +{ + + if (conn == NULL) + + return -1; + + return conn->read_alerts; + +} + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) + +{ + + if (conn == NULL) + + return -1; + + return conn->write_alerts; + +} + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + + const struct tls_connection_params *params) + +{ + + struct tls_global *global = tls_ctx; + + ALG_ID algs[1]; + + SECURITY_STATUS status; + + TimeStamp ts_expiry; + + if (conn == NULL) + + return -1; + + if (global->my_cert_store == NULL && + + (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == + + NULL) { + + wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", + + __func__, (unsigned int) GetLastError()); + + return -1; + + } + + os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); + + conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + + conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; + + algs[0] = CALG_RSA_KEYX; + + conn->schannel_cred.cSupportedAlgs = 1; + + conn->schannel_cred.palgSupportedAlgs = algs; + + conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; + +#ifdef UNICODE + + status = global->sspi->AcquireCredentialsHandleW( + + NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, + + &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); + +#else /* UNICODE */ + + status = global->sspi->AcquireCredentialsHandleA( + + NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, + + &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); + +#endif /* UNICODE */ + + if (status != SEC_E_OK) { + + wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " + + "0x%x", __func__, (unsigned int) status); + + return -1; + + } + + return 0; + +} + +unsigned int tls_capabilities(void *tls_ctx) + +{ + + return 0; + +} + +int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, + + int tls_ia) + +{ + + return -1; + +} + +int tls_connection_ia_send_phase_finished(void *tls_ctx, + + struct tls_connection *conn, + + int final, + + u8 *out_data, size_t out_len) + +{ + + return -1; + +} + +int tls_connection_ia_final_phase_finished(void *tls_ctx, + + struct tls_connection *conn) + +{ + + return -1; + +} + +int tls_connection_ia_permute_inner_secret(void *tls_ctx, + + struct tls_connection *conn, + + const u8 *key, size_t key_len) + +{ + + return -1; + +} + + diff --git a/plugins/trace/.trace_example.c.swo b/plugins/trace/.trace_example.c.swo new file mode 100644 index 0000000000000000000000000000000000000000..dfad1d431cbb303fb39fe114e55ab28895e7198c GIT binary patch literal 16384 zcmeI2TZ|i58OMhbprH*#MO5OdFPod~IP3MMQLAh>$ZOATta|N@J#M;`FdEMs+ry4$ zoSE@%oHQkZw*mqQ=mWeUfduVa1uuX~CE%e*h(`)YNEI(!)gV-~~6c=WHzp%4B}y3&=Tj&R%#hyRzW=UmrZ zf6+62amSQT1qO3_j=PiVX|Aamt(D3CnJE0$O#B;Be4PJ*$Uto8ol*MZ(f8M9 z;-lc?D^Va(AW>lL}0G$8F&+pIO zCrLNK5IhY2`94YdAsB$GU=jT7h$MX%c)$S}@cVlu=~>`{kAQo@KkkvF+u&93GWa?8 zHuy5Q0j>iJY=b(u1PUMzmcRqx_IoAiXW;Xo2#$ke;8i$(6J)_@a5s4UZb|wBcnQ1! zz7M_wz6!2`A^0>XgLB~H;4rxT9!dH&coBRLd0OfaEARvG4R8y520RX$KmkRN0)IXvNxuNkgRghQe!NLs2VM)UBM3f*ZH1-}j zP%QsDb7ABjtxouwM#dXpuWMQ&Od6Cp>0#*fSe+H=hUEF0Z|XFetRsANoG3N0J+osA zgDl7HjKzWV$xP!6tC3XFF+;RXTST@SN$nRT6cbJa zLfbxygs9w-s1%3oI;KGxZbkk#(7T#T{Naf(V9He&#TzS>%aG^yd^Bx#>^-#|mv&6c zqD&5!0z(-t#nyU3NnwcY^m>|YaA9G9(Hj3Ac*2d7k3=%zb@+;q+P1Wg$H}=zs6P3Q zkIL|$Czh`S{>Scerk-|9?Q8lD#OEX&&%u`F`D*M2*JX3NajBvfYny6@b~S4tyeyr` z2leT~Rj;eJzfwqAx?m+j^ZKRuA+``zjTrDk3hm;EPX05uAoepg% zn~J(FE5(a*F|wl8wv_S#dFpaQR_5}swPK~#koS#gz$>*Jf>PKwiTt=+Y%2R_ z->S=7h5B4vUEU}+6uGW8Duu`9VvDtERW70`%{gzC!dgXEOU=!#Sy$G|)k+Os?h{qK zSg3BuYO^8NXLFV0N?}_rVKzbJd}-y)Ip?;@)s1;8Yt6a9t$M8}HyY}rHLUfWVjYqM60b-AEG+PR4JvRpwI zK-sfdE;Z%)_H1CeT2}V4a;aIK%ipNTGPh$i9ZrpY?};m`JV<&&@2aH=+fbtc9=`I& zL+4~T{TafvO`pd|6cGPisBCP7%HPOmm$FMYgK?zrtAZ50BuJsPunrsiA|usJz3U4% z3<^t#5+f+|#t^!txnkO*aP7ea9ZZ*DMqZp+m|n1mV6Jftkqu`O$^?e8q&v3fM^kF1 zHE6emn~i5KOp6`Ao93Dxa0Ml$GI1$tdtmFn>DcMFQXUFqZ&MjpbW9IQsImQYsy9?y zj$>hUo{7p5?(f1yjWbU+Op3icuokxp<_(D}vSOz&5>-a$` zaA{$pP@@C#!@vDCj8=(YIqZ3zU=@pILQuIX6 zafe}&I2TqE)9y@%1$`@eWmXM4_&y0{gVc`Yv^0x@N6tm}66!{^sWxiOdNHU<JKOFwQs1Z)UF4X5qzVa(FHGq4TZeR0;Zt-~tkshGO3JwaNqYVVmS#_yUQS!T=C z+~Eq}3EGZN9!x#b_5J?&TyAe~Z?R(!7BOk$tT4ZKfsJiD1M<4gz%r<-?Fx(*U6{ML zm(wub`$KZtfnN|68IhI@Np@kAXcuq#{-O-0MGXi-CDxn3z`9XbP-oPIFtk!BBZ>xLX*xZ4^AJ{;6HR!E?t!eu`&Z=3BQJBgkH+pH7H=y|bEcW%g8CEDB_y{J2` za0N)$Y;=;bL|hi`DsxUyKKoEazj4h@7xQv4P0LI9bF|5?#+TL*<@Oyn=+RVI!<_Us zToOP3D|o(^@SM)i|Hq>TzgO_g{}b>n@FX}3PJu(D^Va(AWFu08 z_AvZ*Dm;|)u!|P{pR$G1i+J}xc+ij1 ztkM7uJhdm&{Ts{7GLpbH}U0d%Dqk6<=Nm+_E>(c!! zch9NP#Bn1BTj_N2B&CA$ pwREP>@yq8X^quJz6Neh(UNMuVB;#D-z}V4_|A8r@qk{9&e*gi0#5Di_ literal 0 HcmV?d00001 diff --git a/plugins/trace/CMakeLists.txt b/plugins/trace/CMakeLists.txt new file mode 100644 index 00000000..124e05e6 --- /dev/null +++ b/plugins/trace/CMakeLists.txt @@ -0,0 +1,16 @@ +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +# Trace example plugin +SET(TRACE_EXAMPLE_SOURCES trace_example.c) +IF(WIN32) + SET(TRACE_EXAMPLE_SOURCES ${TRACE_EXAMPLE_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) +ENDIF() +ADD_LIBRARY(trace_example SHARED ${TRACE_EXAMPLE_SOURCES}) +SET_TARGET_PROPERTIES(trace_example PROPERTIES PREFIX "") + +INSTALL(TARGETS + trace_example + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") + diff --git a/plugins/trace/trace_example.c b/plugins/trace/trace_example.c new file mode 100644 index 00000000..1ce5d1b8 --- /dev/null +++ b/plugins/trace/trace_example.c @@ -0,0 +1,440 @@ +/************************************************************************************ + Copyright (C) 2015 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA +*************************************************************************************/ +#ifndef _WIN32 +#define _GNU_SOURCE 1 +#endif + +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#define READ 0 +#define WRITE 1 + +/* function prototypes */ +static int trace_init(char *errormsg, + size_t errormsg_size, + int unused __attribute__((unused)), + va_list unused1 __attribute__((unused))); +static int trace_deinit(void); + +int (*register_callback)(my_bool register_callback, + void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length)); +void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length); + +mysql_declare_client_plugin(TRACE) + "trace_example", + "Georg Richter", + "Trace example plugin", + {1,0,0}, + "LGPL", + &trace_init, + &trace_deinit, +mysql_end_client_plugin; + +static char *commands[]= { + "MYSQL_COM_SLEEP", + "MYSQL_COM_QUIT", + "MYSQL_COM_INIT_DB", + "MYSQL_COM_QUERY", + "MYSQL_COM_FIELD_LIST", + "MYSQL_COM_CREATE_DB", + "MYSQL_COM_DROP_DB", + "MYSQL_COM_REFRESH", + "MYSQL_COM_SHUTDOWN", + "MYSQL_COM_STATISTICS", + "MYSQL_COM_PROCESS_INFO", + "MYSQL_COM_CONNECT", + "MYSQL_COM_PROCESS_KILL", + "MYSQL_COM_DEBUG", + "MYSQL_COM_PING", + "MYSQL_COM_TIME", + "MYSQL_COM_DELAYED_INSERT", + "MYSQL_COM_CHANGE_USER", + "MYSQL_COM_BINLOG_DUMP", + "MYSQL_COM_TABLE_DUMP", + "MYSQL_COM_CONNECT_OUT", + "MYSQL_COM_REGISTER_SLAVE", + "MYSQL_COM_STMT_PREPARE", + "MYSQL_COM_STMT_EXECUTE", + "MYSQL_COM_STMT_SEND_LONG_DATA", + "MYSQL_COM_STMT_CLOSE", + "MYSQL_COM_STMT_RESET", + "MYSQL_COM_SET_OPTION", + "MYSQL_COM_STMT_FETCH", + "MYSQL_COM_DAEMON", + "MYSQL_COM_END" +}; + +typedef struct { + unsigned long thread_id; + int last_command; /* MYSQL_COM_* values, -1 for handshake */ + unsigned int max_packet_size; + size_t total_size[2]; + unsigned int client_flags; + char *username; + char *db; + char *command; + char *filename; + unsigned long refid; /* stmt_id, thread_id for kill */ + uchar charset; + void *next; + int local_infile; +} TRACE_INFO; + +#define TRACE_STATUS(a) (!a) ? "ok" : "error" + +TRACE_INFO *trace_info= NULL; + +static TRACE_INFO *get_trace_info(unsigned long thread_id) +{ + TRACE_INFO *info= trace_info; + + while (info) + { + if (info->thread_id == thread_id) + return info; + else + info= info->next; + } + + if (!(info= (TRACE_INFO *)calloc(sizeof(TRACE_INFO), 1))) + return NULL; + info->thread_id= thread_id; + info->next= trace_info; + trace_info= info; + return info; +} + +static void delete_trace_info(unsigned long thread_id) +{ + TRACE_INFO *last= NULL, *current; + current= trace_info; + + while (current) + { + if (current->thread_id == thread_id) + { + printf("deleting thread %d\n", thread_id); + + if (last) + last->next= current->next; + else + trace_info= current->next; + if (current->command) + free(current->command); + if (current->db) + free(current->db); + if (current->username) + free(current->username); + if (current->filename) + free(current->filename); + free(current); + } + last= current; + current= current->next; + } + +} + + +/* {{{ static int trace_init */ +/* + Initialization routine + + SYNOPSIS + trace_init + unused1 + unused2 + unused3 + unused4 + + DESCRIPTION + Init function registers a callback handler for CIO interface. + + RETURN + 0 success +*/ +static int trace_init(char *errormsg, + size_t errormsg_size, + int unused1 __attribute__((unused)), + va_list unused2 __attribute__((unused))) +{ + void *func; + +#ifdef WIN32 + if (!(func= GetProcAddress(GetModuleHandle(NULL), "ma_cio_register_callback"))) +#else + if (!(func= dlsym(RTLD_DEFAULT, "ma_cio_register_callback"))) +#endif + { + strncpy(errormsg, "Can't find ma_cio_register_callback function", errormsg_size); + return 1; + } + register_callback= func; + register_callback(TRUE, trace_callback); + + return 0; +} +/* }}} */ + +static int trace_deinit() +{ + /* unregister plugin */ + while(trace_info) + { + printf("Warning: Connection for thread %d not properly closed\n", trace_info->thread_id); + trace_info= trace_info->next; + } + register_callback(FALSE, trace_callback); +} + +static void trace_set_command(TRACE_INFO *info, char *buffer, size_t size) +{ + if (info->command) + free(info->command); + + info->command= (char *)malloc(size); + strncpy(info->command, buffer, size); +} + +void dump_buffer(uchar *buffer, size_t len) +{ + char *p= buffer; + while (p < buffer + len) + { + printf("%02x ", *p); + p++; + } + printf("\n"); +} + +static void dump_simple(TRACE_INFO *info, my_bool is_error) +{ + printf("%8d: %s %s\n", info->thread_id, commands[info->last_command], TRACE_STATUS(is_error)); +} + +static void dump_reference(TRACE_INFO *info, my_bool is_error) +{ + printf("%8d: %s(%d) %s\n", info->thread_id, commands[info->last_command], info->refid, TRACE_STATUS(is_error)); +} + +static void dump_command(TRACE_INFO *info, my_bool is_error) +{ + int i; + printf("%8d: %s(", info->thread_id, commands[info->last_command]); + for (i= 0; info->command && i < strlen(info->command); i++) + if (info->command[i] == '\n') + printf("\\n"); + else if (info->command[i] == '\r') + printf("\\r"); + else if (info->command[i] == '\t') + printf("\\t"); + else + printf("%c", info->command[i]); + printf(") %s\n", TRACE_STATUS(is_error)); +} + +void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) +{ + unsigned long thread_id= mysql->thread_id; + TRACE_INFO *info; + + + /* check if package is server greeting package, + * and set thread_id */ + if (!thread_id && mode == READ) + { + char *p= buffer; + p+= 4; /* packet length */ + if (*p != 0xFF) /* protocol version 0xFF indicates error */ + { + p+= strlen(p + 1) + 2; + thread_id= uint4korr(p); + } + info= get_trace_info(thread_id); + info->last_command= -1; + } + else + { + char *p= buffer; + info= get_trace_info(thread_id); + + if (info->last_command == -1) + { + if (mode == WRITE) + { + /* client authentication reply packet: + * + * ofs description length + * ------------------------ + * 0 length 3 + * 3 packet_no 1 + * 4 client capab. 4 + * 8 max_packet_size 4 + * 12 character set 1 + * 13 reserved 23 + * ------------------------ + * 36 username (zero terminated) + * len (1 byte) + password or + */ + int len; + + p+= 4; + info->client_flags= uint4korr(p); + p+= 4; + info->max_packet_size= uint4korr(p); + p+= 4; + info->charset= *p; + p+= 24; + info->username= strdup(p); + p+= strlen(p) + 1; + if (*p) /* we are not interested in authentication data */ + p+= *p; + p++; + if (info->client_flags & CLIENT_CONNECT_WITH_DB) + info->db= strdup(p); + } + else + { + p++; + if (*p == 0xFF) + printf("%8d: CONNECT_ERROR(%d)\n", info->thread_id, uint4korr(p+1)); + else + printf("%8d: CONNECT_SUCCESS(host=%s,user=%s,db=%s)\n", info->thread_id, + mysql->host, info->username, info->db ? info->db : "'none'"); + info->last_command= MYSQL_COM_SLEEP; + } + } + else { + char *p= buffer; + int len; + + if (mode == WRITE) + { + len= uint3korr(p); + p+= 4; + info->last_command= *p; + p++; + switch (info->last_command) { + case MYSQL_COM_INIT_DB: + case MYSQL_COM_DROP_DB: + case MYSQL_COM_CREATE_DB: + case MYSQL_COM_DEBUG: + case MYSQL_COM_QUERY: + case MYSQL_COM_STMT_PREPARE: + trace_set_command(info, p, len - 1); + break; + case MYSQL_COM_PROCESS_KILL: + info->refid= uint4korr(p); + break; + case MYSQL_COM_QUIT: + printf("%8d: MYSQL_COM_QUIT\n", info->thread_id); + delete_trace_info(info->thread_id); + break; + case MYSQL_COM_PING: + printf("%8d: MYSQL_COM_PING\n", info->thread_id); + break; + case MYSQL_COM_STMT_EXECUTE: + case MYSQL_COM_STMT_RESET: + case MYSQL_COM_STMT_CLOSE: + info->refid= uint4korr(p); + break; + case MYSQL_COM_CHANGE_USER: + break; + default: + if (info->local_infile == 1) + { + printf("%8d: SEND_LOCAL_INFILE(%s) ", info->thread_id, info->filename); + if (len) + printf("sent %d bytes\n", len); + else + printf("- error\n"); + info->local_infile= 2; + } + else + printf("%8d: UNKNOWN_COMMAND: %d\n", info->thread_id, info->last_command); + break; + } + } + else + { + my_bool is_error; + + len= uint3korr(p); + p+= 4; + + is_error= ((unsigned int)len == -1); + + switch(info->last_command) { + case MYSQL_COM_STMT_EXECUTE: + case MYSQL_COM_STMT_RESET: + case MYSQL_COM_STMT_CLOSE: + case MYSQL_COM_PROCESS_KILL: + dump_reference(info, is_error); + info->refid= 0; + info->last_command= 0; + break; + case MYSQL_COM_QUIT: + dump_simple(info, is_error); + break; + case MYSQL_COM_QUERY: + case MYSQL_COM_INIT_DB: + case MYSQL_COM_DROP_DB: + case MYSQL_COM_CREATE_DB: + case MYSQL_COM_DEBUG: + case MYSQL_COM_CHANGE_USER: + if (info->last_command == MYSQL_COM_QUERY && (uchar)*p == 251) + { + info->local_infile= 1; + p++; + info->filename= (char *)malloc(len); + strncpy(info->filename, (char *)p, len); + dump_command(info, is_error); + break; + } + dump_command(info, is_error); + if (info->local_infile != 1) + { + free(info->command); + info->command= NULL; + } + break; + case MYSQL_COM_STMT_PREPARE: + printf("%8d: MYSQL_COM_STMT_PREPARE(%s) ", info->thread_id, info->command); + if (!*p) + { + unsigned long stmt_id= uint4korr(p+1); + printf("-> stmt_id(%d)\n", stmt_id); + } + else + printf("error\n"); + break; + } + } + } + } + info->total_size[mode]+= length; +} diff --git a/plugins/trace/trace_example.so b/plugins/trace/trace_example.so new file mode 100755 index 0000000000000000000000000000000000000000..5cb7b2f765db5d5af04e8c06191df0a537e773fa GIT binary patch literal 39895 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 Date: Thu, 6 Aug 2015 15:10:34 +0200 Subject: [PATCH 03/18] Added more files --- include/ma_cio.h | 116 +++++++++++++++++++++++++++++++++++++++++ include/ma_ssl.h | 47 +++++++++++++++++ libmariadb/ma_ssl.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 include/ma_cio.h create mode 100644 include/ma_ssl.h create mode 100644 libmariadb/ma_ssl.c diff --git a/include/ma_cio.h b/include/ma_cio.h new file mode 100644 index 00000000..f0d2c0e6 --- /dev/null +++ b/include/ma_cio.h @@ -0,0 +1,116 @@ +#ifndef _ma_cio_h_ +#define _ma_cio_h_ + +#ifdef HAVE_SSL +#include +#endif + +#define CIO_SET_ERROR if (cio->set_error) \ + cio->set_error + +#define CIO_READ_AHEAD_CACHE_SIZE 16384 +#define CIO_READ_AHEAD_CACHE_MIN_SIZE 2048 +#define CIO_EINTR_TRIES 2 + +struct st_ma_cio_methods; +typedef struct st_ma_cio_methods CIO_METHODS; + +struct st_ma_cio_ssl; +typedef struct st_ma_cio_ssl MARIADB_SSL; + +enum enum_cio_timeout { + CIO_CONNECT_TIMEOUT= 0, + CIO_READ_TIMEOUT, + CIO_WRITE_TIMEOUT +}; + +enum enum_cio_io_event +{ + VIO_IO_EVENT_READ, + VIO_IO_EVENT_WRITE, + VIO_IO_EVENT_CONNECT +}; + +enum enum_cio_type { + CIO_TYPE_UNIXSOCKET= 0, + CIO_TYPE_SOCKET, + CIO_TYPE_NAMEDPIPE +}; + +enum enum_cio_operation { + CIO_READ= 0, + CIO_WRITE=1 +}; + +struct st_cio_callback; + +typedef struct st_cio_callback { + void (*callback)(MYSQL *mysql, uchar *buffer, size_t size); + struct st_cio_callback *next; +} CIO_CALLBACK; + +typedef struct st_ma_cio { + void *data; + /* read ahead cache */ + uchar *cache; + uchar *cache_pos; + size_t cache_size; + enum enum_cio_type type; + int timeout[3]; + int ssl_type; /* todo: change to enum (ssl plugins) */ + MARIADB_SSL *cssl; + MYSQL *mysql; + struct mysql_async_context *async_context; /* For non-blocking API */ + CIO_METHODS *methods; + FILE *fp; + void (*set_error)(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, const char *format, ...); + void (*callback)(MARIADB_CIO *cio, my_bool is_read, const char *buffer, size_t length); +} MARIADB_CIO; + +typedef struct st_ma_cio_cinfo +{ + const char *host; + const char *unix_socket; + int port; + enum enum_cio_type type; + MYSQL *mysql; +} MA_CIO_CINFO; + +struct st_ma_cio_methods +{ + my_bool (*set_timeout)(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); + int (*get_timeout)(MARIADB_CIO *cio, enum enum_cio_timeout type); + size_t (*read)(MARIADB_CIO *cio, const uchar *buffer, size_t length); + size_t (*async_read)(MARIADB_CIO *cio, const uchar *buffer, size_t length); + size_t (*write)(MARIADB_CIO *cio, const uchar *buffer, size_t length); + size_t (*async_write)(MARIADB_CIO *cio, const uchar *buffer, size_t length); + int (*wait_io_or_timeout)(MARIADB_CIO *cio, my_bool is_read, int timeout); + my_bool (*blocking)(MARIADB_CIO *cio, my_bool value, my_bool *old_value); + my_bool (*connect)(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo); + my_bool (*close)(MARIADB_CIO *cio); + int (*fast_send)(MARIADB_CIO *cio); + int (*keepalive)(MARIADB_CIO *cio); + my_bool (*get_handle)(MARIADB_CIO *cio, void *handle); + my_bool (*is_blocking)(MARIADB_CIO *cio); + my_bool (*is_alive)(MARIADB_CIO *cio); +}; + +/* Function prototypes */ +MARIADB_CIO *ma_cio_init(MA_CIO_CINFO *cinfo); +void ma_cio_close(MARIADB_CIO *cio); +size_t ma_cio_cache_read(MARIADB_CIO *cio, uchar *buffer, size_t length); +size_t ma_cio_read(MARIADB_CIO *cio, const uchar *buffer, size_t length); +size_t ma_cio_write(MARIADB_CIO *cio, const uchar *buffer, size_t length); +int ma_cio_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type); +my_bool ma_cio_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); +int ma_cio_fast_send(MARIADB_CIO *cio); +int ma_cio_keepalive(MARIADB_CIO *cio); +my_socket ma_cio_get_socket(MARIADB_CIO *cio); +my_bool ma_cio_is_blocking(MARIADB_CIO *cio); +my_bool ma_cio_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode); +my_bool ma_cio_is_blocking(MARIADB_CIO *cio); +int ma_cio_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout); +my_bool ma_cio_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo); +my_bool ma_cio_is_alive(MARIADB_CIO *cio); + +#endif /* _ma_cio_h_ */ diff --git a/include/ma_ssl.h b/include/ma_ssl.h new file mode 100644 index 00000000..33f5458c --- /dev/null +++ b/include/ma_ssl.h @@ -0,0 +1,47 @@ +#ifndef _ma_ssl_h_ +#define _ma_ssl_h_ + +struct st_ma_cio_ssl_methods; +typedef struct st_ma_cio_ssl_methods CIO_SSL_METHODS; +extern int ssl_default_plugin; + +enum enum_cio_ssl_type { + SSL_TYPE_DEFAULT=0, +#ifdef _WIN32 + SSL_TYPE_SCHANNEL, +#endif + SSL_TYPE_OPENSSL, + SSL_TYPE_GNUTLS +}; + +typedef struct st_ma_cio_ssl { + void *data; + enum enum_cio_ssl_type type; + MARIADB_CIO *cio; + CIO_SSL_METHODS *methods; + void *ssl; +} MARIADB_SSL; + +struct st_ma_cio_ssl_methods +{ + void *(*init)(MARIADB_SSL *cssl, MYSQL *mysql); + my_bool (*connect)(MARIADB_SSL *cssl); + size_t (*read)(MARIADB_SSL *cssl, const uchar* buffer, size_t length); + size_t (*write)(MARIADB_SSL *cssl, const uchar* buffer, size_t length); + my_bool (*close)(MARIADB_SSL *cssl); + int (*verify_server_cert)(MARIADB_SSL *ssl); + const char *(*cipher)(MARIADB_SSL *ssl); + my_bool (*check_fp)(MARIADB_SSL *cssl, const char *fp); +}; + +/* Function prototypes */ +MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql); +my_bool ma_cio_ssl_connect(MARIADB_SSL *cssl); +size_t ma_cio_ssl_read(MARIADB_SSL *cssl, const uchar *buffer, size_t length); +size_t ma_cio_ssl_write(MARIADB_SSL *cssl, const uchar *buffer, size_t length); +my_bool ma_cio_ssl_close(MARIADB_SSL *cssl); +int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl); +const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl); +my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, size_t length); + +#endif /* _ma_ssl_h_ */ diff --git a/libmariadb/ma_ssl.c b/libmariadb/ma_ssl.c new file mode 100644 index 00000000..9372a630 --- /dev/null +++ b/libmariadb/ma_ssl.c @@ -0,0 +1,122 @@ +/************************************************************************************ + 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + *************************************************************************************/ + +/* + * this is the abstraction layer for communication via SSL. + * The following SSL libraries/variants are currently supported: + * - openssl + * - gnutls + * - schannel (windows only) + * + * Different SSL variants are implemented as plugins + * On Windows schannel is implemented as (standard) + * built-in plugin. + */ + +#ifdef HAVE_SSL + +#include +#include +#include +#include +#include +#include +#include +#include + +/* +#include +#include +*/ + +/* Errors should be handled via cio callback function */ + +MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql) +{ + MARIADB_CIO_PLUGIN *cio_plugin; + MARIADB_SSL *cssl= NULL; + + if (!(cssl= (MARIADB_SSL *)my_malloc(sizeof(MARIADB_CIO), + MYF(MY_WME | MY_ZEROFILL)))) + { + return NULL; + } + + /* register error routine and methods */ + cssl->methods= cio_plugin->ssl_methods; + cssl->cio= mysql->net.cio; + + if (!(cssl->ssl= cssl->methods->init(cssl, mysql))) + { + my_free((gptr)cssl); + cssl= NULL; + } + return cssl; +} + +my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, size_t length) +{ + if (cssl && cssl->methods->check_fp) + return cssl->methods->check_fp(cssl, fp); + return 0; +} + +my_bool ma_cio_ssl_connect(MARIADB_SSL *cssl) +{ + if (cssl && cssl->methods->connect) + return cssl->methods->connect(cssl); + return 1; +} + +size_t ma_cio_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ + if (cssl && cssl->methods->read) + return cssl->methods->read(cssl, buffer, length); + return -1; +} + +size_t ma_cio_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ + if (cssl && cssl->methods->write) + return cssl->methods->write(cssl, buffer, length); + return -1; +} + +my_bool ma_cio_ssl_close(MARIADB_SSL *cssl) +{ + if (cssl && cssl->methods->close) + return cssl->methods->close(cssl); + return 1; +} + +int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl) +{ + if (cssl && cssl->methods->verify_server_cert) + return cssl->methods->verify_server_cert(cssl); + return 0; +} + +const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl) +{ + if (!cssl && !cssl->methods->cipher) + return NULL; + return cssl->methods->cipher(cssl); +} +#endif /* HAVE_SSL */ From b36f2f627ce46f37b9ce632646d4d8bfccafe97f Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 6 Aug 2015 16:11:06 +0200 Subject: [PATCH 04/18] fixed redundant declarations for MARIADB_CIO and MARIADB_SSL --- include/ma_cio.h | 8 ++++++-- include/ma_ssl.h | 10 ++++++++-- include/mysql_com.h | 10 ++-------- plugins/trace/trace_example.so | Bin 39895 -> 39879 bytes 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/include/ma_cio.h b/include/ma_cio.h index f0d2c0e6..6000e823 100644 --- a/include/ma_cio.h +++ b/include/ma_cio.h @@ -1,5 +1,6 @@ #ifndef _ma_cio_h_ #define _ma_cio_h_ +#define cio_defined #ifdef HAVE_SSL #include @@ -15,8 +16,11 @@ struct st_ma_cio_methods; typedef struct st_ma_cio_methods CIO_METHODS; +#ifndef ssl_defined +#define ssl_defined struct st_ma_cio_ssl; typedef struct st_ma_cio_ssl MARIADB_SSL; +#endif enum enum_cio_timeout { CIO_CONNECT_TIMEOUT= 0, @@ -49,7 +53,7 @@ typedef struct st_cio_callback { struct st_cio_callback *next; } CIO_CALLBACK; -typedef struct st_ma_cio { +struct st_ma_cio { void *data; /* read ahead cache */ uchar *cache; @@ -65,7 +69,7 @@ typedef struct st_ma_cio { FILE *fp; void (*set_error)(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, const char *format, ...); void (*callback)(MARIADB_CIO *cio, my_bool is_read, const char *buffer, size_t length); -} MARIADB_CIO; +}; typedef struct st_ma_cio_cinfo { diff --git a/include/ma_ssl.h b/include/ma_ssl.h index 33f5458c..b18c2f26 100644 --- a/include/ma_ssl.h +++ b/include/ma_ssl.h @@ -5,6 +5,12 @@ struct st_ma_cio_ssl_methods; typedef struct st_ma_cio_ssl_methods CIO_SSL_METHODS; extern int ssl_default_plugin; +#ifndef ssl_defined +#define ssl_defined +struct st_ma_cio_ssl; +typedef struct st_ma_cio_ssl MARIADB_SSL; +#endif + enum enum_cio_ssl_type { SSL_TYPE_DEFAULT=0, #ifdef _WIN32 @@ -14,13 +20,13 @@ enum enum_cio_ssl_type { SSL_TYPE_GNUTLS }; -typedef struct st_ma_cio_ssl { +struct st_ma_cio_ssl { void *data; enum enum_cio_ssl_type type; MARIADB_CIO *cio; CIO_SSL_METHODS *methods; void *ssl; -} MARIADB_SSL; +}; struct st_ma_cio_ssl_methods { diff --git a/include/mysql_com.h b/include/mysql_com.h index b843dfe1..2974cc1b 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -209,17 +209,11 @@ enum enum_server_command #define NET_WRITE_TIMEOUT 60 /* Timeout on write */ #define NET_WAIT_TIMEOUT 8*60*60 /* Wait for new query */ -#ifndef Vio_defined -#define Vio_defined -#ifdef HAVE_VIO -class Vio; /* Fill Vio class in C++ */ -#else -struct st_vio; /* Only C */ -typedef struct st_vio Vio; +#ifndef cio_defined +#define cio_defined struct st_ma_cio; typedef struct st_ma_cio MARIADB_CIO; #endif -#endif #define MAX_CHAR_WIDTH 255 /* Max length for a CHAR colum */ #define MAX_BLOB_WIDTH 8192 /* Default width for blob */ diff --git a/plugins/trace/trace_example.so b/plugins/trace/trace_example.so index 5cb7b2f765db5d5af04e8c06191df0a537e773fa..2aef86d0c1b31314771fa23fe554997ce749f2b5 100755 GIT binary patch delta 5960 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 delta 5963 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 aw`I!&qs@C2tTxYjxYFq8oc{2Ak^cj-$&s}H From 0f7a9413736b64f62e54c18e179a259f3fa7c913 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 10 Aug 2015 12:07:24 +0200 Subject: [PATCH 05/18] Fixed crash when loading external plugin --- include/ma_ssl.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/include/ma_ssl.h b/include/ma_ssl.h index b18c2f26..33f5458c 100644 --- a/include/ma_ssl.h +++ b/include/ma_ssl.h @@ -5,12 +5,6 @@ struct st_ma_cio_ssl_methods; typedef struct st_ma_cio_ssl_methods CIO_SSL_METHODS; extern int ssl_default_plugin; -#ifndef ssl_defined -#define ssl_defined -struct st_ma_cio_ssl; -typedef struct st_ma_cio_ssl MARIADB_SSL; -#endif - enum enum_cio_ssl_type { SSL_TYPE_DEFAULT=0, #ifdef _WIN32 @@ -20,13 +14,13 @@ enum enum_cio_ssl_type { SSL_TYPE_GNUTLS }; -struct st_ma_cio_ssl { +typedef struct st_ma_cio_ssl { void *data; enum enum_cio_ssl_type type; MARIADB_CIO *cio; CIO_SSL_METHODS *methods; void *ssl; -}; +} MARIADB_SSL; struct st_ma_cio_ssl_methods { From f2955a4b39e481ddf35b47e3e92048218e1af436 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 10 Aug 2015 12:09:14 +0200 Subject: [PATCH 06/18] more fixes --- CMakeLists.txt | 37 ++++++++++++----------------- include/mysql/client_plugin.h | 2 +- libmariadb/CMakeLists.txt | 10 ++++---- libmariadb/client_plugin.c | 18 ++++++-------- libmariadb/dbug.c | 2 +- libmariadb/libmariadb.c | 23 ++---------------- libmariadb/ma_ssl.c | 2 +- plugins/trace/trace_example.c | 1 + plugins/trace/trace_example.so | Bin 39879 -> 37355 bytes unittest/libmariadb/CMakeLists.txt | 2 +- unittest/libmariadb/basic-t.c | 1 + 11 files changed, 34 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3499d1dc..232ecc09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ # CMakeLists.txt + # This is the LGPL libmariadb project. PROJECT(mariadb-connector-c C) @@ -76,7 +77,7 @@ ENDIF() SET(PROTOCOL_VERSION 10) # we adapted new password option from PHP's mysqlnd ! SET(MYSQL_CLIENT_VERSION_MAJOR "5") SET(MYSQL_CLIENT_VERSION_MINOR "5") -SET(MYSQL_CLIENT_VERSION_PATCH "1") +SET(MYSQL_CLIENT_VERSION_PATCH "2") SET(MYSQL_CLIENT_VERSION "${MYSQL_CLIENT_VERSION_MAJOR}.${MYSQL_CLIENT_VERSION_MINOR}.${MYSQL_CLIENT_VERSION_PATCH}") MATH(EXPR MYSQL_VERSION_ID "${MYSQL_CLIENT_VERSION_MAJOR} * 10000 + ${MYSQL_CLIENT_VERSION_MINOR} * 100 + @@ -88,13 +89,10 @@ IF(NOT MYSQL_UNIX_ADDR) SET(MYSQL_UNIX_ADDR "/tmp/mysql.sock") ENDIF() -FIND_PACKAGE(CURL) -IF(CURL_FOUND) - ADD_DEFINITIONS(-DHAVE_CURL=1) -ENDIF() - INCLUDE("${CMAKE_SOURCE_DIR}/cmake/install.cmake") +# todo: we don't character sets in share - all is compiled in +SET(SHAREDIR "share") SET(DEFAULT_CHARSET_HOME "${CMAKE_INSTALL_PREFIX}") SET(PLUGINDIR "${CMAKE_INSTALL_PREFIX}/lib/plugin") @@ -109,29 +107,22 @@ ENDIF() IF(UNIX) - SEARCH_LIBRARY(DEFAULT_LIB inet_ntoa "c") - # IF(NOT DEFAULT_LIB) - SEARCH_LIBRARY(LIBNSL inet_ntoa "nsl_r;nsl") - # ENDIF() - SEARCH_LIBRARY(DEFAULT_LIB bind "c") - IF(NOT DEFAULT_LIB) - SEARCH_LIBRARY(LIBBIND bind "bind;socket") - ENDIF() - SEARCH_LIBRARY(DEFAULT_LIB setsockopt "c") - IF(NOT DEFAULT_LIB) - SEARCH_LIBRARY(LIBSOCKET setsockopt "socket") - ENDIF() + SEARCH_LIBRARY(LIBNSL inet_ntoa "nsl_r;nsl") + SEARCH_LIBRARY(LIBBIND bind "bind;socket") + SEARCH_LIBRARY(LIBSOCKET setsockopt "socket") SEARCH_LIBRARY(LIBDL dlopen "dl") SEARCH_LIBRARY(LIBM floor m) + # SEARCH_LIBRARY(LIBICONV iconv iconv) SEARCH_LIBRARY(LIBPTHREAD pthread_getspecific "pthread;pthreads") - SET(EXTRA_LIBS "${LIBNSL}" "${LIBBIND}" "${LIBSOCKET}" "${LIBDL}" "${LIBM}" "${LIBPTHREAD}") + SET(EXTRA_LIBS "${LIBNSL}" "${LIBBIND}" "${LIBSOCKET}") FIND_PACKAGE(Threads) + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${LIBNSL} ${LIBBIND} ${LIBICONV} + ${LIBSOCKET} ${LIBDL} ${LIBM} ${LIBPTHREAD}) #remove possible dups from required libraries - LIST(LENGTH SYS_LIBS rllength) + LIST(LENGTH CMAKE_REQUIRED_LIBRARIES rllength) IF(${rllength} GREATER 0) - LIST(REMOVE_DUPLICATES SYS_LIBS) + LIST(REMOVE_DUPLICATES CMAKE_REQUIRED_LIBRARIES) ENDIF() - SET(CMAKE_REQUIRED_LIBRARIES ${SYS_LIBS}) ENDIF() @@ -189,6 +180,8 @@ IF(WIN32) ENDIF() ENDIF() + + IF(WITH_SQLITE) ADD_DEFINITIONS(-DHAVE_SQLITE) ENDIF() diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index bcda3852..0f4059a1 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -77,7 +77,7 @@ struct st_mysql_client_plugin struct st_mysql; /********* database api plugin specific declarations **********/ -typedef struct st_mariadb_client_plugin_DB +struct st_mariadb_client_plugin_DB { MYSQL_CLIENT_PLUGIN_HEADER /* functions */ diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index be2d3899..7be55991 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -8,7 +8,6 @@ ADD_DEFINITIONS(-D LIBMARIADB) ADD_DEFINITIONS(-D THREAD) SET(EXPORT_SYMBOLS - client_errors load_defaults ma_cio_register_callback mariadb_connection @@ -249,8 +248,6 @@ bmove.c bmove_upp.c my_charset.c hash.c -ma_cio.c -ma_ssl.c net.c charset.c ma_time.c @@ -266,8 +263,9 @@ is_prefix.c libmariadb.c list.c llstr.c -longlong2str.c -ma_io.c +longlong2str.c +ma_cio.c +ma_ssl.c mf_dirname.c mf_fn_ext.c mf_format.c @@ -324,7 +322,7 @@ my_stmt.c my_loaddata.c my_stmt_codec.c client_plugin.c -# built in plugins +ma_io.c ${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c ${CMAKE_SOURCE_DIR}/plugins/builtin/cio_socket.c ) diff --git a/libmariadb/client_plugin.c b/libmariadb/client_plugin.c index ee6b3410..5186bf2b 100644 --- a/libmariadb/client_plugin.c +++ b/libmariadb/client_plugin.c @@ -132,14 +132,12 @@ static struct st_mysql_client_plugin *find_plugin(const char *name, int type) if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) return 0; + if (!name) + return plugin_list[type]->plugin; + for (p= plugin_list[type]; p; p= p->next) { - if (name) - { - if (strcmp(p->plugin->name, name) == 0) - return p->plugin; - } - else if (p->plugin->type == type) + if (strcmp(p->plugin->name, name) == 0) return p->plugin; } return NULL; @@ -274,7 +272,7 @@ int mysql_client_plugin_init() MYSQL mysql; struct st_mysql_client_plugin **builtin; va_list unused; - LINT_INIT_STRUCT(unused); + LINT_INIT_STRUCT(unused); if (initialized) return 0; @@ -410,7 +408,7 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, } - if (!(sym= (int *)dlsym(dlhandle, plugin_declarations_sym))) + if (!(sym= dlsym(dlhandle, plugin_declarations_sym))) { errmsg= "not a plugin"; (void)dlclose(dlhandle); @@ -463,7 +461,6 @@ mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...) return p; } - /* see for a full description */ struct st_mysql_client_plugin * STDCALL mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) @@ -483,8 +480,7 @@ mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) return p; /* not found, load it */ - if (name) - return mysql_load_plugin(mysql, name, type, 0); + return mysql_load_plugin(mysql, name, type, 0); } diff --git a/libmariadb/dbug.c b/libmariadb/dbug.c index fb7c52ba..5e848c44 100644 --- a/libmariadb/dbug.c +++ b/libmariadb/dbug.c @@ -1811,7 +1811,7 @@ static int DoTrace(CODE_STATE *cs) case INCLUDE: return DO_TRACE; case MATCHED|SUBDIR: case NOT_MATCHED|SUBDIR: - case MATCHED: return framep_trace_flag(cs, cs->framep) ? + case MATCHED: return (framep_trace_flag(cs, cs->framep)) ? DO_TRACE : DONT_TRACE; case EXCLUDE: case NOT_MATCHED: return DONT_TRACE; diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index b32990c8..671172f9 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -544,21 +544,6 @@ mysql_debug(const char *debug __attribute__((unused))) } -/************************************************************************** -** Close the server connection if we get a SIGPIPE - ARGSUSED -**************************************************************************/ - -static sig_handler -pipe_sig_handler(int sig __attribute__((unused))) -{ - DBUG_PRINT("info",("Hit by signal %d",sig)); -#ifdef DONT_REMEMBER_SIGNAL - (void) signal(SIGPIPE,pipe_sig_handler); -#endif -} - - /************************************************************************** ** Shut down connection **************************************************************************/ @@ -1142,10 +1127,6 @@ mysql_init(MYSQL *mysql) mysql->charset= default_charset_info; strmov(mysql->net.sqlstate, "00000"); mysql->net.last_error[0]= mysql->net.last_errno= 0; -#if defined(SIGPIPE) && defined(THREAD) && !defined(_WIN32) - if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE)) - (void) signal(SIGPIPE,pipe_sig_handler); -#endif /* Only enable LOAD DATA INFILE by default if configured with @@ -1685,13 +1666,13 @@ static my_bool mysql_reconnect(MYSQL *mysql) tmp_mysql.options.my_cnf_group= tmp_mysql.options.my_cnf_file= NULL; /* make sure that we reconnect with the same character set */ - /* if (!tmp_mysql.options.charset_name || + if (!tmp_mysql.options.charset_name || strcmp(tmp_mysql.options.charset_name, mysql->charset->csname)) { my_free(tmp_mysql.options.charset_name); tmp_mysql.options.charset_name= my_strdup(mysql->charset->csname, MYF(MY_WME)); } -*/ + tmp_mysql.reconnect= mysql->reconnect; if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, mysql->db, mysql->port, mysql->unix_socket, diff --git a/libmariadb/ma_ssl.c b/libmariadb/ma_ssl.c index 9372a630..7b2e4378 100644 --- a/libmariadb/ma_ssl.c +++ b/libmariadb/ma_ssl.c @@ -35,7 +35,7 @@ #include #include #include -#include +//#include #include #include #include diff --git a/plugins/trace/trace_example.c b/plugins/trace/trace_example.c index 1ce5d1b8..25854e48 100644 --- a/plugins/trace/trace_example.c +++ b/plugins/trace/trace_example.c @@ -52,6 +52,7 @@ mysql_declare_client_plugin(TRACE) "LGPL", &trace_init, &trace_deinit, + NULL mysql_end_client_plugin; static char *commands[]= { diff --git a/plugins/trace/trace_example.so b/plugins/trace/trace_example.so index 2aef86d0c1b31314771fa23fe554997ce749f2b5..5694ab08e411c6d5c0c8fcb024fe6af6e583d747 100755 GIT binary patch literal 37355 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

3E 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.client_flag | CLIENT_LOCAL_FILES), "client_flags not correct"); FAIL_IF(thread_id == mysql_thread_id(mysql), "new thread id expected"); + diag("cs: %s", mysql->charset->csname); FAIL_IF(strcmp(mysql->charset->csname, "utf8"), "wrong character set"); } mysql_close(mysql); From 23895fbd4ff662b3389197d97dbf48a7dc761048 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 13 Aug 2015 14:01:33 +0200 Subject: [PATCH 07/18] Fixed gnutls support --- CMakeLists.txt | 16 +- include/ma_ssl.h | 126 +++++++++++++--- libmariadb/CMakeLists.txt | 5 +- libmariadb/client_plugin.c | 6 - libmariadb/libmariadb.c | 2 +- libmariadb/ma_cio.c | 14 +- libmariadb/ma_ssl.c | 108 ++++++++++---- libmariadb/net.c | 6 +- .../secure/gnutls.c | 123 ++++++++-------- .../secure/openssl.c | 139 ++++++++++-------- .../secure/schannel.c | 0 plugins/builtin/my_auth.c | 16 ++ plugins/cio/cio_npipe.c | 1 + plugins/cio/cio_shmem.c | 1 + plugins/trace/trace_example.so | Bin 37355 -> 36859 bytes unittest/libmariadb/CMakeLists.txt | 2 +- unittest/libmariadb/ssl.c.in | 21 ++- 17 files changed, 390 insertions(+), 196 deletions(-) rename plugins/builtin/cio_gnutls.c => libmariadb/secure/gnutls.c (79%) rename plugins/builtin/cio_openssl.c => libmariadb/secure/openssl.c (81%) rename plugins/builtin/cio_schannel.c => libmariadb/secure/schannel.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 232ecc09..9ede4184 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,8 +152,7 @@ IF(WITH_SSL STREQUAL "OPENSSL") FIND_PACKAGE(OpenSSL) IF(OPENSSL_FOUND) ADD_DEFINITIONS(-DHAVE_OPENSSL -DHAVE_SSL) - ADD_DEFINITIONS(-DSSL_PLUGIN=cio_openssl_plugin) - SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_openssl.c") + SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/libmariadb/secure/openssl.c") SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES}) ELSE() MESSAGE(FATAL "OpenSSL not found") @@ -162,24 +161,23 @@ ENDIF() IF(WITH_SSL STREQUAL "GNUTLS") FIND_PACKAGE(GnuTLS) IF(GNUTLS_FOUND) - ADD_DEFINITIONS(-DSSL_PLUGIN=cio_gnutls_plugin) ADD_DEFINITIONS(-DHAVE_GNUTLS -DHAVE_SSL) - SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_gnutls.c") - SET(SSL_LIBRARIES ${GNUTLS_LIBRARIES}) + SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/libmariadb/secure/gnutls.c") + SET(SSL_LIBRARIES ${GNUTLS_LIBRARY}) ELSE() MESSAGE(FATAL "GnuTLS not found") ENDIF() ENDIF() IF(WIN32) IF(WITH_SSL STREQUAL "SCHANNEL") - ADD_DEFINITIONS(-DSSL_PLUGIN=cio_schannel_plugin) MESSAGE(STATUS "SSL_TYPE ${SSL_TYPE}") ADD_DEFINITIONS(-DHAVE_SCHANNEL -DHAVE_SSL) - SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_schannel.c" "${CMAKE_SOURCE_DIR}/plugins/builtin/ma_schannel.c") + SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/libmariadb/secure/schannel.c" "${CMAKE_SOURCE_DIR}/libmariadb/secure/ma_schannel.c") INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/plugins/cio/") ENDIF() ENDIF() +MARK_AS_ADVANCED(SSL_SOURCES) IF(WITH_SQLITE) @@ -206,7 +204,7 @@ IF(WIN32) ELSE() SET(SYSTEM_LIBS ${LIBPTHREAD} ${LIBDL} ${LIBM} ${LIBICONV}) ENDIF() -IF(OPENSSL_FOUND) +IF(WITH_SSL) SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${SSL_LIBRARIES}) ENDIF() @@ -291,7 +289,7 @@ MESSAGE(STATUS "CPack generation: ${CPACK_GENERATOR}") IF(CLIENT_DOCS) MESSAGE(STATUS "Documentation included from ${CLIENT_DOCS}") ENDIF() -MESSAGE(STATUS "SSL support: ${WITH_SSL} Sources: ${SSL_SOURCES}") +MESSAGE(STATUS "SSL support: ${WITH_SSL} Libs: ${SSL_LIBRARIES}") MESSAGE(STATUS "Experimental Sqlite support: ${WITH_SQLITE}") IF(WITH_EXTERNAL_ZLIB) MESSAGE(STATUS "Zlib support: ${WITH_EXTERNAL_ZLIB}") diff --git a/include/ma_ssl.h b/include/ma_ssl.h index 33f5458c..cdd33162 100644 --- a/include/ma_ssl.h +++ b/include/ma_ssl.h @@ -1,10 +1,6 @@ #ifndef _ma_ssl_h_ #define _ma_ssl_h_ -struct st_ma_cio_ssl_methods; -typedef struct st_ma_cio_ssl_methods CIO_SSL_METHODS; -extern int ssl_default_plugin; - enum enum_cio_ssl_type { SSL_TYPE_DEFAULT=0, #ifdef _WIN32 @@ -16,23 +12,117 @@ enum enum_cio_ssl_type { typedef struct st_ma_cio_ssl { void *data; - enum enum_cio_ssl_type type; MARIADB_CIO *cio; - CIO_SSL_METHODS *methods; void *ssl; } MARIADB_SSL; -struct st_ma_cio_ssl_methods -{ - void *(*init)(MARIADB_SSL *cssl, MYSQL *mysql); - my_bool (*connect)(MARIADB_SSL *cssl); - size_t (*read)(MARIADB_SSL *cssl, const uchar* buffer, size_t length); - size_t (*write)(MARIADB_SSL *cssl, const uchar* buffer, size_t length); - my_bool (*close)(MARIADB_SSL *cssl); - int (*verify_server_cert)(MARIADB_SSL *ssl); - const char *(*cipher)(MARIADB_SSL *ssl); - my_bool (*check_fp)(MARIADB_SSL *cssl, const char *fp); -}; +/* Function prototypes */ + +/* ma_ssl_start + initializes the ssl library + Parameter: + errmsg pointer to error message buffer + errmsg_len length of error message buffer + Returns: + 0 success + 1 if an error occured + Notes: + On success the global variable ma_ssl_initialized will be set to 1 +*/ +int ma_ssl_start(char *errmsg, size_t errmsg_len); + +/* ma_ssl_end + unloads/deinitializes ssl library and unsets global variable + ma_ssl_initialized +*/ +void ma_ssl_end(void); + +/* ma_ssl_init + creates a new SSL structure for a SSL connection and loads + client certificates + + Parameters: + MYSQL a mysql structure + Returns: + void * a pointer to internal SSL structure +*/ +void * ma_ssl_init(MYSQL *mysql); + +/* ma_ssl_connect + performs SSL handshake + Parameters: + MARIADB_SSL MariaDB SSL container + Returns: + 0 success + 1 error +*/ +my_bool ma_ssl_connect(MARIADB_SSL *cssl); + +/* ma_ssl_read + reads up to length bytes from socket + Parameters: + cssl MariaDB SSL container + buffer read buffer + length buffer length + Returns: + 0-n bytes read + -1 if an error occured +*/ +size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length); + +/* ma_ssl_write + write buffer to socket + Parameters: + cssl MariaDB SSL container + buffer write buffer + length buffer length + Returns: + 0-n bytes written + -1 if an error occured +*/ +size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length); + +/* ma_ssl_close + closes SSL connection and frees SSL structure which was previously + created by ma_ssl_init call + Parameters: + MARIADB_SSL MariaDB SSL container + Returns: + 0 success + 1 error +*/ +my_bool ma_ssl_close(MARIADB_SSL *cssl); + +/* ma_ssl_verify_server_cert + validation check of server certificate + Parameter: + MARIADB_SSL MariaDB SSL container + Returns: + ß success + 1 error +*/ +int ma_ssl_verify_server_cert(MARIADB_SSL *cssl); + +/* ma_ssl_get_cipher + returns cipher for current ssl connection + Parameter: + MARIADB_SSL MariaDB SSL container + Returns: + cipher in use or + NULL on error +*/ +const char *ma_ssl_get_cipher(MARIADB_SSL *ssl); + +/* ma_ssl_get_finger_print + returns SHA1 finger print of server certificate + Parameter: + MARIADB_SSL MariaDB SSL container + fp buffer for fingerprint + fp_len buffer length + Returns: + actual size of finger print +*/ +unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int fp_len); /* Function prototypes */ MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql); @@ -42,6 +132,6 @@ size_t ma_cio_ssl_write(MARIADB_SSL *cssl, const uchar *buffer, size_t length); my_bool ma_cio_ssl_close(MARIADB_SSL *cssl); int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl); const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl); -my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, size_t length); +my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_list); #endif /* _ma_ssl_h_ */ diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 7be55991..a340f163 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -325,11 +325,10 @@ client_plugin.c ma_io.c ${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c ${CMAKE_SOURCE_DIR}/plugins/builtin/cio_socket.c +${SSL_SOURCES} ) -IF(SSL_SOURCES) - SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ${SSL_SOURCES}) -ENDIF() +MESSAGE(STATUS "${LIBMARIADB_SOURCES}") IF(WIN32) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/win-iconv) diff --git a/libmariadb/client_plugin.c b/libmariadb/client_plugin.c index 5186bf2b..c0d594bb 100644 --- a/libmariadb/client_plugin.c +++ b/libmariadb/client_plugin.c @@ -84,18 +84,12 @@ extern struct st_mysql_client_plugin old_password_client_plugin; extern struct st_mysql_client_plugin native_password_client_plugin; extern MARIADB_CIO_PLUGIN cio_socket_plugin; -#ifdef HAVE_SSL -extern MARIADB_CIO_PLUGIN SSL_PLUGIN; -#endif struct st_mysql_client_plugin *mysql_client_builtins[]= { (struct st_mysql_client_plugin *)&old_password_client_plugin, (struct st_mysql_client_plugin *)&native_password_client_plugin, (struct st_mysql_client_plugin *)&cio_socket_plugin, -#ifdef HAVE_SSL - (struct st_mysql_client_plugin *)&SSL_PLUGIN, -#endif 0 }; diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index 671172f9..4710f644 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -62,10 +62,10 @@ #define INADDR_NONE -1 #endif #include -#include #ifndef _WIN32 #include #endif +#include #include #define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) diff --git a/libmariadb/ma_cio.c b/libmariadb/ma_cio.c index f94a6f26..0b234a48 100644 --- a/libmariadb/ma_cio.c +++ b/libmariadb/ma_cio.c @@ -116,7 +116,7 @@ MARIADB_CIO *ma_cio_init(MA_CIO_CINFO *cinfo) cio->methods->set_timeout(cio, CIO_WRITE_TIMEOUT, cinfo->mysql->options.write_timeout); } - if (!(cio->cache= my_malloc(CIO_READ_AHEAD_CACHE_SIZE, MYF(MY_WME)))) + if (!(cio->cache= my_malloc(CIO_READ_AHEAD_CACHE_SIZE, MYF(MY_ZEROFILL)))) { CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); return NULL; @@ -405,7 +405,9 @@ my_bool ma_cio_start_ssl(MARIADB_CIO *cio) return 1; CLEAR_CLIENT_ERROR(cio->mysql); if (!(cio->cssl= ma_cio_ssl_init(cio->mysql))) + { return 1; + } if (ma_cio_ssl_connect(cio->cssl)) { my_free((gptr)cio->cssl); @@ -417,6 +419,16 @@ my_bool ma_cio_start_ssl(MARIADB_CIO *cio) ma_cio_ssl_verify_server_cert(cio->cssl)) return 1; + if (cio->mysql->options.extension && + (cio->mysql->options.extension->ssl_fp || cio->mysql->options.extension->ssl_fp_list)) + { + + if (ma_cio_ssl_check_fp(cio->cssl, + cio->mysql->options.extension->ssl_fp, + cio->mysql->options.extension->ssl_fp_list)) + return 1; + } + return 0; } /* }}} */ diff --git a/libmariadb/ma_ssl.c b/libmariadb/ma_ssl.c index 7b2e4378..272df926 100644 --- a/libmariadb/ma_ssl.c +++ b/libmariadb/ma_ssl.c @@ -35,6 +35,7 @@ #include #include #include +#include //#include #include #include @@ -47,12 +48,15 @@ */ /* Errors should be handled via cio callback function */ +my_bool ma_ssl_initialized= FALSE; MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql) { - MARIADB_CIO_PLUGIN *cio_plugin; MARIADB_SSL *cssl= NULL; + if (!ma_ssl_initialized) + ma_ssl_start(mysql->net.last_error, MYSQL_ERRMSG_SIZE); + if (!(cssl= (MARIADB_SSL *)my_malloc(sizeof(MARIADB_CIO), MYF(MY_WME | MY_ZEROFILL)))) { @@ -60,63 +64,107 @@ MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql) } /* register error routine and methods */ - cssl->methods= cio_plugin->ssl_methods; cssl->cio= mysql->net.cio; - - if (!(cssl->ssl= cssl->methods->init(cssl, mysql))) + if (!(cssl->ssl= ma_ssl_init(mysql))) { - my_free((gptr)cssl); + my_free(cssl); cssl= NULL; } return cssl; } -my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, size_t length) -{ - if (cssl && cssl->methods->check_fp) - return cssl->methods->check_fp(cssl, fp); - return 0; -} - my_bool ma_cio_ssl_connect(MARIADB_SSL *cssl) { - if (cssl && cssl->methods->connect) - return cssl->methods->connect(cssl); - return 1; + return ma_ssl_connect(cssl); } size_t ma_cio_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) { - if (cssl && cssl->methods->read) - return cssl->methods->read(cssl, buffer, length); - return -1; + return ma_ssl_read(cssl, buffer, length); } size_t ma_cio_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) { - if (cssl && cssl->methods->write) - return cssl->methods->write(cssl, buffer, length); - return -1; + return ma_ssl_write(cssl, buffer, length); } my_bool ma_cio_ssl_close(MARIADB_SSL *cssl) { - if (cssl && cssl->methods->close) - return cssl->methods->close(cssl); - return 1; + return ma_ssl_close(cssl); } int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl) { - if (cssl && cssl->methods->verify_server_cert) - return cssl->methods->verify_server_cert(cssl); - return 0; + return ma_ssl_verify_server_cert(cssl); } const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl) { - if (!cssl && !cssl->methods->cipher) - return NULL; - return cssl->methods->cipher(cssl); + return ma_ssl_get_cipher(cssl); +} + +static my_bool ma_cio_ssl_compare_fp(char *fp1, unsigned int fp1_len, + char *fp2, unsigned int fp2_len) +{ + char hexstr[fp1_len * 2 + 1]; + + fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len); + if (strncasecmp(hexstr, fp2, fp1_len) != 0) + return 1; + return 0; +} + +my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_list) +{ + unsigned int cert_fp_len= 64; + unsigned char cert_fp[64]; + MYSQL *mysql; + my_bool rc=1; + + if (ma_ssl_get_finger_print(cssl, cert_fp, cert_fp_len) < 1) + goto end; + + if (fp) + rc= ma_cio_ssl_compare_fp(cert_fp, cert_fp_len, fp, strlen(fp)); + else if (fp_list) + { + FILE *fp; + char buff[255]; + + if (!(fp = fopen(fp_list, "r"))) + { +/* + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Can't open finger print list"); + */ + goto end; + } + + while (fgets(buff, sizeof(buff)-1, fp)) + { + /* remove trailing new line character */ + char *pos= strchr(buff, '\r'); + if (!pos) + pos= strchr(buff, '\n'); + if (pos) + *pos= '\0'; + + if (!ma_cio_ssl_compare_fp(cert_fp, cert_fp_len, buff, strlen(buff))) + { + /* finger print is valid: close file and exit */ + fclose(fp); + rc= 0; + goto end; + } + } + + /* No finger print matched - close file and return error */ + fclose(fp); + } + + +end: + return rc; } #endif /* HAVE_SSL */ diff --git a/libmariadb/net.c b/libmariadb/net.c index 26949a71..892b9dfe 100644 --- a/libmariadb/net.c +++ b/libmariadb/net.c @@ -164,7 +164,11 @@ static my_bool net_realloc(NET *net, size_t length) DBUG_RETURN(1); } pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); - if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length + 1, MYF(MY_WME)))) + /* reallocate buffer: + size= pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE */ + if (!(buff=(uchar*) my_realloc((char*) net->buff, + pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE, + MYF(MY_WME)))) { DBUG_PRINT("info", ("Out of memory")); net->error=1; diff --git a/plugins/builtin/cio_gnutls.c b/libmariadb/secure/gnutls.c similarity index 79% rename from plugins/builtin/cio_gnutls.c rename to libmariadb/secure/gnutls.c index 657ed62f..bb22230b 100644 --- a/plugins/builtin/cio_gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -29,53 +29,18 @@ #include #include #include +#include pthread_mutex_t LOCK_gnutls_config; -static my_bool my_gnutls_initialized= FALSE; static gnutls_certificate_credentials_t GNUTLS_xcred; +extern my_bool ma_ssl_initialized; + +static int my_verify_callback(gnutls_session_t ssl); #define MAX_SSL_ERR_LEN 100 -int cio_gnutls_start(char *errmsg, size_t errmsg_len, int count, va_list); -int cio_gnutls_end(); -void *cio_gnutls_init(MARIADB_SSL *cssl, MYSQL *mysql); -my_bool cio_gnutls_connect(MARIADB_SSL *cssl); -size_t cio_gnutls_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length); -size_t cio_gnutls_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length); -my_bool cio_gnutls_close(MARIADB_SSL *cssl); -int cio_gnutls_verify_server_cert(MARIADB_SSL *cssl); -const char *cio_gnutls_cipher(MARIADB_SSL *cssl); -static int my_verify_callback(gnutls_session_t ssl); - -struct st_ma_cio_ssl_methods cio_gnutls_methods= { - cio_gnutls_init, - cio_gnutls_connect, - cio_gnutls_read, - cio_gnutls_write, - cio_gnutls_close, - cio_gnutls_verify_server_cert, - cio_gnutls_cipher -}; - -MARIADB_CIO_PLUGIN cio_gnutls_plugin= -{ - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, - "cio_gnutls", - "Georg Richter", - "MariaDB communication IO plugin for GnuTLS SSL communication", - {1, 0, 0}, - "LGPL", - NULL, - cio_gnutls_start, - cio_gnutls_end, - NULL, - &cio_gnutls_methods, - NULL -}; - -static void cio_gnutls_set_error(MYSQL *mysql, int ssl_errno) +static void ma_ssl_set_error(MYSQL *mysql, int ssl_errno) { char ssl_error[MAX_SSL_ERR_LEN]; const char *ssl_error_reason; @@ -98,7 +63,7 @@ static void cio_gnutls_set_error(MYSQL *mysql, int ssl_errno) } -static void cio_gnutls_get_error(char *errmsg, size_t length, int ssl_errno) +static void ma_ssl_get_error(char *errmsg, size_t length, int ssl_errno) { const char *ssl_error_reason; @@ -127,21 +92,21 @@ static void cio_gnutls_get_error(char *errmsg, size_t length, int ssl_errno) 0 success 1 error */ -int cio_gnutls_start(char *errmsg, size_t errmsg_len, int count, va_list list) +int ma_ssl_start(char *errmsg, size_t errmsg_len) { int rc= 0; pthread_mutex_init(&LOCK_gnutls_config,MY_MUTEX_INIT_FAST); pthread_mutex_lock(&LOCK_gnutls_config); - if (!my_gnutls_initialized) + if (!ma_ssl_initialized) { if ((rc= gnutls_global_init()) != GNUTLS_E_SUCCESS) { - cio_gnutls_get_error(errmsg, errmsg_len, rc); + ma_ssl_get_error(errmsg, errmsg_len, rc); goto end; } - my_gnutls_initialized= TRUE; + ma_ssl_initialized= TRUE; } /* Allocate a global context for credentials */ rc= gnutls_certificate_allocate_credentials(&GNUTLS_xcred); @@ -162,10 +127,10 @@ end: RETURN VALUES void */ -int cio_gnutls_end() +void ma_ssl_end() { pthread_mutex_lock(&LOCK_gnutls_config); - if (my_gnutls_initialized) + if (ma_ssl_initialized) { gnutls_certificate_free_keys(GNUTLS_xcred); gnutls_certificate_free_cas(GNUTLS_xcred); @@ -173,14 +138,14 @@ int cio_gnutls_end() gnutls_certificate_free_ca_names(GNUTLS_xcred); gnutls_certificate_free_credentials(GNUTLS_xcred); gnutls_global_deinit(); - my_gnutls_initialized= FALSE; + ma_ssl_initialized= FALSE; } pthread_mutex_unlock(&LOCK_gnutls_config); pthread_mutex_destroy(&LOCK_gnutls_config); - return 0; + return; } -static int cio_gnutls_set_certs(MYSQL *mysql, MARIADB_SSL *cssl) +static int ma_ssl_set_certs(MYSQL *mysql) { char *certfile= mysql->options.ssl_cert, *keyfile= mysql->options.ssl_key; @@ -218,11 +183,11 @@ static int cio_gnutls_set_certs(MYSQL *mysql, MARIADB_SSL *cssl) error: if (cipher) - my_free(cipher)); + my_free(cipher); return ssl_error; } -void *cio_gnutls_init(MARIADB_SSL *cssl, MYSQL *mysql) +void *ma_ssl_init(MYSQL *mysql) { gnutls_session_t ssl= NULL; int ssl_error= 0; @@ -230,7 +195,7 @@ void *cio_gnutls_init(MARIADB_SSL *cssl, MYSQL *mysql) pthread_mutex_lock(&LOCK_gnutls_config); - if ((ssl_error= cio_gnutls_set_certs(mysql, cssl)) < 0) + if ((ssl_error= ma_ssl_set_certs(mysql)) < 0) goto error; if ((ssl_error = gnutls_init(&ssl, GNUTLS_CLIENT & GNUTLS_NONBLOCK)) < 0) @@ -244,18 +209,17 @@ void *cio_gnutls_init(MARIADB_SSL *cssl, MYSQL *mysql) if ((ssl_error= gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, GNUTLS_xcred)) < 0) goto error; - cssl->ssl= ssl; pthread_mutex_unlock(&LOCK_gnutls_config); return (void *)ssl; error: - cio_gnutls_set_error(mysql, ssl_error); + ma_ssl_set_error(mysql, ssl_error); if (ssl) gnutls_deinit(ssl); pthread_mutex_unlock(&LOCK_gnutls_config); return NULL; } -my_bool cio_gnutls_connect(MARIADB_SSL *cssl) +my_bool ma_ssl_connect(MARIADB_SSL *cssl) { gnutls_session_t ssl = (gnutls_session_t)cssl->ssl; my_bool blocking; @@ -273,7 +237,7 @@ my_bool cio_gnutls_connect(MARIADB_SSL *cssl) if (!(blocking= cio->methods->is_blocking(cio))) cio->methods->blocking(cio, TRUE, 0); - gnutls_transport_set_int(ssl, cio->methods->get_socket(cio)); + gnutls_transport_set_int(ssl, mysql_get_socket(mysql)); gnutls_handshake_set_timeout(ssl, mysql->options.connect_timeout); do { @@ -282,7 +246,7 @@ my_bool cio_gnutls_connect(MARIADB_SSL *cssl) if (ret < 0) { - cio_gnutls_set_error(mysql, ret); + ma_ssl_set_error(mysql, ret); /* restore blocking mode */ if (!blocking) cio->methods->blocking(cio, FALSE, 0); @@ -293,17 +257,17 @@ my_bool cio_gnutls_connect(MARIADB_SSL *cssl) return 0; } -size_t cio_gnutls_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) { return gnutls_record_recv((gnutls_session_t )cssl->ssl, (void *)buffer, length); } -size_t cio_gnutls_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) { return gnutls_record_send((gnutls_session_t )cssl->ssl, (void *)buffer, length); } -my_bool cio_gnutls_close(MARIADB_SSL *cssl) +my_bool ma_ssl_close(MARIADB_SSL *cssl) { gnutls_bye((gnutls_session_t )cssl->ssl, GNUTLS_SHUT_WR); gnutls_deinit((gnutls_session_t )cssl->ssl); @@ -312,13 +276,13 @@ my_bool cio_gnutls_close(MARIADB_SSL *cssl) return 0; } -int cio_gnutls_verify_server_cert(MARIADB_SSL *cssl) +int ma_ssl_verify_server_cert(MARIADB_SSL *cssl) { /* server verification is already handled before */ return 0; } -const char *cio_gnutls_cipher(MARIADB_SSL *cssl) +const char *ma_ssl_get_cipher(MARIADB_SSL *cssl) { if (!cssl || !cssl->ssl) return NULL; @@ -390,7 +354,6 @@ static int my_verify_callback(gnutls_session_t ssl) if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && gnutls_x509_crt_check_hostname (cert, hostname) < 0) { - printf("Error: %s does not match\n", hostname); gnutls_x509_crt_deinit (cert); cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Hostname in certificate doesn't match"); return GNUTLS_E_CERTIFICATE_ERROR; @@ -402,4 +365,36 @@ static int my_verify_callback(gnutls_session_t ssl) return 0; } +unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int len) +{ + MYSQL *mysql; + size_t fp_len= len; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + + if (!cssl || !cssl->ssl) + return 0; + + mysql= (MYSQL *)gnutls_session_get_ptr(cssl->ssl); + + cert_list = gnutls_certificate_get_peers (cssl->ssl, &cert_list_size); + if (cert_list == NULL) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Unable to get server certificate"); + return 0; + } + + if (gnutls_fingerprint(GNUTLS_DIG_MD5, &cert_list[0], fp, &fp_len) > 0) + return fp_len; + else + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Finger print buffer too small"); + return 0; + } +} + #endif /* HAVE_GNUTLS */ diff --git a/plugins/builtin/cio_openssl.c b/libmariadb/secure/openssl.c similarity index 81% rename from plugins/builtin/cio_openssl.c rename to libmariadb/secure/openssl.c index f6e836f3..18a140c6 100644 --- a/plugins/builtin/cio_openssl.c +++ b/libmariadb/secure/openssl.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include /* SSL and SSL_CTX */ @@ -32,14 +33,14 @@ #include #define my_malloc(A,B) malloc((A)) #undef my_free -#define my_free(A,B) free((A)) +#define my_free(A) free((A)) #define my_snprintf snprintf #define my_vsnprintf vsnprintf #undef SAFE_MUTEX #endif #include -static my_bool my_openssl_initialized= FALSE; +extern my_bool ma_ssl_initialized; static SSL_CTX *SSL_context= NULL; #define MAX_SSL_ERR_LEN 100 @@ -47,43 +48,8 @@ static SSL_CTX *SSL_context= NULL; static pthread_mutex_t LOCK_openssl_config; static pthread_mutex_t *LOCK_crypto= NULL; -int cio_openssl_start(char *errmsg, size_t errmsg_len, int count, va_list); -int cio_openssl_end(); -void *cio_openssl_init(MARIADB_SSL *cssl, MYSQL *mysql); -my_bool cio_openssl_connect(MARIADB_SSL *cssl); -size_t cio_openssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length); -size_t cio_openssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length); -my_bool cio_openssl_close(MARIADB_SSL *cssl); -int cio_openssl_verify_server_cert(MARIADB_SSL *cssl); -const char *cio_openssl_cipher(MARIADB_SSL *cssl); -struct st_ma_cio_ssl_methods cio_openssl_methods= { - cio_openssl_init, - cio_openssl_connect, - cio_openssl_read, - cio_openssl_write, - cio_openssl_close, - cio_openssl_verify_server_cert, - cio_openssl_cipher -}; - -MARIADB_CIO_PLUGIN cio_openssl_plugin= -{ - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, - "cio_openssl", - "Georg Richter", - "MariaDB communication IO plugin for OpenSSL communication", - {1, 0, 0}, - "LGPL", - cio_openssl_start, - cio_openssl_end, - NULL, - &cio_openssl_methods, - NULL -}; - -static void cio_openssl_set_error(MYSQL *mysql) +static void ma_ssl_set_error(MYSQL *mysql) { ulong ssl_errno= ERR_get_error(); char ssl_error[MAX_SSL_ERR_LEN]; @@ -107,7 +73,7 @@ static void cio_openssl_set_error(MYSQL *mysql) } -static void cio_openssl_get_error(char *errmsg, size_t length) +static void ma_ssl_get_error(char *errmsg, size_t length) { ulong ssl_errno= ERR_get_error(); const char *ssl_error_reason; @@ -189,13 +155,13 @@ static int ssl_thread_init() 0 success 1 error */ -int cio_openssl_start(char *errmsg, size_t errmsg_len, int count, va_list list) +int ma_ssl_start(char *errmsg, size_t errmsg_len) { int rc= 1; /* lock mutex to prevent multiple initialization */ pthread_mutex_init(&LOCK_openssl_config,MY_MUTEX_INIT_FAST); pthread_mutex_lock(&LOCK_openssl_config); - if (!my_openssl_initialized) + if (!ma_ssl_initialized) { if (ssl_thread_init()) { @@ -214,11 +180,11 @@ int cio_openssl_start(char *errmsg, size_t errmsg_len, int count, va_list list) if (!(SSL_context= SSL_CTX_new(TLSv1_client_method()))) { - cio_openssl_get_error(errmsg, errmsg_len); + ma_ssl_get_error(errmsg, errmsg_len); goto end; } rc= 0; - my_openssl_initialized= TRUE; + ma_ssl_initialized= TRUE; } end: pthread_mutex_unlock(&LOCK_openssl_config); @@ -237,10 +203,10 @@ end: RETURN VALUES void */ -int cio_openssl_end() +void ma_ssl_end() { pthread_mutex_lock(&LOCK_openssl_config); - if (my_openssl_initialized) + if (ma_ssl_initialized) { int i; CRYPTO_set_locking_callback(NULL); @@ -249,7 +215,7 @@ int cio_openssl_end() for (i=0; i < CRYPTO_num_locks(); i++) pthread_mutex_destroy(&LOCK_crypto[i]); - my_free((gptr)LOCK_crypto, MYF(0)); + my_free((gptr)LOCK_crypto); LOCK_crypto= NULL; if (SSL_context) @@ -265,14 +231,14 @@ int cio_openssl_end() CONF_modules_free(); CONF_modules_unload(1); sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); - my_openssl_initialized= FALSE; + ma_ssl_initialized= FALSE; } pthread_mutex_unlock(&LOCK_openssl_config); pthread_mutex_destroy(&LOCK_openssl_config); - return 0; + return; } -static int cio_openssl_set_certs(MYSQL *mysql) +static int ma_ssl_set_certs(MYSQL *mysql) { char *certfile= mysql->options.ssl_cert, *keyfile= mysql->options.ssl_key; @@ -331,7 +297,7 @@ static int cio_openssl_set_certs(MYSQL *mysql) return 0; error: - cio_openssl_set_error(mysql); + ma_ssl_set_error(mysql); return 1; } @@ -364,14 +330,17 @@ static int my_verify_callback(int ok, X509_STORE_CTX *ctx) return ok; } -void *cio_openssl_init(MARIADB_SSL *cssl, MYSQL *mysql) +void *ma_ssl_init(MYSQL *mysql) { int verify; SSL *ssl= NULL; pthread_mutex_lock(&LOCK_openssl_config); - if (cio_openssl_set_certs(mysql)) + + if (ma_ssl_set_certs(mysql)) + { goto error; + } if (!(ssl= SSL_new(SSL_context))) goto error; @@ -394,12 +363,13 @@ error: return NULL; } -my_bool cio_openssl_connect(MARIADB_SSL *cssl) +my_bool ma_ssl_connect(MARIADB_SSL *cssl) { SSL *ssl = (SSL *)cssl->ssl; my_bool blocking; MYSQL *mysql; MARIADB_CIO *cio; + int rc; mysql= (MYSQL *)SSL_get_app_data(ssl); cio= mysql->net.cio; @@ -415,28 +385,40 @@ my_bool cio_openssl_connect(MARIADB_SSL *cssl) if (SSL_connect(ssl) != 1) { - cio_openssl_set_error(mysql); + ma_ssl_set_error(mysql); /* restore blocking mode */ if (!blocking) cio->methods->blocking(cio, FALSE, 0); return 1; } + rc= SSL_get_verify_result(ssl); + if (rc != X509_V_OK) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc)); + /* restore blocking mode */ + if (!blocking) + cio->methods->blocking(cio, FALSE, 0); + + return 1; + } + cio->cssl->ssl= cssl->ssl= (void *)ssl; return 0; } -size_t cio_openssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) { return SSL_read((SSL *)cssl->ssl, (void *)buffer, (int)length); } -size_t cio_openssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) { return SSL_write((SSL *)cssl->ssl, (void *)buffer, (int)length); } -my_bool cio_openssl_close(MARIADB_SSL *cssl) +my_bool ma_ssl_close(MARIADB_SSL *cssl) { int i, rc; SSL *ssl; @@ -457,7 +439,7 @@ my_bool cio_openssl_close(MARIADB_SSL *cssl) return rc; } -int cio_openssl_verify_server_cert(MARIADB_SSL *cssl) +int ma_ssl_verify_server_cert(MARIADB_SSL *cssl) { X509 *cert; MYSQL *mysql; @@ -504,9 +486,48 @@ int cio_openssl_verify_server_cert(MARIADB_SSL *cssl) return 1; } -const char *cio_openssl_cipher(MARIADB_SSL *cssl) +const char *ma_ssl_get_cipher(MARIADB_SSL *cssl) { if (!cssl || !cssl->ssl) return NULL; return SSL_get_cipher_name(cssl->ssl); } + +unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int len) +{ + EVP_MD *digest= (EVP_MD *)EVP_sha1(); + X509 *cert; + MYSQL *mysql; + unsigned int *fp_len; + + if (!cssl || !cssl->ssl) + return NULL; + + mysql= SSL_get_app_data(cssl->ssl); + + if (!(cert= SSL_get_peer_certificate(cssl->ssl))) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Unable to get server certificate"); + return 0; + } + + 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"); + return 0; + } + *fp_len= len; + if (!X509_digest(cert, digest, fp, fp_len)) + { + my_free(fp); + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "invalid finger print of server certificate"); + return 0; + } + return (*fp_len); +} diff --git a/plugins/builtin/cio_schannel.c b/libmariadb/secure/schannel.c similarity index 100% rename from plugins/builtin/cio_schannel.c rename to libmariadb/secure/schannel.c diff --git a/plugins/builtin/my_auth.c b/plugins/builtin/my_auth.c index 2c8d3510..9b9792ef 100644 --- a/plugins/builtin/my_auth.c +++ b/plugins/builtin/my_auth.c @@ -243,6 +243,22 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (mpvio->db) mysql->client_flag|= CLIENT_CONNECT_WITH_DB; + /* if server doesn't support SSL and verification of server certificate + was set to mandatory, we need to return an error */ + if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL)) + { + if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) || + (mysql->options.extension && (mysql->options.extension->ssl_fp || + mysql->options.extension->ssl_fp_list))) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Server doesn't support SSL"); + goto error; + } + } + + /* Remove options that server doesn't support */ mysql->client_flag= mysql->client_flag & (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41) diff --git a/plugins/cio/cio_npipe.c b/plugins/cio/cio_npipe.c index 7fa688e5..e9ad6439 100644 --- a/plugins/cio/cio_npipe.c +++ b/plugins/cio/cio_npipe.c @@ -53,6 +53,7 @@ MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = "Georg Richter", "MariaDB communication IO plugin for named pipe communication", {1, 0, 0}, + "LGPL", NULL, NULL, &cio_npipe_methods, diff --git a/plugins/cio/cio_shmem.c b/plugins/cio/cio_shmem.c index 70deedbf..ca282587 100644 --- a/plugins/cio/cio_shmem.c +++ b/plugins/cio/cio_shmem.c @@ -59,6 +59,7 @@ MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = "Georg Richter", "MariaDB communication IO plugin for named pipe communication", {1, 0, 0}, + "LGPL", NULL, NULL, &cio_shm_methods, diff --git a/plugins/trace/trace_example.so b/plugins/trace/trace_example.so index 5694ab08e411c6d5c0c8fcb024fe6af6e583d747..e92d7a54026704c8debef2a6bda56caf9e13e266 100755 GIT binary patch delta 7699 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 Sc&tfGz+>axU3aWrl=>h0{3^Tv diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index 73061eca..a7990e70 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -25,7 +25,7 @@ SET(API_TESTS "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view "sp" "result" "connection" "misc" "ps_new" "sqlite3" "thread" "dyncol") # Get finger print from server certificate -IF(WITH_OPENSSL) +IF(WITH_SSL) #create certificates IF(EXISTS "${CMAKE_SOURCE_DIR}/unittest/libmariadb/certs/server-cert.pem") diff --git a/unittest/libmariadb/ssl.c.in b/unittest/libmariadb/ssl.c.in index 0ba3dd25..4866796c 100644 --- a/unittest/libmariadb/ssl.c.in +++ b/unittest/libmariadb/ssl.c.in @@ -33,8 +33,8 @@ pthread_mutex_t LOCK_test; int check_skip_ssl() { -#ifndef HAVE_OPENSSL - diag("client library built without OpenSSL support -> skip"); +#ifndef HAVE_SSL + diag("client library built without SSL support -> skip"); return 1; #endif if (skip_ssl) @@ -92,7 +92,11 @@ static int test_ssl_cipher(MYSQL *unused) port, socketname, 0), mysql_error(my)); cipher= (char *)mysql_get_ssl_cipher(my); +#ifdef HAVE_OPENSSL FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA"); +#elif defined(HAVE_HNUTLS) + FAIL_IF(strcmp(cipher, "AES-256-CBC") != 0, "Cipher != AES-256-CBC"); +#endif mysql_close(my); return OK; } @@ -174,7 +178,11 @@ static int test_multi_ssl_connections(MYSQL *unused) } cipher= (char *)mysql_get_ssl_cipher(mysql[i]); +#ifdef HAVE_OPENSSL FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA"); +#elif defined(HAVE_HNUTLS) + FAIL_IF(strcmp(cipher, "AES-256-CBC") != 0, "Cipher != AES-256-CBC"); +#endif } for (i=0; i < 50; i++) mysql_close(mysql[i]); @@ -637,7 +645,11 @@ static int test_ssl_fp(MYSQL *unused) port, socketname, 0), mysql_error(my)); cipher= (char *)mysql_get_ssl_cipher(my); +#ifdef HAVE_OPENSSL FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA"); +#elif defined(HAVE_HNUTLS) + FAIL_IF(strcmp(cipher, "AES-256-CBC") != 0, "Cipher != AES-256-CBC"); +#endif mysql_close(my); return OK; } @@ -660,8 +672,11 @@ static int test_ssl_fp_list(MYSQL *unused) FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, port, socketname, 0), mysql_error(my)); - cipher= (char *)mysql_get_ssl_cipher(my); +#ifdef HAVE_OPENSSL FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA"); +#elif defined(HAVE_HNUTLS) + FAIL_IF(strcmp(cipher, "AES-256-CBC") != 0, "Cipher != AES-256-CBC"); +#endif mysql_close(my); return OK; } From df8f6225f78d1911ecd163065cd151c3207eac95 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sun, 16 Aug 2015 12:00:59 +0200 Subject: [PATCH 08/18] schannel is no longer plugin --- {plugins/builtin => libmariadb/secure}/ma_schannel.c | 0 {plugins/builtin => libmariadb/secure}/ma_schannel.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {plugins/builtin => libmariadb/secure}/ma_schannel.c (100%) rename {plugins/builtin => libmariadb/secure}/ma_schannel.h (100%) diff --git a/plugins/builtin/ma_schannel.c b/libmariadb/secure/ma_schannel.c similarity index 100% rename from plugins/builtin/ma_schannel.c rename to libmariadb/secure/ma_schannel.c diff --git a/plugins/builtin/ma_schannel.h b/libmariadb/secure/ma_schannel.h similarity index 100% rename from plugins/builtin/ma_schannel.h rename to libmariadb/secure/ma_schannel.h From 0cc8a364541abb098eaaab768b89576b73ded888 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sun, 16 Aug 2015 11:33:31 +0200 Subject: [PATCH 09/18] Fix for CONC-137: Error code not set in mysql_stmt_send_long_data --- libmariadb/my_stmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libmariadb/my_stmt.c b/libmariadb/my_stmt.c index a3225874..e5a7ac53 100644 --- a/libmariadb/my_stmt.c +++ b/libmariadb/my_stmt.c @@ -1811,7 +1811,7 @@ my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, DBUG_RETURN(1); } - if (stmt->mysql->status== MYSQL_STATUS_READY && (length || !stmt->params[param_number].long_data_used)) + if (length || !stmt->params[param_number].long_data_used) { int ret; size_t packet_len; @@ -1824,7 +1824,7 @@ my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, my_free(cmd_buff); DBUG_RETURN(ret); } - DBUG_RETURN(1); + DBUG_RETURN(0); } my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt) From 4597cd6a8090ffaf3328356014639c0c68d447b3 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 10 Sep 2015 17:16:21 +0200 Subject: [PATCH 10/18] Various ssl and schannel fixes --- include/ma_secure.h | 45 -- include/ma_ssl.h | 1 + libmariadb/libmariadb.c | 30 +- libmariadb/ma_cio.c | 2 +- libmariadb/ma_secure.c | 688 ---------------- libmariadb/ma_ssl.c | 10 +- libmariadb/my_malloc.c | 9 +- libmariadb/secure/gnutls.c | 2 +- libmariadb/secure/ma_schannel.c | 394 +++++++--- libmariadb/secure/ma_schannel.h | 35 +- libmariadb/secure/openssl.c | 7 +- libmariadb/secure/schannel.c | 542 +++++++------ plugins/builtin/cio_socket.c | 1 + plugins/cio/tls_schannel.c | 1257 ------------------------------ plugins/trace/trace_example.c | 1 - plugins/trace/trace_example.so | Bin 36859 -> 43254 bytes unittest/libmariadb/connection.c | 2 +- unittest/libmariadb/dyncol.c | 3 +- unittest/libmariadb/misc.c | 1 + unittest/libmariadb/ssl.c.in | 74 +- 20 files changed, 731 insertions(+), 2373 deletions(-) delete mode 100644 include/ma_secure.h delete mode 100644 libmariadb/ma_secure.c delete mode 100644 plugins/cio/tls_schannel.c diff --git a/include/ma_secure.h b/include/ma_secure.h deleted file mode 100644 index b380630a..00000000 --- a/include/ma_secure.h +++ /dev/null @@ -1,45 +0,0 @@ -/************************************************************************************ - 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 - or write to the Free Software Foundation, Inc., - 51 Franklin St., Fifth Floor, Boston, MA 02110, USA - - Part of this code includes code from the PHP project which - is freely available from http://www.php.net -*************************************************************************************/ -#ifndef _ma_secure_h_ -#define _ma_secure_h_ - -#ifdef HAVE_OPENSSL -#include -#include /* SSL and SSL_CTX */ -#include /* error reporting */ -#include - -struct MYSQL; - -size_t my_ssl_read(Vio *vio, uchar* buf, size_t size); -int my_ssl_close(Vio *vio); -size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size); -SSL *my_ssl_init(MYSQL *mysql); -int my_ssl_connect(SSL *ssl); -int my_ssl_verify_server_cert(SSL *ssl); -int ma_ssl_verify_fingerprint(SSL *ssl); - -int my_ssl_start(MYSQL *mysql); -void my_ssl_end(void); - -#endif /* HAVE_OPENSSL */ -#endif /* _ma_secure_h_ */ diff --git a/include/ma_ssl.h b/include/ma_ssl.h index cdd33162..b9c84b77 100644 --- a/include/ma_ssl.h +++ b/include/ma_ssl.h @@ -133,5 +133,6 @@ my_bool ma_cio_ssl_close(MARIADB_SSL *cssl); int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl); const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl); my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_list); +my_bool ma_cio_start_ssl(MARIADB_CIO *cio); #endif /* _ma_ssl_h_ */ diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index 4710f644..bbc03e18 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -1145,13 +1145,15 @@ int STDCALL mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher) { - mysql->options.ssl_key = key==0 ? 0 : my_strdup(key,MYF(0)); - mysql->options.ssl_cert = cert==0 ? 0 : my_strdup(cert,MYF(0)); - mysql->options.ssl_ca = ca==0 ? 0 : my_strdup(ca,MYF(0)); - mysql->options.ssl_capath = capath==0 ? 0 : my_strdup(capath,MYF(0)); - mysql->options.ssl_cipher = cipher==0 ? 0 : my_strdup(cipher,MYF(0)); -/* todo: add crl stuff */ +#ifdef HAVE_SSL + return (mysql_optionsv(mysql, MYSQL_OPT_SSL_KEY, key) | + mysql_optionsv(mysql, MYSQL_OPT_SSL_CERT, cert) | + mysql_optionsv(mysql, MYSQL_OPT_SSL_CA, ca) | + mysql_optionsv(mysql, MYSQL_OPT_SSL_CAPATH, capath) | + mysql_optionsv(mysql, MYSQL_OPT_SSL_CIPHER, cipher)) ? 1 : 0; +#else return 0; +#endif } /************************************************************************** @@ -2674,23 +2676,23 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) break; case MYSQL_OPT_SSL_KEY: my_free(mysql->options.ssl_key); - mysql->options.ssl_key=my_strdup((char *)arg1,MYF(MY_WME)); + mysql->options.ssl_key=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); break; case MYSQL_OPT_SSL_CERT: my_free(mysql->options.ssl_cert); - mysql->options.ssl_cert=my_strdup((char *)arg1,MYF(MY_WME)); + mysql->options.ssl_cert=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); break; case MYSQL_OPT_SSL_CA: my_free(mysql->options.ssl_ca); - mysql->options.ssl_ca=my_strdup((char *)arg1,MYF(MY_WME)); + mysql->options.ssl_ca=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); break; case MYSQL_OPT_SSL_CAPATH: my_free(mysql->options.ssl_capath); - mysql->options.ssl_capath=my_strdup((char *)arg1,MYF(MY_WME)); + mysql->options.ssl_capath=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); break; case MYSQL_OPT_SSL_CIPHER: my_free(mysql->options.ssl_cipher); - mysql->options.ssl_cipher=my_strdup((char *)arg1,MYF(MY_WME)); + mysql->options.ssl_cipher=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); break; case MYSQL_OPT_SSL_CRL: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1); @@ -2789,6 +2791,12 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) my_free(mysql->options.bind_address); mysql->options.bind_address= my_strdup(arg1, MYF(MY_WME)); break; + case MARIADB_OPT_SSL_FP: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp, (char *)arg1); + break; + case MARIADB_OPT_SSL_FP_LIST: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp_list, (char *)arg1); + break; default: va_end(ap); DBUG_RETURN(-1); diff --git a/libmariadb/ma_cio.c b/libmariadb/ma_cio.c index 0b234a48..fabb1417 100644 --- a/libmariadb/ma_cio.c +++ b/libmariadb/ma_cio.c @@ -283,7 +283,7 @@ size_t ma_cio_cache_read(MARIADB_CIO *cio, uchar *buffer, size_t length) } memcpy(buffer, cio->cache, r); } - } + } return r; } /* }}} */ diff --git a/libmariadb/ma_secure.c b/libmariadb/ma_secure.c deleted file mode 100644 index cda76f38..00000000 --- a/libmariadb/ma_secure.c +++ /dev/null @@ -1,688 +0,0 @@ -/************************************************************************************ - 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 - or write to the Free Software Foundation, Inc., - 51 Franklin St., Fifth Floor, Boston, MA 02110, USA - - *************************************************************************************/ -unsigned int mariadb_deinitialize_ssl= 1; -#ifdef HAVE_OPENSSL - -#include -#include -#include -#include -#include -#include -#include -#include - -static my_bool my_ssl_initialized= FALSE; -static SSL_CTX *SSL_context= NULL; - -#define MAX_SSL_ERR_LEN 100 - -extern pthread_mutex_t LOCK_ssl_config; -static pthread_mutex_t *LOCK_crypto= NULL; - -/* - SSL error handling -*/ -static void my_SSL_error(MYSQL *mysql) -{ - ulong ssl_errno= ERR_get_error(); - char ssl_error[MAX_SSL_ERR_LEN]; - const char *ssl_error_reason; - - DBUG_ENTER("my_SSL_error"); - - if (mysql_errno(mysql)) - DBUG_VOID_RETURN; - - if (!ssl_errno) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); - DBUG_VOID_RETURN; - } - if ((ssl_error_reason= ERR_reason_error_string(ssl_errno))) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), ssl_error_reason); - DBUG_VOID_RETURN; - } - my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset); - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), ssl_error); - DBUG_VOID_RETURN; -} - -/* - thread safe callbacks for OpenSSL - Crypto call back functions will be - set during ssl_initialization - */ -#if (OPENSSL_VERSION_NUMBER < 0x10000000) -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 - -static void my_cb_locking(int mode, int n, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) - pthread_mutex_lock(&LOCK_crypto[n]); - else - pthread_mutex_unlock(&LOCK_crypto[n]); -} - - -static int ssl_crypto_init() -{ - int i, rc= 1, max= CRYPTO_num_locks(); - -#if (OPENSSL_VERSION_NUMBER < 0x10000000) - CRYPTO_set_id_callback(my_cb_threadid); -#else - rc= CRYPTO_THREADID_set_callback(my_cb_threadid); -#endif - - /* if someone else already set callbacks - * there is nothing do */ - if (!rc) - return 0; - - if (LOCK_crypto == NULL) - { - if (!(LOCK_crypto= - (pthread_mutex_t *)my_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); - - return 0; -} - - -/* - Initializes SSL and allocate global - context SSL_context - - SYNOPSIS - my_ssl_start - mysql connection handle - - RETURN VALUES - 0 success - 1 error -*/ -int my_ssl_start(MYSQL *mysql) -{ - int rc= 0; - DBUG_ENTER("my_ssl_start"); - /* lock mutex to prevent multiple initialization */ - pthread_mutex_lock(&LOCK_ssl_config); - if (!my_ssl_initialized) - { - if (ssl_crypto_init()) - goto end; - SSL_library_init(); - -#if SSLEAY_VERSION_NUMBER >= 0x00907000L - OPENSSL_config(NULL); -#endif - /* load errors */ - SSL_load_error_strings(); - /* digests and ciphers */ - OpenSSL_add_all_algorithms(); - - if (!(SSL_context= SSL_CTX_new(TLSv1_client_method()))) - { - my_SSL_error(mysql); - rc= 1; - goto end; - } - my_ssl_initialized= TRUE; - } -end: - pthread_mutex_unlock(&LOCK_ssl_config); - DBUG_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 my_ssl_end() -{ - DBUG_ENTER("my_ssl_end"); - pthread_mutex_lock(&LOCK_ssl_config); - if (my_ssl_initialized) - { - int i; - - if (LOCK_crypto) - { - CRYPTO_set_locking_callback(NULL); - CRYPTO_set_id_callback(NULL); - - for (i=0; i < CRYPTO_num_locks(); i++) - pthread_mutex_destroy(&LOCK_crypto[i]); - - my_free(LOCK_crypto); - LOCK_crypto= NULL; - } - - if (SSL_context) - { - SSL_CTX_free(SSL_context); - SSL_context= FALSE; - } - if (mariadb_deinitialize_ssl) - { - ERR_remove_state(0); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); - ERR_free_strings(); - CONF_modules_free(); - CONF_modules_unload(1); - sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); - } - my_ssl_initialized= FALSE; - } - pthread_mutex_unlock(&LOCK_ssl_config); - pthread_mutex_destroy(&LOCK_ssl_config); - DBUG_VOID_RETURN; -} - -/* - Set certification stuff. -*/ -static int my_ssl_set_certs(MYSQL *mysql) -{ - char *certfile= mysql->options.ssl_cert, - *keyfile= mysql->options.ssl_key; - - DBUG_ENTER("my_ssl_set_certs"); - - /* Make sure that ssl was allocated and - ssl_system was initialized */ - DBUG_ASSERT(my_ssl_initialized == TRUE); - - /* add cipher */ - if ((mysql->options.ssl_cipher && - mysql->options.ssl_cipher[0] != 0) && - SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0) - goto error; - - /* ca_file and ca_path */ - if (SSL_CTX_load_verify_locations(SSL_context, - mysql->options.ssl_ca, - mysql->options.ssl_capath) == 0) - { - if (mysql->options.ssl_ca || mysql->options.ssl_capath) - goto error; - if (SSL_CTX_set_default_verify_paths(SSL_context) == 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_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1) - goto error; - - /* set key */ - if (keyfile && keyfile[0]) - { - if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1) - goto error; - } - /* verify key */ - if (certfile && !SSL_CTX_check_private_key(SSL_context)) - 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(SSL_context))) - { - if (X509_STORE_load_locations(certstore, mysql->options.ssl_ca, - mysql->options.ssl_capath) == 0 || - X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | - X509_V_FLAG_CRL_CHECK_ALL) == 0) - goto error; - } - } - - DBUG_RETURN(0); - -error: - my_SSL_error(mysql); - DBUG_RETURN(1); -} - -static unsigned int ma_get_cert_fingerprint(X509 *cert, EVP_MD *digest, - unsigned char *fingerprint, unsigned int *fp_length) -{ - if (*fp_length < EVP_MD_size(digest)) - return 0; - if (!X509_digest(cert, digest, fingerprint, fp_length)) - return 0; - return *fp_length; -} - -static my_bool ma_check_fingerprint(char *fp1, unsigned int fp1_len, - char *fp2, unsigned int fp2_len) -{ - char hexstr[fp1_len * 2 + 1]; - - fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len); - if (strncasecmp(hexstr, fp2, fp1_len) != 0) - return 1; - return 0; -} - -int my_verify_callback(int ok, X509_STORE_CTX *ctx) -{ - X509 *check_cert; - SSL *ssl; - MYSQL *mysql; - DBUG_ENTER("my_verify_callback"); - - ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - DBUG_ASSERT(ssl != NULL); - mysql= (MYSQL *)SSL_get_app_data(ssl); - DBUG_ASSERT(mysql != NULL); - - /* skip verification if no ca_file/path was specified */ - if (!mysql->options.ssl_ca && !mysql->options.ssl_capath) - { - ok= 1; - DBUG_RETURN(1); - } - - if (!ok) - { - uint depth; - if (!(check_cert= X509_STORE_CTX_get_current_cert(ctx))) - DBUG_RETURN(0); - depth= X509_STORE_CTX_get_error_depth(ctx); - if (depth == 0) - ok= 1; - } - -/* - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - X509_verify_cert_error_string(ctx->error)); -*/ - DBUG_RETURN(ok); -} - - -/* - allocates a new ssl object - - SYNOPSIS - my_ssl_init - mysql connection object - - RETURN VALUES - NULL on error - SSL new SSL object -*/ -SSL *my_ssl_init(MYSQL *mysql) -{ - int verify; - SSL *ssl= NULL; - - DBUG_ENTER("my_ssl_init"); - - DBUG_ASSERT(mysql->net.vio->ssl == NULL); - - if (!my_ssl_initialized) - my_ssl_start(mysql); - - pthread_mutex_lock(&LOCK_ssl_config); - if (my_ssl_set_certs(mysql)) - goto error; - - if (!(ssl= SSL_new(SSL_context))) - goto error; - - if (!SSL_set_app_data(ssl, mysql)) - goto error; - - verify= (!mysql->options.ssl_ca && !mysql->options.ssl_capath) ? - SSL_VERIFY_NONE : SSL_VERIFY_PEER; - - SSL_CTX_set_verify(SSL_context, verify, my_verify_callback); - SSL_CTX_set_verify_depth(SSL_context, 1); - - pthread_mutex_unlock(&LOCK_ssl_config); - DBUG_RETURN(ssl); -error: - pthread_mutex_unlock(&LOCK_ssl_config); - if (ssl) - SSL_free(ssl); - DBUG_RETURN(NULL); -} - -/* - establish SSL connection between client - and server - - SYNOPSIS - my_ssl_connect - ssl ssl object - - RETURN VALUES - 0 success - 1 error -*/ -int my_ssl_connect(SSL *ssl) -{ - my_bool blocking; - MYSQL *mysql; - long rc; - - DBUG_ENTER("my_ssl_connect"); - - DBUG_ASSERT(ssl != NULL); - - mysql= (MYSQL *)SSL_get_app_data(ssl); - CLEAR_CLIENT_ERROR(mysql); - - /* Set socket to blocking if not already set */ - if (!(blocking= vio_is_blocking(mysql->net.vio))) - vio_blocking(mysql->net.vio, TRUE, 0); - - SSL_clear(ssl); - SSL_SESSION_set_timeout(SSL_get_session(ssl), - mysql->options.connect_timeout); - SSL_set_fd(ssl, mysql->net.vio->sd); - - if (SSL_connect(ssl) != 1) - { - my_SSL_error(mysql); - /* restore blocking mode */ - if (!blocking) - vio_blocking(mysql->net.vio, FALSE, 0); - DBUG_RETURN(1); - } - - rc= SSL_get_verify_result(ssl); - if (rc != X509_V_OK) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc)); - /* restore blocking mode */ - if (!blocking) - vio_blocking(mysql->net.vio, FALSE, 0); - - DBUG_RETURN(1); - } - - vio_reset(mysql->net.vio, VIO_TYPE_SSL, mysql->net.vio->sd, 0, 0); - mysql->net.vio->ssl= ssl; - DBUG_RETURN(0); -} - -int ma_ssl_verify_fingerprint(SSL *ssl) -{ - X509 *cert= SSL_get_peer_certificate(ssl); - MYSQL *mysql= (MYSQL *)SSL_get_app_data(ssl); - unsigned char fingerprint[EVP_MAX_MD_SIZE]; - EVP_MD *digest; - unsigned int fp_length; - - DBUG_ENTER("ma_ssl_verify_fingerprint"); - - if (!cert) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Unable to get server certificate"); - DBUG_RETURN(1); - } - - digest= (EVP_MD *)EVP_sha1(); - fp_length= sizeof(fingerprint); - - if (!ma_get_cert_fingerprint(cert, digest, fingerprint, &fp_length)) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Unable to get finger print of server certificate"); - DBUG_RETURN(1); - } - - /* single finger print was specified */ - if (mysql->options.extension->ssl_fp) - { - if (ma_check_fingerprint(fingerprint, fp_length, mysql->options.extension->ssl_fp, - strlen(mysql->options.extension->ssl_fp))) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "invalid finger print of server certificate"); - DBUG_RETURN(1); - } - } - - /* white list of finger prints was specified */ - if (mysql->options.extension->ssl_fp_list) - { - FILE *fp; - char buff[255]; - - if (!(fp = my_fopen(mysql->options.extension->ssl_fp_list ,O_RDONLY, MYF(0)))) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Can't open finger print list"); - DBUG_RETURN(1); - } - - while (fgets(buff, sizeof(buff)-1, fp)) - { - /* remove trailing new line character */ - char *pos= strchr(buff, '\r'); - if (!pos) - pos= strchr(buff, '\n'); - if (pos) - *pos= '\0'; - - if (!ma_check_fingerprint(fingerprint, fp_length, buff, strlen(buff))) - { - /* finger print is valid: close file and exit */ - my_fclose(fp, MYF(0)); - DBUG_RETURN(0); - } - } - - /* No finger print matched - close file and return error */ - my_fclose(fp, MYF(0)); - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "invalid finger print of server certificate"); - DBUG_RETURN(1); - } - DBUG_RETURN(0); -} - -/* - verify server certificate - - SYNOPSIS - my_ssl_verify_server_cert() - MYSQL mysql - mybool verify_server_cert; - - RETURN VALUES - 1 Error - 0 OK -*/ - -int my_ssl_verify_server_cert(SSL *ssl) -{ - X509 *cert; - MYSQL *mysql; - char *p1, *p2, buf[256]; - - DBUG_ENTER("my_ssl_verify_server_cert"); - - mysql= (MYSQL *)SSL_get_app_data(ssl); - - if (!mysql->host) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Invalid (empty) hostname"); - DBUG_RETURN(1); - } - - if (!(cert= SSL_get_peer_certificate(ssl))) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Unable to get server certificate"); - DBUG_RETURN(1); - } - - X509_NAME_oneline(X509_get_subject_name(cert), buf, 256); - X509_free(cert); - - /* Extract the server name from buffer: - Format: ....CN=/hostname/.... */ - if ((p1= strstr(buf, "/CN="))) - { - p1+= 4; - if ((p2= strchr(p1, '/'))) - *p2= 0; - if (!strcmp(mysql->host, p1)) - DBUG_RETURN(0); - } - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Validation of SSL server certificate failed"); - DBUG_RETURN(1); -} -/* - write to ssl socket - - SYNOPSIS - my_ssl_write() - vio vio - buf write buffer - size size of buffer - - RETURN VALUES - bytes written -*/ -size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size) -{ - size_t written; - DBUG_ENTER("my_ssl_write"); - if (vio->async_context && vio->async_context->active) - written= my_ssl_write_async(vio->async_context, (SSL *)vio->ssl, buf, - size); - else - written= SSL_write((SSL*) vio->ssl, buf, size); - DBUG_RETURN(written); -} - -/* - read from ssl socket - - SYNOPSIS - my_ssl_read() - vio vio - buf read buffer - size_t max number of bytes to read - - RETURN VALUES - number of bytes read -*/ -size_t my_ssl_read(Vio *vio, uchar* buf, size_t size) -{ - size_t read; - DBUG_ENTER("my_ssl_read"); - - if (vio->async_context && vio->async_context->active) - read= my_ssl_read_async(vio->async_context, (SSL *)vio->ssl, buf, size); - else - read= SSL_read((SSL*) vio->ssl, buf, size); - DBUG_RETURN(read); -} - -/* - close ssl connection and free - ssl object - - SYNOPSIS - my_ssl_close() - vio vio - - RETURN VALUES - 1 ok - 0 or -1 on error -*/ -int my_ssl_close(Vio *vio) -{ - int i, rc; - DBUG_ENTER("my_ssl_close"); - - if (!vio || !vio->ssl) - DBUG_RETURN(1); - - SSL_set_quiet_shutdown(vio->ssl, 1); - /* 2 x pending + 2 * data = 4 */ - for (i=0; i < 4; i++) - if ((rc= SSL_shutdown(vio->ssl))) - break; - - SSL_free(vio->ssl); - vio->ssl= NULL; - - DBUG_RETURN(rc); -} - -#endif /* HAVE_OPENSSL */ diff --git a/libmariadb/ma_ssl.c b/libmariadb/ma_ssl.c index 272df926..3e9708ee 100644 --- a/libmariadb/ma_ssl.c +++ b/libmariadb/ma_ssl.c @@ -49,6 +49,7 @@ /* Errors should be handled via cio callback function */ my_bool ma_ssl_initialized= FALSE; +unsigned int mariadb_deinitialize_ssl= 1; MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql) { @@ -106,10 +107,14 @@ const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl) static my_bool ma_cio_ssl_compare_fp(char *fp1, unsigned int fp1_len, char *fp2, unsigned int fp2_len) { - char hexstr[fp1_len * 2 + 1]; + char hexstr[64]; fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len); +#ifdef WIN32 + if (strnicmp(hexstr, fp2, fp1_len) != 0) +#else if (strncasecmp(hexstr, fp2, fp1_len) != 0) +#endif return 1; return 0; } @@ -121,9 +126,8 @@ my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_li MYSQL *mysql; my_bool rc=1; - if (ma_ssl_get_finger_print(cssl, cert_fp, cert_fp_len) < 1) + if ((cert_fp_len= ma_ssl_get_finger_print(cssl, cert_fp, cert_fp_len)) < 1) goto end; - if (fp) rc= ma_cio_ssl_compare_fp(cert_fp, cert_fp_len, fp, strlen(fp)); else if (fp_list) diff --git a/libmariadb/my_malloc.c b/libmariadb/my_malloc.c index 0e681cae..429932d4 100644 --- a/libmariadb/my_malloc.c +++ b/libmariadb/my_malloc.c @@ -77,7 +77,14 @@ gptr my_memdup(const unsigned char *from, size_t length, myf MyFlags) my_string my_strdup(const char *from, myf MyFlags) { gptr ptr; - uint length=(uint) strlen(from)+1; + uint length; + + if (MyFlags & MY_ALLOW_ZERO_PTR) + if (!from) + return NULL; + + length=(uint) strlen(from)+1; + if ((ptr=my_malloc(length,MyFlags)) != 0) memcpy((unsigned char*) ptr, (unsigned char*) from,(size_t) length); return((my_string) ptr); diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index bb22230b..ee48201d 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -386,7 +386,7 @@ unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsig return 0; } - if (gnutls_fingerprint(GNUTLS_DIG_MD5, &cert_list[0], fp, &fp_len) > 0) + if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &cert_list[0], fp, &fp_len) == 0) return fp_len; else { diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index c66684dd..d27d895f 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -20,8 +20,75 @@ *************************************************************************************/ #include "ma_schannel.h" +#include #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_sec_error */ +void ma_schannel_set_sec_error(MARIADB_CIO *cio, DWORD ErrorNo) +{ + MYSQL *mysql= cio->mysql; + switch(ErrorNo) { + case SEC_E_UNTRUSTED_ROOT: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Untrusted root certificate"); + break; + case SEC_E_BUFFER_TOO_SMALL: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Buffer too small"); + break; + case SEC_E_CRYPTO_SYSTEM_INVALID: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Cipher is not supported"); + break; + case SEC_E_INSUFFICIENT_MEMORY: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Out of memory"); + break; + case SEC_E_OUT_OF_SEQUENCE: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Invalid message sequence"); + break; + case SEC_E_DECRYPT_FAILURE: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "An error occured during decrypting data"); + break; + case SEC_I_INCOMPLETE_CREDENTIALS: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Incomplete credentials"); + break; + case SEC_E_ENCRYPT_FAILURE: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "An error occured during encrypting data"); + break; + case SEC_I_CONTEXT_EXPIRED: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Context expired: "); + case SEC_E_OK: + break; + default: + cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error (%d)", ErrorNo); + } +} +/* }}} */ + +/* {{{ void ma_schnnel_set_win_error */ +void ma_schannel_set_win_error(MARIADB_CIO *cio) +{ + ulong ssl_errno= GetLastError(); + char ssl_error[MAX_SSL_ERR_LEN]; + char *ssl_error_reason= NULL; + + if (!ssl_errno) + { + cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); + return; + } + /* todo: obtain error messge */ + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ssl_errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &ssl_error_reason, 0, NULL ); + cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error_reason); + + if (ssl_error_reason) + LocalFree(ssl_error_reason); + return; +} +/* }}} */ /* {{{ LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) */ /* @@ -44,7 +111,7 @@ LPBYTE * a pointer to a binary der object buffer_len will contain the length of binary der object */ -static LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) +static LPBYTE ma_schannel_load_pem(MARIADB_CIO *cio, const char *PemFileName, DWORD *buffer_len) { HANDLE hfile; char *buffer= NULL; @@ -57,32 +124,53 @@ static LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) return NULL; - if ((hfile= CreateFile(PemFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, + if ((hfile= CreateFile(PemFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE) + { + ma_schannel_set_win_error(cio); return NULL; + } if (!(*buffer_len = GetFileSize(hfile, NULL))) + { + cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Invalid pem format"); goto end; + } if (!(buffer= LocalAlloc(0, *buffer_len + 1))) + { + cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); goto end; + } if (!ReadFile(hfile, buffer, *buffer_len, &dwBytesRead, NULL)) + { + ma_schannel_set_win_error(cio); goto end; + } CloseHandle(hfile); /* calculate the length of DER binary */ if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER, NULL, &der_buffer_length, NULL, NULL)) + { + ma_schannel_set_win_error(cio); goto end; + } /* allocate DER binary buffer */ if (!(der_buffer= (LPBYTE)LocalAlloc(0, der_buffer_length))) + { + cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); goto end; + } /* convert to DER binary */ if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER, der_buffer, &der_buffer_length, NULL, NULL)) + { + ma_schannel_set_win_error(cio); goto end; + } *buffer_len= der_buffer_length; LocalFree(buffer); @@ -101,12 +189,13 @@ end: } /* }}} */ -/* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file) */ +/* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file) */ /* Create a certification context from ca or cert file SYNOPSIS ma_schannel_create_cert_context() + cio cio object pem_file name of certificate or ca file DESCRIPTION @@ -119,7 +208,7 @@ end: NULL If loading of the file or creating context failed CERT_CONTEXT * A pointer to a certification context structure */ -CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file) +CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file) { DWORD der_buffer_length; LPBYTE der_buffer= NULL; @@ -127,10 +216,12 @@ CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file) CERT_CONTEXT *ctx= NULL; /* create DER binary object from ca/certification file */ - if (!(der_buffer= ma_schannel_load_pem(pem_file, (DWORD *)&der_buffer_length))) + if (!(der_buffer= ma_schannel_load_pem(cio, pem_file, (DWORD *)&der_buffer_length))) goto end; - ctx= CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - der_buffer, der_buffer_length); + if (!(ctx= CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + der_buffer, der_buffer_length))) + ma_schannel_set_win_error(cio); + end: if (der_buffer) LocalFree(der_buffer); @@ -138,7 +229,7 @@ end: } /* }}} */ -/* {{{ PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file) */ +/* {{{ PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file) */ /* Create a crl context from crlfile @@ -156,7 +247,7 @@ end: NULL If loading of the file or creating context failed PCCRL_CONTEXT A pointer to a certification context structure */ -PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file) +PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file) { DWORD der_buffer_length; LPBYTE der_buffer= NULL; @@ -164,10 +255,11 @@ PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file) PCCRL_CONTEXT ctx= NULL; /* load ca pem file into memory */ - if (!(der_buffer= ma_schannel_load_pem(pem_file, (DWORD *)&der_buffer_length))) + if (!(der_buffer= ma_schannel_load_pem(cio, pem_file, (DWORD *)&der_buffer_length))) goto end; - ctx= CertCreateCRLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - der_buffer, der_buffer_length); + if (!(ctx= CertCreateCRLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + der_buffer, der_buffer_length))) + ma_schannel_set_win_error(cio); end: if (der_buffer) LocalFree(der_buffer); @@ -175,7 +267,7 @@ end: } /* }}} */ -/* {{{ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) */ +/* {{{ my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file) */ /* Load privte key into context @@ -195,7 +287,7 @@ end: PCCRL_CONTEXT A pointer to a certification context structure */ -my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) +my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file) { DWORD der_buffer_len= 0; LPBYTE der_buffer= NULL; @@ -203,11 +295,11 @@ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) LPBYTE priv_key= NULL; HCRYPTPROV crypt_prov= NULL; HCRYPTKEY crypt_key= NULL; - CRYPT_KEY_PROV_INFO kpi; + CERT_KEY_CONTEXT kpi; my_bool rc= 0; /* load private key into der binary object */ - if (!(der_buffer= ma_schannel_load_pem(key_file, &der_buffer_len))) + if (!(der_buffer= ma_schannel_load_pem(cio, key_file, &der_buffer_len))) return 0; /* determine required buffer size for decoded private key */ @@ -216,11 +308,17 @@ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) der_buffer, der_buffer_len, 0, NULL, NULL, &priv_key_len)) + { + ma_schannel_set_win_error(cio); goto end; + } /* allocate buffer for decoded private key */ if (!(priv_key= LocalAlloc(0, priv_key_len))) + { + cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); goto end; + } /* decode */ if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, @@ -228,24 +326,37 @@ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) der_buffer, der_buffer_len, 0, NULL, priv_key, &priv_key_len)) + { + ma_schannel_set_win_error(cio); goto end; + } - /* Acquire context */ + /* Acquire context: + If cio_schannel context doesn't exist, create a new one */ if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, 0)) + if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET)) + { + ma_schannel_set_win_error(cio); goto end; - + } /* ... and import the private key */ if (!CryptImportKey(crypt_prov, priv_key, priv_key_len, NULL, 0, &crypt_key)) + { + ma_schannel_set_win_error(cio); goto end; + } SecureZeroMemory(&kpi, sizeof(kpi)); - kpi.pwszContainerName = "cio-schanel"; + kpi.hCryptProv= crypt_prov; kpi.dwKeySpec = AT_KEYEXCHANGE; - kpi.dwFlags = CRYPT_MACHINE_KEYSET; + kpi.cbSize= sizeof(kpi); /* assign private key to certificate context */ - if (CertSetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi)) + if (CertSetCertificateContextProperty(ctx, CERT_KEY_CONTEXT_PROP_ID, 0, &kpi)) rc= 1; + else + ma_schannel_set_win_error(cio); + end: if (der_buffer) LocalFree(der_buffer); @@ -285,7 +396,7 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead PUCHAR IoBuffer; BOOL fDoRead; MARIADB_SSL *cssl= cio->cssl; - SC_CTX *sctx= (SC_CTX *)cssl->data; + SC_CTX *sctx= (SC_CTX *)cssl->ssl; dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | @@ -416,6 +527,7 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead 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 */ @@ -425,7 +537,10 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead break; default: if (FAILED(rc)) + { + ma_schannel_set_sec_error(cio, rc); goto loopend; + } break; } @@ -472,22 +587,24 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) 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 BufferIn, BufferOut; SecBuffer BuffersOut[1], BuffersIn[2]; - if (!cssl || !cssl->cio || !cssl->data) + if (!cssl || !cssl->cio) return 1; cio= cssl->cio; - sctx= (SC_CTX *)cssl->data; + sctx= (SC_CTX *)cssl->ssl; /* Initialie securifty context */ BuffersOut[0].BufferType= SECBUFFER_TOKEN; BuffersOut[0].cbBuffer= 0; BuffersOut[0].pvBuffer= NULL; + BufferOut.cBuffers= 1; BufferOut.pBuffers= BuffersOut; BufferOut.ulVersion= SECBUFFER_VERSION; @@ -506,7 +623,18 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) NULL); if(sRet != SEC_I_CONTINUE_NEEDED) + { + ma_schannel_set_sec_error(cio, sRet); return sRet; + } + + /* Allocate IO-Buffer */ + sctx->IoBufferSize= 2 * net_buffer_length; + if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(LMEM_ZEROINIT, sctx->IoBufferSize))) + { + sRet= SEC_E_INSUFFICIENT_MEMORY; + goto end; + } /* send client hello packaet */ if(BuffersOut[0].cbBuffer != 0 && BuffersOut[0].pvBuffer != NULL) @@ -518,51 +646,26 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) goto end; } } - return ma_schannel_handshake_loop(cio, TRUE, &ExtraData); + sRet= ma_schannel_handshake_loop(cio, TRUE, &ExtraData); + /* Reallocate IO-Buffer for write operations: After handshake + was successfull, we are able now to calculate payload */ + QueryContextAttributes( &sctx->ctxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes ); + sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes); + sctx->IoBuffer= LocalReAlloc(sctx->IoBuffer, sctx->IoBufferSize, LMEM_ZEROINIT); + + return sRet; end: + LocalFree(sctx->IoBuffer); + sctx->IoBufferSize= 0; FreeContextBuffer(BuffersOut[0].pvBuffer); DeleteSecurityContext(&sctx->ctxt); return sRet; } /* }}} */ -/* {{{ static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLength) */ -/* - alloates an IO Buffer for ssl communication - - SYNOPSUS - ma_schannel_alloc_iobuffer() - Context SChannel context handle - BufferLength a pointer for the buffer length - - DESCRIPTION - calculates the required buffer length and allocates a memory buffer for en- and - decryption. The calculated buffer length will be returned in BufferLength pointer. - The allocated buffer needs to be freed at end of the connection. - - RETURN - NULL if an error occured - PUCHAR an IO Buffer -*/ -static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLength) -{ - SecPkgContext_StreamSizes StreamSizes; - - PUCHAR Buffer= NULL; - - if (!BufferLength || QueryContextAttributes(Context, SECPKG_ATTR_STREAM_SIZES, &StreamSizes) != SEC_E_OK) - return NULL; - - /* Calculate BufferLength */ - *BufferLength= StreamSizes.cbHeader + StreamSizes.cbTrailer + StreamSizes.cbMaximumMessage; - - Buffer= LocalAlloc(LMEM_FIXED, *BufferLength); - return Buffer; -} -/* }}} */ - -/* {{{ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext) */ +/* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext, + DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */ /* Reads encrypted data from a SSL stream and decrypts it. @@ -583,44 +686,34 @@ static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLengt SEC_E_* if an error occured */ -SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, - PCredHandle phCreds, - CtxtHandle * phContext, - DWORD *DecryptLength, - uchar *ReadBuffer, - DWORD ReadBufferSize) +SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_CIO *cio, + PCredHandle phCreds, + CtxtHandle * phContext, + DWORD *DecryptLength, + uchar *ReadBuffer, + DWORD ReadBufferSize) { DWORD dwBytesRead= 0; DWORD dwOffset= 0; SC_CTX *sctx; SECURITY_STATUS sRet= 0; - SecBuffersDesc Msg; + SecBufferDesc Msg; SecBuffer Buffers[4], ExtraBuffer, *pData, *pExtra; int i; - if (!cio || !cio->methods || !cio->methods->read || !cio->cssl || !cio->cssl->data | !DecryptLength) + if (!cio || !cio->methods || !cio->methods->read || !cio->cssl || !DecryptLength) return SEC_E_INTERNAL_ERROR; - sctx= (SC_CTX *)cio->cssl->data; + sctx= (SC_CTX *)cio->cssl->ssl; *DecryptLength= 0; - /* Allocate IoBuffer */ - if (!sctx->IoBuffer) - { - if (!(sctx->IoBuffer= ma_schannel_alloc_iobuffer(&sctx->ctxt, &sctx->IoBufferSize))) - { - /* todo: error */ - return NULL; - } - } - while (1) { - if (!dwOffset || sRet == SEC_E_INCOMPLETE_MESSAGE) + if (!dwBytesRead || sRet == SEC_E_INCOMPLETE_MESSAGE) { - dwBytesRead= cio->methods->read(cio, sctx->IoBuffer + dwOffset, cbIoBufferLength - dwOffset); + dwBytesRead= cio->methods->read(cio, sctx->IoBuffer + dwOffset, sctx->IoBufferSize - dwOffset); if (dwBytesRead == 0) { /* server closed connection */ @@ -633,8 +726,9 @@ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, printf("Socket error\n"); return NULL; } + dwOffset+= dwBytesRead; } - ZeroMem(Buffers, sizeof(SecBuffer) * 4); + ZeroMemory(Buffers, sizeof(SecBuffer) * 4); Buffers[0].pvBuffer= sctx->IoBuffer; Buffers[0].cbBuffer= dwOffset; @@ -652,9 +746,9 @@ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, /* Check for possible errors: we continue in case context has expired or renogitiation is required */ if (sRet != SEC_E_OK && sRet != SEC_I_CONTEXT_EXPIRED && - sRet != SEC_I_RENEGOTIARE) + sRet != SEC_I_RENEGOTIATE && sRet != SEC_E_INCOMPLETE_MESSAGE) { - // set error + ma_schannel_set_sec_error(cio, sRet); return sRet; } @@ -668,11 +762,12 @@ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, if (pData && pExtra) break; } - + if (pData && pData->cbBuffer) { - memcpy(ReadBuffer + *DecrypthLength, pData->pvBuffer, pData->cbBuffer); + memcpy(ReadBuffer + *DecryptLength, pData->pvBuffer, pData->cbBuffer); *DecryptLength+= pData->cbBuffer; + return sRet; } if (pExtra) @@ -684,4 +779,129 @@ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, dwOffset= 0; } } + +my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags) +{ + SECURITY_STATUS sRet; + DWORD flags; + MARIADB_CIO *cio= sctx->mysql->net.cio; + PCCERT_CONTEXT pServerCert= NULL; + + if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK) + { + ma_schannel_set_sec_error(cio, sRet); + return 0; + } + + flags= CERT_STORE_SIGNATURE_FLAG | + CERT_STORE_TIME_VALIDITY_FLAG; + + + + if (sctx->client_ca_ctx) + { + if (!(sRet= CertVerifySubjectCertificateContext(pServerCert, + sctx->client_ca_ctx, + &flags))) + { + ma_schannel_set_win_error(cio); + return 0; + } + + if (flags) + { + if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0) + cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate signature check failed"); + else if ((flags & CERT_STORE_REVOCATION_FLAG) != 0) + cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate was revoked"); + else if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0) + cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate has expired"); + else + cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown error during client certificate validation"); + return 0; + } + } + + /* Check if none of the certificates in the certificate chain have been revoked. */ + if (sctx->client_crl_ctx) + { + PCRL_INFO Info[1]; + + Info[0]= sctx->client_crl_ctx->pCrlInfo; + if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + pServerCert->pCertInfo, + 1, Info)) ) + { + cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CRL Revocation failed"); + return 0; + } + } + return 1; +} + + +/* {{{ size_t ma_schannel_write_encrypt(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext) */ +/* + Decrypts data and write to SSL stream + SYNOPSIS + ma_schannel_write_decrypt + cio 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 occured +*/ +size_t ma_schannel_write_encrypt(MARIADB_CIO *cio, + uchar *WriteBuffer, + size_t WriteBufferSize) +{ + SECURITY_STATUS scRet; + SecBufferDesc Message; + SecBuffer Buffers[4]; + DWORD cbMessage, cbData; + PBYTE pbMessage; + SC_CTX *sctx= (SC_CTX *)cio->cssl->ssl; + size_t payload; + + + payload= MIN(WriteBufferSize, sctx->IoBufferSize); + + memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload); + pbMessage = sctx->IoBuffer + sctx->Sizes.cbHeader; + cbMessage = 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 = 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->ctxt, 0, &Message, 0))!= SEC_E_OK) + return -1; + + if (cio->methods->write(cio, sctx->IoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer)) + return payload; +} /* }}} */ + diff --git a/libmariadb/secure/ma_schannel.h b/libmariadb/secure/ma_schannel.h index 006613ef..6aa937c1 100644 --- a/libmariadb/secure/ma_schannel.h +++ b/libmariadb/secure/ma_schannel.h @@ -28,23 +28,23 @@ #include #include #include -#include + typedef void VOID; #include -#define SECURITY_WIN32 +#include + + #include + #include #undef SECURITY_WIN32 #include +#include #define SC_IO_BUFFER_SIZE 0x4000 -CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file); -SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData); -my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file); -PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file); #ifndef HAVE_SCHANNEL_DEFAULT #define my_snprintf snprintf @@ -61,12 +61,35 @@ struct st_schannel { CredHandle CredHdl; PUCHAR IoBuffer; DWORD IoBufferSize; +/* PUCHAR EncryptBuffer; + DWORD EncryptBufferSize; + DWORD EncryptBufferLength; PUCHAR DecryptBuffer; DWORD DecryptBufferSize; DWORD DecryptBufferLength; + uchar thumbprint[21]; */ + SecPkgContext_StreamSizes Sizes; + CtxtHandle ctxt; + MYSQL *mysql; }; typedef struct st_schannel SC_CTX; +CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file); +SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData); +my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file); +PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file); +my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags); +size_t ma_schannel_write_encrypt(MARIADB_CIO *cio, + uchar *WriteBuffer, + size_t WriteBufferSize); + size_t ma_schannel_read_decrypt(MARIADB_CIO *cio, + PCredHandle phCreds, + CtxtHandle * phContext, + DWORD *DecryptLength, + uchar *ReadBuffer, + DWORD ReadBufferSize); + + #endif /* _ma_schannel_h_ */ diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 18a140c6..26809b11 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -287,10 +287,9 @@ static int ma_ssl_set_certs(MYSQL *mysql) if ((certstore= SSL_CTX_get_cert_store(SSL_context))) { - if (X509_STORE_load_locations(certstore, mysql->options.ssl_ca, - mysql->options.ssl_capath) == 0 || - X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | - X509_V_FLAG_CRL_CHECK_ALL) == 0) + if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl, + mysql->options.extension->ssl_crlpath) == 0 || + X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) == 0) goto error; } } diff --git a/libmariadb/secure/schannel.c b/libmariadb/secure/schannel.c index 26b49f99..e77e661c 100644 --- a/libmariadb/secure/schannel.c +++ b/libmariadb/secure/schannel.c @@ -24,77 +24,72 @@ #define VOID void -static my_bool my_schannel_initialized= FALSE; - -#define MAX_SSL_ERR_LEN 100 +extern my_bool ma_ssl_initialized; static pthread_mutex_t LOCK_schannel_config; static pthread_mutex_t *LOCK_crypto= NULL; -int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list); -int cio_schannel_end(); -void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql); -my_bool cio_schannel_connect(MARIADB_SSL *cssl); -size_t cio_schannel_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length); -size_t cio_schannel_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length); -my_bool cio_schannel_close(MARIADB_SSL *cssl); -int cio_schannel_verify_server_cert(MARIADB_SSL *cssl); -const char *cio_schannel_cipher(MARIADB_SSL *cssl); - -struct st_ma_cio_ssl_methods cio_schannel_methods= { - cio_schannel_init, - cio_schannel_connect, - cio_schannel_read, - cio_schannel_write, - cio_schannel_close, - cio_schannel_verify_server_cert, - cio_schannel_cipher +struct st_cipher_suite { + DWORD aid; + CHAR *cipher; }; -#ifndef HAVE_SCHANNEL_DEFAULT -MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_= -#else -MARIADB_CIO_PLUGIN cio_schannel_plugin= -#endif +void ma_schannel_set_sec_error(MARIADB_CIO *cio, DWORD ErrorNo); +void ma_schannel_set_win_error(MYSQL *mysql); + +const struct st_cipher_suite sc_ciphers[]= { - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, - "cio_schannel", - "Georg Richter", - "MariaDB communication IO plugin for Windows SSL/SChannel communication", - {1, 0, 0}, - "LGPL", - cio_schannel_start, - cio_schannel_end, - NULL, - &cio_schannel_methods, - NULL + {CALG_3DES, "CALG_3DES"}, + {CALG_3DES_112, "CALG_3DES_112"}, + {CALG_AES, "CALG_AES"}, + {CALG_AES_128, "CALG_AES_128"}, + {CALG_AES_192, "CALG_AES_192"}, + {CALG_AES_256, "CALG_AES_256"}, + {CALG_AGREEDKEY_ANY, "CALG_AGREEDKEY_ANY"}, + {CALG_CYLINK_MEK, "CALG_CYLINK_MEK"}, + {CALG_DES, "CALG_DES"}, + {CALG_DESX, "CALG_DESX"}, + {CALG_DH_EPHEM, "CALG_DH_EPHEM"}, + {CALG_DH_SF, "CALG_DH_SF"}, + {CALG_DSS_SIGN, "CALG_DSS_SIGN"}, + {CALG_ECDH, "CALG_ECDH"}, + {CALG_ECDSA, "CALG_ECDSA"}, + {CALG_ECMQV, "CALG_ECMQV"}, + {CALG_HASH_REPLACE_OWF, "CALG_HASH_REPLACE_OWF"}, + {CALG_HUGHES_MD5, "CALG_HUGHES_MD5"}, + {CALG_HMAC, "CALG_HMAC"}, + {CALG_KEA_KEYX, "CALG_KEA_KEYX"}, + {CALG_MAC, "CALG_MAC"}, + {CALG_MD2, "CALG_MD2"}, + {CALG_MD4, "CALG_MD4"}, + {CALG_MD4, "CALG_MD5"}, + {CALG_NO_SIGN, "CALG_NO_SIGN"}, + {CALG_OID_INFO_CNG_ONLY, "CALG_OID_INFO_CNG_ONLY"}, + {CALG_OID_INFO_PARAMETERS, "CALG_OID_INFO_PARAMETERS"}, + {CALG_PCT1_MASTER, "CALG_PCT1_MASTER"}, + {CALG_RC2, "CALG_RC2"}, + {CALG_RC4, "CALG_RC4"}, + {CALG_RC5, "CALG_RC5"}, + {CALG_RSA_KEYX, "CALG_RSA_KEYX"}, + {CALG_RSA_SIGN, "CALG_RSA_SIGN"}, + {CALG_SCHANNEL_MAC_KEY, "CALG_SCHANNEL_MAC_KEY"}, + {CALG_SCHANNEL_MASTER_HASH, "CALG_SCHANNEL_MASTER_HASH"}, + {CALG_SEAL, "CALG_SEAL"}, + {CALG_SHA, "CALG_SHA"}, + {CALG_SHA1, "CALG_SHA1"}, + {CALG_SHA_256, "CALG_SHA_256"}, + {CALG_SHA_384, "CALG_SHA_384"}, + {CALG_SHA_512, "CALG_SHA_512"}, + {CALG_SKIPJACK, "CALG_SKIPJACK"}, + {CALG_SSL2_MASTER, "CALG_SSL2_MASTER"}, + {CALG_SSL3_MASTER, "CALG_SSL3_MASTER"}, + {CALG_SSL3_SHAMD5, "CALG_SSL3_SHAMD5"}, + {CALG_TEK, "CALG_TEK"}, + {CALG_TLS1_MASTER, "CALG_TLS1_MASTER"}, + {CALG_TLS1PRF, "CALG_TLS1PRF"}, + {0, NULL} }; -static void cio_schannel_set_error(MYSQL *mysql) -{ - ulong ssl_errno= GetLastError(); - char ssl_error[MAX_SSL_ERR_LEN]; - char *ssl_error_reason= NULL; - MARIADB_CIO *cio= mysql->net.cio; - - if (!ssl_errno) - { - cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); - return; - } - /* todo: obtain error messge */ - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, ssl_errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &ssl_error_reason, 0, NULL ); - cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error_reason); - - if (ssl_error_reason) - LocalFree(ssl_error_reason); - return; -} - - static int ssl_thread_init() { return 0; @@ -106,22 +101,19 @@ static int ssl_thread_init() context SSL_context SYNOPSIS - cio_schannel_start + ma_ssl_start RETURN VALUES 0 success 1 error */ -int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list list) +int ma_ssl_start(char *errmsg, size_t errmsg_len, int count, va_list list) { - if (!my_schannel_initialized) + if (!ma_ssl_initialized) { pthread_mutex_init(&LOCK_schannel_config,MY_MUTEX_INIT_FAST); pthread_mutex_lock(&LOCK_schannel_config); - - // SecureZeroMemory(&SC_CTX, sizeof(struct st_schannel_global)); - - my_schannel_initialized= TRUE; + ma_ssl_initialized= TRUE; } pthread_mutex_unlock(&LOCK_schannel_config); return 0; @@ -133,27 +125,27 @@ int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list list) mysql_server_end() function SYNOPSIS - cio_schannel_end() + ma_ssl_end() void RETURN VALUES void */ -int cio_schannel_end() +void ma_ssl_end() { pthread_mutex_lock(&LOCK_schannel_config); - if (my_schannel_initialized) + if (ma_ssl_initialized) { - my_schannel_initialized= FALSE; + ma_ssl_initialized= FALSE; } pthread_mutex_unlock(&LOCK_schannel_config); pthread_mutex_destroy(&LOCK_schannel_config); - return 0; + return; } -/* {{{ static int cio_schannel_set_client_certs(MARIADB_SSL *cssl) */ -static int cio_schannel_set_client_certs(MARIADB_SSL *cssl) +/* {{{ static int ma_ssl_set_client_certs(MARIADB_SSL *cssl) */ +static int ma_ssl_set_client_certs(MARIADB_SSL *cssl) { MYSQL *mysql= cssl->cio->mysql; char *certfile= mysql->options.ssl_cert, @@ -161,24 +153,50 @@ static int cio_schannel_set_client_certs(MARIADB_SSL *cssl) *cafile= mysql->options.ssl_ca; SC_CTX *sctx= (SC_CTX *)cssl->ssl; + MARIADB_CIO *cio= cssl->cio; if (cafile) - if (!(sctx->client_ca_ctx = ma_schannel_create_cert_context(cafile))) + { + HCERTSTORE myCS= NULL; + char szName[64]; + + if (!(sctx->client_ca_ctx = ma_schannel_create_cert_context(cio, cafile))) goto end; - if (certfile) - { - if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(certfile))) + /* For X509 authentication we need to add ca certificate to local MY store. + Schannel doesn't provide a callback to send ca to server during handshake */ + if ((myCS= CertOpenStore(CERT_STORE_PROV_SYSTEM, + 0, //X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_SYSTEM_STORE_CURRENT_USER, + L"CA"))) + { + CertAddCertificateContextToStore(myCS, sctx->client_ca_ctx, CERT_STORE_ADD_NEWER, NULL); + CertCloseStore(myCS, 0); + } + else { + ma_schannel_set_win_error(sctx->mysql); goto end; - if (keyfile) - if (!ma_schannel_load_private_key(sctx->client_cert_ctx, keyfile)) - goto end; + } } + if (!certfile && keyfile) + certfile= keyfile; + if (!keyfile && certfile) + keyfile= certfile; + + if (certfile && certfile[0]) + if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(cssl->cio, certfile))) + goto end; + + if (sctx->client_cert_ctx && keyfile[0]) + if (!ma_schannel_load_private_key(cio, sctx->client_cert_ctx, keyfile)) + goto end; + if (mysql->options.extension && mysql->options.extension->ssl_crl) { - sctx->client_crl_ctx= ma_schannel_create_crl_context(mysql->options.extension->ssl_crl); - + if (!(sctx->client_crl_ctx= ma_schannel_create_crl_context(cio, mysql->options.extension->ssl_crl))) + goto end; } return 0; @@ -189,95 +207,207 @@ end: CertFreeCertificateContext(sctx->client_cert_ctx); if (sctx->client_crl_ctx) CertFreeCRLContext(sctx->client_crl_ctx); - - cio_schannel_set_error(mysql); + sctx->client_ca_ctx= sctx->client_cert_ctx= NULL; + sctx->client_crl_ctx= NULL; return 1; } /* }}} */ -/* {{{ void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql) */ -void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql) +/* {{{ void *ma_ssl_init(MARIADB_SSL *cssl, MYSQL *mysql) */ +void *ma_ssl_init(MYSQL *mysql) { int verify; - SC_CTX *sctx; - - if (!(sctx= LocalAlloc(0, sizeof(SC_CTX)))) - return NULL; - ZeroMemory(sctx, sizeof(SC_CTX)); - - cssl->data= (void *)sctx; + SC_CTX *sctx= NULL; pthread_mutex_lock(&LOCK_schannel_config); - return (void *)sctx; -error: + + if ((sctx= LocalAlloc(0, sizeof(SC_CTX)))) + { + ZeroMemory(sctx, sizeof(SC_CTX)); + sctx->mysql= mysql; + } + pthread_mutex_unlock(&LOCK_schannel_config); - return NULL; + return sctx; } /* }}} */ - -static my_bool VerifyServerCertificate(SC_CTX *sctx, PCCERT_CONTEXT pServerCert, PSTR pszServerName, DWORD dwCertFlags ) +my_bool ma_ssl_connect(MARIADB_SSL *cssl) { + my_bool blocking; + MYSQL *mysql; + SCHANNEL_CRED Cred; + MARIADB_CIO *cio; + SC_CTX *sctx; SECURITY_STATUS sRet; - DWORD flags; - char *szName= NULL; - int rc= 0; + PCCERT_CONTEXT pRemoteCertContext = NULL, + pLocalCertContext= NULL; + ALG_ID AlgId[2]= {0, 0}; + + if (!cssl || !cssl->cio) + return 1;; + + cio= cssl->cio; + sctx= (SC_CTX *)cssl->ssl; - /* We perform a manually validation, as described at - http://msdn.microsoft.com/en-us/library/windows/desktop/aa378740%28v=vs.85%29.aspx - */ + /* Set socket to blocking if not already set */ + if (!(blocking= cio->methods->is_blocking(cio))) + cio->methods->blocking(cio, TRUE, 0); - /* Check if - - The certificate chain is complete and the root is a certificate from a trusted certification authority (CA). - - The current time is not beyond the begin and end dates for each of the certificates in the certificate chain. - */ - flags= CERT_STORE_SIGNATURE_FLAG | - CERT_STORE_TIME_VALIDITY_FLAG; - - if (!(sRet= CertVerifySubjectCertificateContext(pServerCert, - sctx->client_ca_ctx, - &flags))) + mysql= cio->mysql; + + if (ma_ssl_set_client_certs(cssl)) + goto end; + + /* Set cipher */ + if (mysql->options.ssl_cipher) { - /* todo: error handling */ - return 0; - } - - /* Check if none of the certificates in the certificate chain have been revoked. */ - if (sctx->client_crl_ctx) - { - PCRL_INFO Info[1]; - - Info[0]= sctx->client_crl_ctx->pCrlInfo; - if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - pServerCert->pCertInfo, - 1, Info)) ) - { - /* todo: error handling */ - return 0; + DWORD i= 0; + while (sc_ciphers[i].cipher) { + if (!strcmp(sc_ciphers[i].cipher, mysql->options.ssl_cipher)) + { + AlgId[0]= sc_ciphers[i].aid; + break; + } } + Cred.palgSupportedAlgs= &AlgId; } + + ZeroMemory(&Cred, sizeof(SCHANNEL_CRED)); + Cred.dwVersion= SCHANNEL_CRED_VERSION; + Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK | SCH_SEND_ROOT_CERT | + SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION; + if (sctx->client_cert_ctx) + { + Cred.cCreds = 1; + Cred.paCred = &sctx->client_cert_ctx; + } + Cred.grbitEnabledProtocols= SP_PROT_TLS1; + + if ((sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, + NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL)) != SEC_E_OK) + { + ma_schannel_set_sec_error(cio, sRet); + goto end; + } + + if (ma_schannel_client_handshake(cssl)) + goto end; + + sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext); + if (sRet != SEC_E_OK) + { + ma_schannel_set_sec_error(cio, sRet); + goto end; + } + + if (!ma_schannel_verify_certs(sctx, 0)) + goto end; + + + return 0; +end: + /* todo: cleanup */ + if (sctx->IoBufferSize) + LocalFree(sctx->IoBuffer); + if (sctx->client_ca_ctx) + CertFreeCertificateContext(sctx->client_ca_ctx); + if (sctx->client_cert_ctx) + CertFreeCertificateContext(sctx->client_cert_ctx); + if (sctx->client_crl_ctx) + CertFreeCRLContext(sctx->client_crl_ctx); + return 1; +} + +size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ + SC_CTX *sctx= (SC_CTX *)cssl->ssl; + MARIADB_CIO *cio= sctx->mysql->net.cio; + size_t dlength= -1; + + ma_schannel_read_decrypt(cio, &sctx->CredHdl, &sctx->ctxt, &dlength, buffer, length); + return dlength; +} + +size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) +{ + SC_CTX *sctx= (SC_CTX *)cssl->ssl; + MARIADB_CIO *cio= sctx->mysql->net.cio; + size_t rc, wlength= 0; + size_t remain= length; + + while (remain) + { + if ((rc= ma_schannel_write_encrypt(cio, (uchar *)buffer + wlength, remain)) <= 0) + return rc; + wlength+= rc; + remain-= rc; + } + return length; +} + +/* {{{ void ma_ssl_close(MARIADB_CIO *cio) */ +void ma_ssl_close(MARIADB_CIO *cio) +{ + SC_CTX *sctx; + if (!cio || !cio->cssl) + return; + + if ((sctx= (SC_CTX *)cio->cssl)) + { + if (sctx->IoBufferSize) + LocalFree(sctx->IoBuffer); + if (sctx->client_ca_ctx) + CertFreeCertificateContext(sctx->client_ca_ctx); + if (sctx->client_cert_ctx) + CertFreeCertificateContext(sctx->client_cert_ctx); + if (sctx->client_crl_ctx) + CertFreeCRLContext(sctx->client_crl_ctx); + FreeCredentialHandle(&sctx->CredHdl); + DeleteSecurityContext(&sctx->ctxt); + } + LocalFree(sctx); +} +/* }}} */ + +int ma_ssl_verify_server_cert(MARIADB_SSL *cssl) +{ + SC_CTX *sctx= (SC_CTX *)cssl->ssl; + MARIADB_CIO *cio= cssl->cio; + int rc= 1; + char *szName= NULL; + char *pszServerName= cio->mysql->host; + /* check server name */ - if (pszServerName) + if (pszServerName && (sctx->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)) { + PCCERT_CONTEXT pServerCert; DWORD NameSize= 0; char *p1, *p2; + SECURITY_STATUS sRet; + + if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK) + { + ma_schannel_set_sec_error(cio, sRet); + return 1; + } if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType, &pServerCert->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, NULL, 0))) { - /* todo: error handling */ - return 0; + cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate"); + return 1; } if (!(szName= LocalAlloc(0, NameSize + 1))) { - /* error handling */ - return 0; + cio->set_error(sctx->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); + goto end; } if (!CertNameToStr(pServerCert->dwCertEncodingType, @@ -285,7 +415,7 @@ static my_bool VerifyServerCertificate(SC_CTX *sctx, PCCERT_CONTEXT pServerCert, CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, szName, NameSize)) { - /* error handling */ + cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate"); goto end; } if ((p1 = strstr(szName, "CN="))) @@ -295,116 +425,50 @@ static my_bool VerifyServerCertificate(SC_CTX *sctx, PCCERT_CONTEXT pServerCert, *p2= 0; if (!strcmp(pszServerName, p1)) { - rc= 1; + rc= 0; goto end; } - + cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + "Name of server certificate didn't match"); } - } - end: - if (szName) - LocalFree(szName); - return rc; - -} - -my_bool cio_schannel_connect(MARIADB_SSL *cssl) -{ - my_bool blocking; - MYSQL *mysql; - SCHANNEL_CRED Cred; - MARIADB_CIO *cio; - SC_CTX *sctx; - SECURITY_STATUS sRet; - PCCERT_CONTEXT pRemoteCertContext = NULL; - - if (!cssl || !cssl->cio || !cssl->data) - return 1;; - - cio= cssl->cio; - sctx= (SC_CTX *)cssl->data; - - /* Set socket to blocking if not already set */ - if (!(blocking= cio->methods->is_blocking(cio))) - cio->methods->blocking(cio, TRUE, 0); - - mysql= cio->mysql; - - if (cio_schannel_set_client_certs(cssl)) - { - cio_schannel_set_error(mysql); - goto end; - } - - ZeroMemory(&Cred, sizeof(SCHANNEL_CRED)); - Cred.dwVersion= SCHANNEL_CRED_VERSION; - Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK | - SCH_CRED_NO_DEFAULT_CREDS | - SCH_CRED_MANUAL_CRED_VALIDATION; - if (sctx->client_cert_ctx) - { - Cred.cCreds = 1; - Cred.paCred = &sctx->client_cert_ctx; - } - Cred.grbitEnabledProtocols= SP_PROT_TLS1; - - /* We allocate 2 x net_buffer_length */ - if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, 0x4000))) - goto end; - - if (AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, - NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL) != SEC_E_OK) - goto end; - - if (ma_schannel_client_handshake(cssl)) - goto end; - - sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext); - if (sRet != SEC_E_OK) - goto end; - - if (!VerifyServerCertificate(sctx, - pRemoteCertContext, - mysql->host, - 0 )) - goto end; - - - return 0; -end: - /* todo: cleanup */ - if (sctx->IoBuffer) - LocalFree(sctx->IoBuffer); - if (sctx->client_ca_ctx) - CertFreeCertificateContext(sctx->client_ca_ctx); - if (sctx->client_cert_ctx) - CertFreeCertificateContext(sctx->client_cert_ctx); - return 1; -} - -size_t cio_schannel_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length) -{ -} - -size_t cio_schannel_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length) -{ -} - -my_bool cio_schannel_close(MARIADB_SSL *cssl) -{ - int i, rc; - + if (szName) + LocalFree(szName); return rc; } -int cio_schannel_verify_server_cert(MARIADB_SSL *cssl) +const char *ma_ssl_get_cipher(MARIADB_SSL *cssl) { -} + SecPkgContext_ConnectionInfo cinfo; + SECURITY_STATUS sRet; + SC_CTX *sctx; + DWORD i= 0; -const char *cio_schannel_cipher(MARIADB_SSL *cssl) -{ if (!cssl || !cssl->ssl) return NULL; + + sctx= (SC_CTX *)cssl->ssl; + + sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_CONNECTION_INFO, (PVOID)&cinfo); + if (sRet != SEC_E_OK) + return NULL; + + while (sc_ciphers[i].cipher) + { + if (sc_ciphers[i].aid == cinfo.aiCipher) + return sc_ciphers[i].cipher; + i++; + } + return NULL; } + +unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int len) +{ + SC_CTX *sctx= (SC_CTX *)cssl->ssl; + PCCERT_CONTEXT pRemoteCertContext = NULL; + if (QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext) != SEC_E_OK) + return NULL; + CertGetCertificateContextProperty(pRemoteCertContext, CERT_HASH_PROP_ID, fp, &len); + return len; +} \ No newline at end of file diff --git a/plugins/builtin/cio_socket.c b/plugins/builtin/cio_socket.c index a4060ab7..485b1c2c 100644 --- a/plugins/builtin/cio_socket.c +++ b/plugins/builtin/cio_socket.c @@ -436,6 +436,7 @@ int cio_socket_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout csock= (struct st_cio_socket *)cio->data; { #ifndef _WIN32 + memset(&p_fd, 0, sizeof(p_fd)); p_fd.fd= csock->socket; p_fd.events= (is_read) ? POLLIN : POLLOUT; diff --git a/plugins/cio/tls_schannel.c b/plugins/cio/tls_schannel.c deleted file mode 100644 index 6fb27536..00000000 --- a/plugins/cio/tls_schannel.c +++ /dev/null @@ -1,1257 +0,0 @@ - * SSL/TLS interface functions for Microsoft Schannel - - * Copyright (c) 2005-2009, Jouni Malinen - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License version 2 as - - * published by the Free Software Foundation. - - * - - * Alternatively, this software may be distributed under the terms of BSD - - * license. - - * - - * See README and COPYING for more details. - - */ - -/* - - * FIX: Go through all SSPI functions and verify what needs to be freed - - * FIX: session resumption - - * TODO: add support for server cert chain validation - - * TODO: add support for CA cert validation - - * TODO: add support for EAP-TLS (client cert/key conf) - - */ - -#include "includes.h" - -#include - -#include - -#include - -#define SECURITY_WIN32 - -#include - -#include - -#include "common.h" - -#include "tls.h" - -struct tls_global { - - HMODULE hsecurity; - - PSecurityFunctionTable sspi; - - HCERTSTORE my_cert_store; - -}; - -struct tls_connection { - - int established, start; - - int failed, read_alerts, write_alerts; - - SCHANNEL_CRED schannel_cred; - - CredHandle creds; - - CtxtHandle context; - - u8 eap_tls_prf[128]; - - int eap_tls_prf_set; - -}; - -static int schannel_load_lib(struct tls_global *global) - -{ - - INIT_SECURITY_INTERFACE pInitSecurityInterface; - - global->hsecurity = LoadLibrary(TEXT("Secur32.dll")); - - if (global->hsecurity == NULL) { - - wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", - - __func__, (unsigned int) GetLastError()); - - return -1; - - } - - pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( - - global->hsecurity, "InitSecurityInterfaceA"); - - if (pInitSecurityInterface == NULL) { - - wpa_printf(MSG_ERROR, "%s: Could not find " - - "InitSecurityInterfaceA from Secur32.dll", - - __func__); - - FreeLibrary(global->hsecurity); - - global->hsecurity = NULL; - - return -1; - - } - - global->sspi = pInitSecurityInterface(); - - if (global->sspi == NULL) { - - wpa_printf(MSG_ERROR, "%s: Could not read security " - - "interface - 0x%x", - - __func__, (unsigned int) GetLastError()); - - FreeLibrary(global->hsecurity); - - global->hsecurity = NULL; - - return -1; - - } - - return 0; - -} - -void * tls_init(const struct tls_config *conf) - -{ - - struct tls_global *global; - - global = os_zalloc(sizeof(*global)); - - if (global == NULL) - - return NULL; - - if (schannel_load_lib(global)) { - - os_free(global); - - return NULL; - - } - - return global; - -} - -void tls_deinit(void *ssl_ctx) - -{ - - struct tls_global *global = ssl_ctx; - - if (global->my_cert_store) - - CertCloseStore(global->my_cert_store, 0); - - FreeLibrary(global->hsecurity); - - os_free(global); - -} - -int tls_get_errors(void *ssl_ctx) - -{ - - return 0; - -} - -struct tls_connection * tls_connection_init(void *ssl_ctx) - -{ - - struct tls_connection *conn; - - conn = os_zalloc(sizeof(*conn)); - - if (conn == NULL) - - return NULL; - - conn->start = 1; - - return conn; - -} - -void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) - -{ - - if (conn == NULL) - - return; - - os_free(conn); - -} - -int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) - -{ - - return conn ? conn->established : 0; - -} - -int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) - -{ - - struct tls_global *global = ssl_ctx; - - if (conn == NULL) - - return -1; - - conn->eap_tls_prf_set = 0; - - conn->established = conn->failed = 0; - - conn->read_alerts = conn->write_alerts = 0; - - global->sspi->DeleteSecurityContext(&conn->context); - - /* FIX: what else needs to be reseted? */ - - return 0; - -} - -int tls_global_set_params(void *tls_ctx, - - const struct tls_connection_params *params) - -{ - - return -1; - -} - -int tls_global_set_verify(void *ssl_ctx, int check_crl) - -{ - - return -1; - -} - -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - - int verify_peer) - -{ - - return -1; - -} - -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - - struct tls_keys *keys) - -{ - - /* Schannel does not export master secret or client/server random. */ - - return -1; - -} - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - - const char *label, int server_random_first, - - u8 *out, size_t out_len) - -{ - - /* - - * Cannot get master_key from Schannel, but EapKeyBlock can be used to - - * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and - - * EAP-TTLS cannot use this, though, since they are using different - - * labels. The only option could be to implement TLSv1 completely here - - * and just use Schannel or CryptoAPI for low-level crypto - - * functionality.. - - */ - - if (conn == NULL || !conn->eap_tls_prf_set || server_random_first || - - os_strcmp(label, "client EAP encryption") != 0 || - - out_len > sizeof(conn->eap_tls_prf)) - - return -1; - - os_memcpy(out, conn->eap_tls_prf, out_len); - - return 0; - -} - -static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, - - struct tls_connection *conn) - -{ - - DWORD sspi_flags, sspi_flags_out; - - SecBufferDesc outbuf; - - SecBuffer outbufs[1]; - - SECURITY_STATUS status; - - TimeStamp ts_expiry; - - sspi_flags = ISC_REQ_REPLAY_DETECT | - - ISC_REQ_CONFIDENTIALITY | - - ISC_RET_EXTENDED_ERROR | - - ISC_REQ_ALLOCATE_MEMORY | - - ISC_REQ_MANUAL_CRED_VALIDATION; - - wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__); - - outbufs[0].pvBuffer = NULL; - - outbufs[0].BufferType = SECBUFFER_TOKEN; - - outbufs[0].cbBuffer = 0; - - outbuf.cBuffers = 1; - - outbuf.pBuffers = outbufs; - - outbuf.ulVersion = SECBUFFER_VERSION; - -#ifdef UNICODE - - status = global->sspi->InitializeSecurityContextW( - - &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, - - SECURITY_NATIVE_DREP, NULL, 0, &conn->context, - - &outbuf, &sspi_flags_out, &ts_expiry); - -#else /* UNICODE */ - - status = global->sspi->InitializeSecurityContextA( - - &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, - - SECURITY_NATIVE_DREP, NULL, 0, &conn->context, - - &outbuf, &sspi_flags_out, &ts_expiry); - -#endif /* UNICODE */ - - if (status != SEC_I_CONTINUE_NEEDED) { - - wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " - - "failed - 0x%x", - - __func__, (unsigned int) status); - - return NULL; - - } - - if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - - struct wpabuf *buf; - - wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", - - outbufs[0].pvBuffer, outbufs[0].cbBuffer); - - conn->start = 0; - - buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, - - outbufs[0].cbBuffer); - - if (buf == NULL) - - return NULL; - - global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); - - return buf; - - } - - wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello"); - - return NULL; - -} - -#ifndef SECPKG_ATTR_EAP_KEY_BLOCK - -#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b - -typedef struct _SecPkgContext_EapKeyBlock { - - BYTE rgbKeys[128]; - - BYTE rgbIVs[64]; - -} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock; - -#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */ - -static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) - -{ - - SECURITY_STATUS status; - - SecPkgContext_EapKeyBlock kb; - - /* Note: Windows NT and Windows Me/98/95 do not support getting - - * EapKeyBlock */ - - status = global->sspi->QueryContextAttributes( - - &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb); - - if (status != SEC_E_OK) { - - wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes(" - - "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)", - - __func__, (int) status); - - return -1; - - } - - wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys", - - kb.rgbKeys, sizeof(kb.rgbKeys)); - - wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs", - - kb.rgbIVs, sizeof(kb.rgbIVs)); - - os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); - - conn->eap_tls_prf_set = 1; - - return 0; - -} - -struct wpabuf * tls_connection_handshake(void *tls_ctx, - - struct tls_connection *conn, - - const struct wpabuf *in_data, - - struct wpabuf **appl_data) - -{ - - struct tls_global *global = tls_ctx; - - DWORD sspi_flags, sspi_flags_out; - - SecBufferDesc inbuf, outbuf; - - SecBuffer inbufs[2], outbufs[1]; - - SECURITY_STATUS status; - - TimeStamp ts_expiry; - - struct wpabuf *out_buf = NULL; - - if (appl_data) - - *appl_data = NULL; - - if (conn->start) - - return tls_conn_hs_clienthello(global, conn); - - wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", - - (int) wpabuf_len(in_data)); - - sspi_flags = ISC_REQ_REPLAY_DETECT | - - ISC_REQ_CONFIDENTIALITY | - - ISC_RET_EXTENDED_ERROR | - - ISC_REQ_ALLOCATE_MEMORY | - - ISC_REQ_MANUAL_CRED_VALIDATION; - - /* Input buffer for Schannel */ - - inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data); - - inbufs[0].cbBuffer = wpabuf_len(in_data); - - inbufs[0].BufferType = SECBUFFER_TOKEN; - - /* Place for leftover data from Schannel */ - - inbufs[1].pvBuffer = NULL; - - inbufs[1].cbBuffer = 0; - - inbufs[1].BufferType = SECBUFFER_EMPTY; - - inbuf.cBuffers = 2; - - inbuf.pBuffers = inbufs; - - inbuf.ulVersion = SECBUFFER_VERSION; - - /* Output buffer for Schannel */ - - outbufs[0].pvBuffer = NULL; - - outbufs[0].cbBuffer = 0; - - outbufs[0].BufferType = SECBUFFER_TOKEN; - - outbuf.cBuffers = 1; - - outbuf.pBuffers = outbufs; - - outbuf.ulVersion = SECBUFFER_VERSION; - -#ifdef UNICODE - - status = global->sspi->InitializeSecurityContextW( - - &conn->creds, &conn->context, NULL, sspi_flags, 0, - - SECURITY_NATIVE_DREP, &inbuf, 0, NULL, - - &outbuf, &sspi_flags_out, &ts_expiry); - -#else /* UNICODE */ - - status = global->sspi->InitializeSecurityContextA( - - &conn->creds, &conn->context, NULL, sspi_flags, 0, - - SECURITY_NATIVE_DREP, &inbuf, 0, NULL, - - &outbuf, &sspi_flags_out, &ts_expiry); - -#endif /* UNICODE */ - - wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> " - - "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " - - "intype[1]=%d outlen[0]=%d", - - (int) status, (int) inbufs[0].cbBuffer, - - (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, - - (int) inbufs[1].BufferType, - - (int) outbufs[0].cbBuffer); - - if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || - - (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { - - if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - - wpa_hexdump(MSG_MSGDUMP, "SChannel - output", - - outbufs[0].pvBuffer, outbufs[0].cbBuffer); - - out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, - - outbufs[0].cbBuffer); - - global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); - - outbufs[0].pvBuffer = NULL; - - if (out_buf == NULL) - - return NULL; - - } - - } - - switch (status) { - - case SEC_E_INCOMPLETE_MESSAGE: - - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); - - break; - - case SEC_I_CONTINUE_NEEDED: - - wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); - - break; - - case SEC_E_OK: - - /* TODO: verify server certificate chain */ - - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " - - "completed successfully"); - - conn->established = 1; - - tls_get_eap(global, conn); - - /* Need to return something to get final TLS ACK. */ - - if (out_buf == NULL) - - out_buf = wpabuf_alloc(0); - - if (inbufs[1].BufferType == SECBUFFER_EXTRA) { - - wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " - - "application data", - - inbufs[1].pvBuffer, inbufs[1].cbBuffer); - - if (appl_data) { - - *appl_data = wpabuf_alloc_copy( - - outbufs[1].pvBuffer, - - outbufs[1].cbBuffer); - - } - - global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); - - inbufs[1].pvBuffer = NULL; - - } - - break; - - case SEC_I_INCOMPLETE_CREDENTIALS: - - wpa_printf(MSG_DEBUG, - - "Schannel: SEC_I_INCOMPLETE_CREDENTIALS"); - - break; - - case SEC_E_WRONG_PRINCIPAL: - - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL"); - - break; - - case SEC_E_INTERNAL_ERROR: - - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR"); - - break; - - } - - if (FAILED(status)) { - - wpa_printf(MSG_DEBUG, "Schannel: Handshake failed " - - "(out_buf=%p)", out_buf); - - conn->failed++; - - global->sspi->DeleteSecurityContext(&conn->context); - - return out_buf; - - } - - if (inbufs[1].BufferType == SECBUFFER_EXTRA) { - - /* TODO: Can this happen? What to do with this data? */ - - wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data", - - inbufs[1].pvBuffer, inbufs[1].cbBuffer); - - global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); - - inbufs[1].pvBuffer = NULL; - - } - - return out_buf; - -} - -struct wpabuf * tls_connection_server_handshake(void *tls_ctx, - - struct tls_connection *conn, - - const struct wpabuf *in_data, - - struct wpabuf **appl_data) - -{ - - return NULL; - -} - -struct wpabuf * tls_connection_encrypt(void *tls_ctx, - - struct tls_connection *conn, - - const struct wpabuf *in_data) - -{ - - struct tls_global *global = tls_ctx; - - SECURITY_STATUS status; - - SecBufferDesc buf; - - SecBuffer bufs[4]; - - SecPkgContext_StreamSizes sizes; - - int i; - - struct wpabuf *out; - - status = global->sspi->QueryContextAttributes(&conn->context, - - SECPKG_ATTR_STREAM_SIZES, - - &sizes); - - if (status != SEC_E_OK) { - - wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", - - __func__); - - return NULL; - - } - - wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", - - __func__, - - (unsigned int) sizes.cbHeader, - - (unsigned int) sizes.cbTrailer); - - out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + - - sizes.cbTrailer); - - os_memset(&bufs, 0, sizeof(bufs)); - - bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); - - bufs[0].cbBuffer = sizes.cbHeader; - - bufs[0].BufferType = SECBUFFER_STREAM_HEADER; - - bufs[1].pvBuffer = wpabuf_put(out, 0); - - wpabuf_put_buf(out, in_data); - - bufs[1].cbBuffer = wpabuf_len(in_data); - - bufs[1].BufferType = SECBUFFER_DATA; - - bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); - - bufs[2].cbBuffer = sizes.cbTrailer; - - bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; - - buf.ulVersion = SECBUFFER_VERSION; - - buf.cBuffers = 3; - - buf.pBuffers = bufs; - - status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0); - - wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> " - - "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " - - "len[2]=%d type[2]=%d", - - (int) status, - - (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, - - (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, - - (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); - - wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " - - "out_data=%p bufs %p %p %p", - - wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, - - bufs[2].pvBuffer); - - for (i = 0; i < 3; i++) { - - if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY) - - { - - wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs", - - bufs[i].pvBuffer, bufs[i].cbBuffer); - - } - - } - - if (status == SEC_E_OK) { - - wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - - wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data " - - "from EncryptMessage", out); - - return out; - - } - - wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", - - __func__, (int) status); - - wpabuf_free(out); - - return NULL; - -} - -struct wpabuf * tls_connection_decrypt(void *tls_ctx, - - struct tls_connection *conn, - - const struct wpabuf *in_data) - -{ - - struct tls_global *global = tls_ctx; - - SECURITY_STATUS status; - - SecBufferDesc buf; - - SecBuffer bufs[4]; - - int i; - - struct wpabuf *out, *tmp; - - wpa_hexdump_buf(MSG_MSGDUMP, - - "Schannel: Encrypted data to DecryptMessage", in_data); - - os_memset(&bufs, 0, sizeof(bufs)); - - tmp = wpabuf_dup(in_data); - - if (tmp == NULL) - - return NULL; - - bufs[0].pvBuffer = wpabuf_mhead(tmp); - - bufs[0].cbBuffer = wpabuf_len(in_data); - - bufs[0].BufferType = SECBUFFER_DATA; - - bufs[1].BufferType = SECBUFFER_EMPTY; - - bufs[2].BufferType = SECBUFFER_EMPTY; - - bufs[3].BufferType = SECBUFFER_EMPTY; - - buf.ulVersion = SECBUFFER_VERSION; - - buf.cBuffers = 4; - - buf.pBuffers = bufs; - - status = global->sspi->DecryptMessage(&conn->context, &buf, 0, - - NULL); - - wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> " - - "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " - - "len[2]=%d type[2]=%d len[3]=%d type[3]=%d", - - (int) status, - - (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, - - (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, - - (int) bufs[2].cbBuffer, (int) bufs[2].BufferType, - - (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); - - wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " - - "out_data=%p bufs %p %p %p %p", - - wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, - - bufs[2].pvBuffer, bufs[3].pvBuffer); - - switch (status) { - - case SEC_E_INCOMPLETE_MESSAGE: - - wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE", - - __func__); - - break; - - case SEC_E_OK: - - wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - - for (i = 0; i < 4; i++) { - - if (bufs[i].BufferType == SECBUFFER_DATA) - - break; - - } - - if (i == 4) { - - wpa_printf(MSG_DEBUG, "%s: No output data from " - - "DecryptMessage", __func__); - - wpabuf_free(tmp); - - return NULL; - - } - - wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " - - "DecryptMessage", - - bufs[i].pvBuffer, bufs[i].cbBuffer); - - out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer); - - wpabuf_free(tmp); - - return out; - - } - - wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", - - __func__, (int) status); - - wpabuf_free(tmp); - - return NULL; - -} - -int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) - -{ - - return 0; - -} - -int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, - - u8 *ciphers) - -{ - - return -1; - -} - -int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, - - char *buf, size_t buflen) - -{ - - return -1; - -} - -int tls_connection_enable_workaround(void *ssl_ctx, - - struct tls_connection *conn) - -{ - - return 0; - -} - -int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, - - int ext_type, const u8 *data, - - size_t data_len) - -{ - - return -1; - -} - -int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) - -{ - - if (conn == NULL) - - return -1; - - return conn->failed; - -} - -int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) - -{ - - if (conn == NULL) - - return -1; - - return conn->read_alerts; - -} - -int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) - -{ - - if (conn == NULL) - - return -1; - - return conn->write_alerts; - -} - -int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, - - const struct tls_connection_params *params) - -{ - - struct tls_global *global = tls_ctx; - - ALG_ID algs[1]; - - SECURITY_STATUS status; - - TimeStamp ts_expiry; - - if (conn == NULL) - - return -1; - - if (global->my_cert_store == NULL && - - (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == - - NULL) { - - wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", - - __func__, (unsigned int) GetLastError()); - - return -1; - - } - - os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); - - conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - - conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; - - algs[0] = CALG_RSA_KEYX; - - conn->schannel_cred.cSupportedAlgs = 1; - - conn->schannel_cred.palgSupportedAlgs = algs; - - conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; - -#ifdef UNICODE - - status = global->sspi->AcquireCredentialsHandleW( - - NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, - - &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); - -#else /* UNICODE */ - - status = global->sspi->AcquireCredentialsHandleA( - - NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, - - &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); - -#endif /* UNICODE */ - - if (status != SEC_E_OK) { - - wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " - - "0x%x", __func__, (unsigned int) status); - - return -1; - - } - - return 0; - -} - -unsigned int tls_capabilities(void *tls_ctx) - -{ - - return 0; - -} - -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - - int tls_ia) - -{ - - return -1; - -} - -int tls_connection_ia_send_phase_finished(void *tls_ctx, - - struct tls_connection *conn, - - int final, - - u8 *out_data, size_t out_len) - -{ - - return -1; - -} - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - - struct tls_connection *conn) - -{ - - return -1; - -} - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - - struct tls_connection *conn, - - const u8 *key, size_t key_len) - -{ - - return -1; - -} - - diff --git a/plugins/trace/trace_example.c b/plugins/trace/trace_example.c index 25854e48..1ce5d1b8 100644 --- a/plugins/trace/trace_example.c +++ b/plugins/trace/trace_example.c @@ -52,7 +52,6 @@ mysql_declare_client_plugin(TRACE) "LGPL", &trace_init, &trace_deinit, - NULL mysql_end_client_plugin; static char *commands[]= { diff --git a/plugins/trace/trace_example.so b/plugins/trace/trace_example.so index e92d7a54026704c8debef2a6bda56caf9e13e266..c26ca8261e24bde91e467fe4e5d004be123b6773 100755 GIT binary patch literal 43254 zcmeHwdtg-6)$chodF+9iBn%M4OGf1plspKCK|vCdfe9ob=7FH#kjLav@**>VV5?F= z3sNezFKew(tNpBxezdox)cRS_wo+~V_*!exw%62Z1+9u!Ek1L9zx|jw8N#jKz4wp% zhX*oy?X}n1d+oK?UVEQ?_SxsUVC7PmZCmDYTbEjlo%AV8iB357Wep%Gv5Kv9{7$#d zm9(jeq|=~rQxwXCmKBf)mXU_PL$hTRVL$>V%uP}FgE};PE}+SGEKj8$%L_=rgoC;& zCKQUyVmACcqSKSZX`0T2q~%rfVyuO5ISRs_f5Axarpf;JKx+?e9q7A`0(DQXZpxF27i+Y;L1{gm5DGN ze>wQ0{NwSLjlT)_qZzR3Y-5xEXuxN>*gm|Rb@w3d#~elsFE=IC{lE}C3xIV=z77B}68?Ku&)3u*Rb_c3ewT zV0|4S@q@Z@Cv}IuNavF*&*wl-d3tsJrXQ2tKZ5_;kK@x z?r=2L*cS_jt#C(oN6ZShBCx_0^;O}PNMEF_BN~hJ)mN2u_H;+;8=E>KDsAMHaPvST zGidDWxEh4ZH-&3;4rQH<(P$)U#rhhXBPgl0$Lj6t=#I5oCZ}+7V`pbmWAg@06OXoZ zMz?fX(O6%1bMF?bw?Ee0-q^=@XQbOARZlajf_O`RFO#EIYhNT{b*Yq={;u9|Q-5n~ zq|XX>ZHaE|3^#XnM7m?)-p>BEj&9VlxwEmaG1dW9SXHI96{Y1%!uVfP*|4ml+Uo3R zYR-@LTc2EMg|(YdOIT^wU$*@EGY{YhmYJ5%-zFOl0N%-{|pPPh_YJ6c5 zeuu^vC*k*Md`S}i`x+lg!XMH2nk4)&jbELFe@WxlC*j}G`1T}xCI?4cy-E1#8b6SP zzeMA=CgE!|en%3%Rpa+0;jh#9Ta)l#)%bl$_y;upo+SJe8oxgYKdA8slkjhA{GlZL zC=R%|jwIoyYW&e8e4)l~)qdL8p^4v!C~Hb9B8pzcX_)CIlRKJ*AZNxemOU%QB(Rfp^aBiXC{CVXhJf-n22(LJmCV zh32Yp;5k1u*J=mesps_$yw?yTY4OpQY4#iTBe*;XR zZ9f-}D(0aJpGVTr^cV1(Ikf~3DF+n+cp#9 ze}r+W_QXCJe~@vS)`>kb{sYFT;uBkC{9eYX+7rDpekbEp>526+ejDRd<%t>@zlCwC z@I;A>-^4goccKvS(C*YzK!kQLdKp1OXx6FF&KE=5PGxL%t$P%Txa%CHh&R^_4YIzV zKdk5}r=dT5${pJINa*m(i$nI&(0`rkm(1h8CYgU5dvjfA=P!qzC;!$(Uk9JnKQpv# z(RD~h7CT>zc|*Gvxqv^u4k8_I$IrT7ruG71|KrhCUiq0*w_CixmU}kXVlyDz)N_<@ z6B{7EA86z?badT`%aK9m)U6=5POJc&VBb?0+BM~50F=AjY~Mg=S0Q+3K%a9C9l!1) zC(E=wA0-`h*Ewf$uGPY!JMHhL%Xb@!9<1-|LxH33-3k-b)lW_&l#ct9lwzo zA^USeyT;$7*f${fnGK0TJ6%IZX;E_yT?rNMzVI(-+|bUKLpz^4{^W1i=;{9rvb3c>LLfiB zA&&x|!RqTASw4s9f ztZaS>4fuPh(BKU(gmyh#5K?XNMXjgTp{^U+XqiP&~(D0BNP6jHkF13Pp3Md;4k zPEE`_&~KGz9thf*2d;5te(qaf3hf@BkG{9_aH!dPQ^2kheqBxzY;>z-;m_SY;n6m?R% zuJo$X@T0O{-!PPUgNHTVeML%W_tmM#n^cx=SLT5bYFLA?F!Ml(`|w}H;pek3?SVWV2mbioz@7-O zwB6$;Obj6V6(O*p%xNrQ_oAOO*fs7EsncE*j%A9xD zNv?r4ZTs|kwE3gbH+;JI_$b9*O!gBG7(OYZ@5itnNYJLr>_LF?W>L27lCXq!0A&WD z%qmN_@X_+!vjyTQnGgTh)^{iFtP`1^`@O1LHF_2->~M8+%I7NW$A(_o1O7Ddr-MG# zVM9V&2#g_gB1e|BodQ}=)%%$@6hJejhX=*sm$Du!TaUc0a{Dn9dr{@i*N^||U8=1= z6}%;t^h1vwUka2`z-Q1VCmxf1c`NfXnv#Z3#*gX$*3JUv4n#C_s?IPrHVz=Zh zr|v&pT)OR5yQ=xs&<#TvS06n!G{n+ZBuX#Vr5{1ki8{%t?c~$27-abz65x;vCk3p^ zT^EFQ<-#|hUxG3?XXur)l)VJ$yEC_s1-62{;}?~ebXol`LEz;wm&%>59sepas=5Jr z1y^Toe+bp8*tu+VXxBCHo0M&)Ea#d+6`P=9D2!>NRjfqCKKv&nLM`4#_{ui=J89Q% zLB)`_0JJA=f(a$Y3mQ`_TTx}JT6sv-LbtW6u0*B$O7XvrT!V0bvmyL%fhynm8ghLG zShOOTtrK~Y?ZUHZ@;lQ`ERa25(T_lT{1%Gz2MiyxFXG4~$BYH9M+3@#WhC^3ts8}8=b??IA6GO-HCi4TlYv-U>^nY^2WaIj_$UL17$tk-H~SAfCpN8 z`U0``zDQ$BV0ufSyC)Xt?d$1{^mT3tGBTK5V`X)9u&h2@*HBg#tgFjy?}^41O^?p*k4E|!wlv{)mYg0+`l>Z` zmsN(#R#t`U>Z|I*HMPN-(%N7yXadZW*O>~pdN!ysr$=+GwcXa*K5K2vn%Ujc9hs>T z5}9AtP*Fe1YKe44Vo*h*>Z6hgYbvUjX=Ginx;$LDvaGZ+Tv5HWqEbs0#hM0{*0d!S ziH@@J0+GJHo<5aNL-mU4m6unuK2@dFMc8w#xv?5rET2$24JCJ3y!P5HRaK`f5 zl{LdLwZWye!Mc!xuP)S3Uyk}YQT3SO)YVs%)j5zgwJX{9RNG1iTHA&LV+dDNR8~6B z<-sKl%N%hwiX&QIQ5AGV%Y&7rYl7t{tu9zQteTSe)j1JDlgB^A|`E0=}K8>*a| ztS?@D}oO(hw#MX$dprpFPQaNeO%o265}DH-?7RBK-0{ zhK4vYzku*AgynAy4gCq>ytjvjCc_(l8DS;D?a1eLgnY)r%f7nSvIny4N#1m^2l#2g zO~zj@@;yp|jI5;@Im9E?=H^>+?Z09k-IImJfqO{wNV+lWf_6ej2uub$q*rR*FGC8f5KlD>MzSh>gQdd zjGPcSg7gPko8`c5$G6JS0;aQ zeWHO+H1LTAKGDD@8u&y5pJ?C{4Sb@3Pc-m}20qciCmQ%f1ONYM!0e|o`=^p6`-|QW zW%f&%{ZVE=l-d7e_B)yVO=drn*}r7=E1CUCWnf*m(Katr#WcCZ0{Xu3w zklFuZ_WPLqJ!U_T*}r4<>zMsHWW~-rrt!*9ffds6_?`*9Ny6-v zF#8hqcAxnEwMm*^FC)o)wnUkIY+QHZWzthr)HvhQF+= z?BYOf9hUP0l^xyv1G572@{9B51d0mh%q?6{xFB$4TXS>Xyl`<|Q(t3ub9G=w@-<$d>j z)3e6w@=X89(VNUnJc(Vd^=f(Js>;tVh;E6tM0%qI1I6>h^XC;b_r*H$>#TzIo~}qi zV^e=eXG=j}Z(z;v;ppNWTnw{oS^>5)5uv`V4H;bEl-QlE}1m5}EpE z)G{R{6P7~0U_#)x=Zzd>iMW;{wfN+-B1b>nGA>kY`1x%?Q5e|qNh?zP> zIMtY$wk+x$^CAn)vF|}{+dS9fH+8ao5Wx=59n4_5{b`Wx@sy#zrRLka5xLc~ zoRI<<+2^^8kwO`{$FrJ|IWn@}6K15y&I8Xu&uwE6nI~mC?4=BM-bu80mu>i+CZMydG_e7=G(iIxO}k*7y*C0NmZC`PlLk8$*ryQjNgEQW zdwtTfDk+M16$u&cl$1=gvs2NF6KOjn?6#+#qWW^e=}sa{I8*cwtpY$@AA)K0seQOmlXdUv`&8nwwva^ z8I_Mbx zH^A%n7on7~{z;%8=f4D$#p3gj#IxifRI+7;C5@=7}WG0mw2fG z^&5+g3YIud&=TK-TEz*vB_ZA*;$^~F;t}E{oFm?b(iI#S+uCA@S3ZD*xnck^i@z63 znR8(O3N99Yq>5LGFA;x&Knf0tDB422MtqG3qK7DWwRjSm7q3?Vzh3+z4!4MhfDtE& zZx^$vYzcRY(d3nIulN*vk9dQ6i-{%Bk9e~Y;tzK%0;!~t~m-6fpM_8}-nE#i>MAE!Z`mc!d z2!BBMtKt#Zt@x-7yk8rC1sX#P4G?x^SF@6q@-{B?HujA)!OYSGvzHx}v}fIj*pRId zJAjb=In~Ok#`A4q+cd!mzeY=?2(R6MZsk84GFtu_XfWHq8ow_8<3PCm8=$ik|1$Kg zRR3{srTJ&EkNW%ZEBp`ARQv})Gs>R{Lm%yb26(T3GAMoitDx5m|1Z&cnf^Jjqb&dJ zz`tYr>tO)@wEeTte@@x{9ccDop?Xl}6Dg z6HL#8azmqM&P4|q+udi4o<)tKj`$gUKH(Ix5REkY0+uR7Ix07MHsP$XEghiGBb+0) zQw{l~4~TCd_t6E!=ZgLK9bHI#p?C;ojGjYyo>)(sq9wo-3m;l!^xU<8ORR}ZCVCP9 z$V$BD3@&(B*i){uwHY*6qEGpc+xU-?)%yRomNHLpJmZ;9U118}By>rittEn?4Px zHQsL>CBr#X@|`6BEpd#^_iak4dW`q3-N0vwezw)!XF;$W%XJ%b`_7k;Fs1_IWM1T6fGsFuh7AX;ve z|0(ph(f)<#2wwjT>;#>e^JL*e87 zw}B?x{|Ls>v;0#bRF3}$WS-z(39gC$&msRw{(j^(*}ockpW=TUT0YxP7ckZTcVGhk ze*^D1{wwf%uK#J^&-0hzcbfkp$p3o2zXZjc%=do=vLBo8{}qaOX1;$KqFLz&dZUSMp0ElPCx??X-9{_`M1ivN9BTdKbwnosjzhjP=c^!FgjDevDW zaC{uXkM~tJgCz$s1*gctO~FF+(g0s0jaTp%Q^8r{W)$pwoz0&ksz`H^ogg6oPD%;q zikIT}LU9l3pzy`AFG-RT@eGabjk6(0NOaIb-efj4;=AlW5?(FV&{`zCUfc-3tKfFA zkXinN^u1y<@oy0x5E1r`w+U|*-=%%OLwJYCflj>tB)mtw#nwMX_*T(E{JX5mK5;AI z_c%u0Bgb8@96P@&Hp2!Ke6Jj*y>cAAPma?HzF&^b3VuM2%?kdW_=vn8@>XoWXr`R< z4(6b^g!LL?S%*9Wbaa-@$YIa35XI}V%b=Abo-QigZ8sru)N`CFO0lDe9P|7X#NJey zGU(Yy!PDfpecU5^d%Ars$WB_5e}-mA6KwH`O~`o>fJyR4I{4i{A(=s%m}ZDroE*j` zQAcKcr~q~f3ztctZwEYi5hP3#TL7ND6E=A&#XHLWGwL(7I})?Jqis$Otn61aA)r?Z zc#kVvnv750aP4>fIuF*d!9<<8zgqw?GIQ^oG=pV17* z%2t#f8J;$lI4XzA0ltNDqwcq7B50PTeGi=aqS1}?bp0BY} z_s9%Fo*;a&w^ydrc&1T;jWV*@^C(mLTvgy%@A)<}h`OetoOaJ=nL*6ehLm2<3JeF{ ze%AmZ1E}M1RLhrwpEN;Fk$Dqp=ACVC2md)o(abx%`8H>%p1(8u0%@t1M;cY3G!@|~ zgYvv8;`PXmFh_QTEDvuNy+x8g$HP@9SN?r>mgqzAL41=6s#dids+_hK3OEGojuL7MmnDwUbR zX1T;30_?gJnfc~E1I)NEz}0Tw{fp6_q70h%eNT2)c?0VEcNS4BUVzp4_OmmV$jOoK z`-DSsZteTQVX)T7X@~EBu-mT|REqD1vV)3yVd=gf5pEHC@asFk`<-?XWm!L7fM)3x z7ojux4iX*^(@{6yPbm0SaRuogq@Iq7tI=A%pR#3+iOrD8_YmP{#JzAWzK28L8k9F` zzC$eQl)MS^9j*iZ19=nXdxSSuAG*dD!|nKfPG+}z{5R2-zF)95!ae>c%=VYG^(^=J z8KCwZAz7|_Jbk|J@!cRRbdOJk8T+0X2WgAlRdg6aT*nr?oE6TJx4gbJ#OH`h&=$Tccv~D09$L|r z&mfyzkxD(TeGhP zX(6qgVy_oax#eqPYqW@B%GOReBIYB(*Fm^d{GCev6yY{GCHHNh{O#71-$0Z}@`r1w z&@%_kH1TWHH%(9tQ{Ey46DQGtCozg7Rx?dJhluOX`0;g;BmL7*ZjP^;7AH7R_~hMz zB{-n?cpDW6#tIy{HUnrI_ zpWVb4i?d1p1;QobB9!a9nQ%zl2XE*5Vio9Xgxd%BOPzpMi}%?sUnb3Z5nw)F`8@FL z;t}$GbwA)<@l~kHcPnKW5LL8?uM@^H47hx^{Q~d~v7L(f2H`#81H!ixzEymOUgC~t z0q+yzDDz&z_lOfz@IJ!(MJZ|SBz#bG;Me!<*YSHutfEccMfiw#60Pt1P6nFjsCbn! z{~O_B)F9+5% z!3V!`0!$|^oto)Hs!BBUi5yHgVVFc9u>#_NWHsc>xmZ;r2=XbMS4bWdbPZrx5a$;v zqqz`M<&g)bNtD`5p}%0LIp;80JVYc%a?U+e7P8l~aFp<0hKI8el`O^Lpgc)>N|*y_ zSP&;Eljx$rI1aExa-{9U83~hQty#n*;*bd+|D*{roJaJd0Hy(P5Messk*mq5;nKHU!^o&5i6wrA z&XrNif>Px2O-3!7L5O&$aU+CdKMdkD4b@c??84rnZ6q+3g<_e8M`TpT&2#;Ve6j8Re;xv$C*zP>D$Eu%wRmtgnO+qx1s_mo(X&);C~7UmVXz1ZU4*A zB}Vz#P=1PM2A?DNTcAeEZ^IMX{#+O#S57lwd!yNMd|IH*B2CmoHIx5@oYTa8z^4i7 zHci|Px+#1%;IL-d$@oLdcm!!v_`qNiwXS-QB%o;_{jW$${~OvY;8|V?oybKAPbEn# zx%l9zBCHlPJk^vdOFi*e$qJ5BPcv%R?XmXM57Y5n=0-lUx3J+XLdw z!PV-FjY`R~OG;RGNeSyNxzW)jC0=*QjgBt4QFcjddb@q)mZ>O|H{14F>2bv`A&k9_ z0tor6#=fcwuvdHojb?{Qo+VDe@a^@)2gCvhX3Mo(tk*VKwp_X`6hSn#EthVK#VV4w z+zj#(5hg5`ZbO3W26ii(szxq^+HKUzYOxW&b~{B_k5zdDJ5j2%aociT>_TD(bv5G) z=v(&7_K%S|lgktK%Mx6|e)KZc4F!{RMin}+{j~i`obYK$$T_b4H07Gfg?0OBmSyF> ziSA}U!Q#!s{iVd?vTqLsENhkxRq$Fl%d##ASl0P=gU8KDk-RRLp_75O3(vRfJ?=pW zr>@+0U|aSNnYr1*ag|Q8Zx5V>tzXp9kNS*97Nh|Ab29yI`}HYe8Be=dpm4(+FgQ&2Q5*r*!(9;Q|-%c|WhM$O9E} zMe&Oot=1WwZ(ruxgyh0%3ij3>P5p}G0p|AU=R!=`IB!wsw?MAhvVq=W1HFOhoDR#D zyA;lMLFPwv4%F@E&e6L4z=&IPOecRE$+KS5k%thuU(rH7Yu0$JZszfX$zwEhBd@tt zj^vk3^1UW`)>V#V52Tl^n`)8^HaU`Ik}yeD?nd^79@cZKrb;yKwN!-cXg%y`e59Wz z^|F!l?;~393(fRf$W2{~cIeuk0|Wjl+j*OPKk}F<^Vr5b=3Px&xq z2K>7HpQM&WyiSstQthwHesSS_-RVVYwA?c39GEo=Tam|2Q^;D0$pU-BHPAgr7@$@1oVwz1Ek1#HUNS7V;6^t*%B}h0l)3O%yTJ}V_ zCbbM*xwseTVvVkp==2kryrW#)gmW{Kzjvv~fOdE92h=J|H5v#^B$xGG-xDzLtbQZC~1 zi~WGqX9n7~pjs2fvwk9OgR|~-RO<+sWz`Ov+RXZ?PJR`&e#sW$nZ(z-Hl@-}AhlqePG$biysbt50llr( zse`Dzz1DsT?3ers__e6?CC>r4j=)m@?gUWOrOBl{e7ePp^86$rPXQ;Hyp#iT$YL#G zm;9Da&azQtv;FBADBvdGA436`61n&*h<%9I;(Y+FfXkWnvgV=s=YT<~`22Y5*0ZKX z9*m>BC|uE5&d79UWUeDpaET*A?a;GYvy6x=?lx20_;{0DrkR-h6O(*tJbAMt`It$* zF`oQINAf>Sa$P+6-yF%4(Whipdvr3qlWf!{H4$w}5?pKet%a-MN_g#1f%P?HQX+eF zf$Y(<{#_H~Lr)u339R?5pK3gv`_2Tu=vfE7Rg|e?9A*l(I!s^6vBQ|XRchj3tNn9IIk%0TO*P0KgXEY^+R82tG9Ycxxt>RmP+YE6JaWk|v|w$D$0O zgk~Azq(G@gAZ#2qRVqfUvmF8?Yuvorj&5f6+q?wl}#1gq2yWuQ~wm_Gw@fS})napSP=rvN+c=$g@X0UKH^x;n@ z&mt1lA_GHoDRaZPK2Ot@Br?VM8Hy1;KQ1d;W0M0D{4 zTGTQpf)t^6InYVP*oCq|E8<8LZjj3p$nJ~;dPN+K3>15%6Ge9E4w4Q7CI&`_hP%Ue^G%8`^0Ua&1*L9|FmpZ3UE8&^5CiQ z|FkmHuKYG0u2ZnNr*A_+S7TpCV@p$kIwHHEts_>z1GeVo7Z#{vcA^FH1aGULKiXG- zef!wqkGC82X8*h>j?>EPh4SxjC=9*@8Nwu@>xi%WtQWVq1D6QGn1I&>cMhF>;`)23*K@7h-uG-Ir4P zU0cF!ojpyBouGqf(I<@ebm^!(pv)wNJ8_!2j)a?8kV(O@9<$AHn{noW2RuCXTerK5 zj<}2Oxac?T`>qpDe=aC)ah*G^^5RR^T(V;Cwo4JQ@BHn-<# zckUe(uJgyapSRu=EADb1x>oqzx7)6n_O^cas5{)AJ$v_k(f!M7A<2uDE7umAHeGzF zJ6qi0I&UmQwcU9~+#PqgC&b)sG54g{wu%)G>~;Gd+!hu6?t7Wnc6+(|gKJ}Ju0WFe zMqA{&?;`rocFg_lYZqR++nuw=y^ z?g6n{tXRV$#d!B^*FNZe(SE`G^0iN&8`}Q8pH{58s$xx^?4aw=3jeJR&pC_ z_e^_*3#Y@gx0{!A+!ttEq`SXMopayaB29@$)yE>1JPj_qsj;&^0&CGH;mZT<<1uW2 zi0WgxaBx?+dS&$z95c7VisCT;a100aNBcWt;pYCnD9)85k``>?`2EICp6K6!Zv&K7 ztPEEL>q9Hc^|@;nj-_h}%RUv3^@MTS-X~9EiaRu8&R;97udfZ` zn71GfP*XJvcQ$sn^*6RftbV*vG`Gl-hpb6D93dzVJ`2~byxbg0M`4F$R$5-J%P9{o zEp4c*50^I7hb-Bsx`gJQuC7LWc7V;MymitC4RG#WYXl!jFmgjdVQ9B4){aj%tPCq9 zH#J6K6SNijciE8jwWVc2bv9m0lQNyQNKEGv?ux`3rMV>3j8pRJSC*};w3_*(2;WiY zz&977Ru~Pn!P$EC71e9PFr_8<S@mN2zCoYdD)7nTPEqPeQ;EmJz}BaE*&gq2~I zu7*j5)xm+ZiaHDUaC1*TUfqf}pRAW^KU%2e{QN`W#5tpL9-tS71$J~J=jo&8<~3gC%(OG@i-YF}*yY;Ty|RKW`BOzZQ6ys}VfEyMDP z+TjMSa%2NE#^BtW`q76sx8sW(;U-J9rtZwW_^`(27O1u-)&u#&=wE!fP!}stV${bz zI@@GTu(TFE8VWYuts@FucWjDS094_o9a=m4qwV4TZuJ$7NDG>Jm_@-1H?Sk(L{4KZ zEja8_Is=^OSRUrVh>5cbWsj~7US1w7tEeii zDyGRs%BvxMN~0{!be(0jD;vyNiqfH#R^d3tnu;28b|H~?Z8MD0(ACh8uz^Upu^&A} zi6TWUU)g{&7^S0PQ}ca=ge95dDVu2YVf3fC^(sS$&!b+5mD-laXljQ-qviRX4kuC8 zP+PaMHq3IRV~g~euG`uX>1>H=&nf$`mO^?Mo+YWyRUCdKrQ;ArBZ2Hp%87>~1DKE) zpD#1IytD${b(vHP1gWcESsS#d5xD&R?r29_ccdjipUq}#?rz1`Uf}kXK6Fp64z8}( zDh=1*C`U>}JMP6-R^UZYrMNHAF3p%MrlH|(&^WbUS6|Bv)VxY+PmO28Mgx9^<^Omt zt7f#&MWg(HOS3LLSS^p^;N+t+F&ye(T$sEyj2n{lJNKzY^0Ch$$`OGY-QOpaNr#nd!n8ATu)*i zfd*~wfpsQyQybJBR>~wzzc=RWIEk}fTYJMjt*z)c#5IT`O(qaMdB zOHYnlI~&_f!SvXUgJt_7u|62793Y~x9&~Nf@s!7kay;Sy)58x4ZEBR$^Kh)O4M+P9 zz+CB*!g3VBM8oiQL5VQ`gox5dEUjEwT5oNLM0%AE)!#OfezYsvrgDjNH}{|p$Xo+w}4hM(eI#fwBV_ScWuNTQfi)9}0bQKL%YScAS=scx{q)(NU)%1$e zDRMYvBbkPmE(e26YfScE+@q+jmuMPku=;Fd3txCLE}Xq%S%dW9>fGhTDX^*+$?;Cz zh++1qKKrDyl`>US*B8yx;!1>^J^UZ{a2mWCGaYJOQh`%u<8xo>q-oY0B3p2?*Bt3a zKM70YDsl?KnW)mLYQ&yio?D$TaykEyqc*-c)f|gpMC4RQp74yRZQ|=!{oNe{X57Qr z3JYr~qBsQ59lPS6qB5RlX^o{#Qdfne;^nm!I563ai7b~>I$5N$cUT!v-NbIbC3UPK#r(<&@UU&cK0VzU2>3 z9Kg54xR!vgkl_Yie%_2~jar!F!!_YEW4#;DqE5G|stqEX#jOjYOlEc@?=(~`6TOtj zDJNp`(CU_+e)TCgb3ed=B0)3aQPNWN!Rm&pgzspmu2@}%TSUAV+ZXA=G#Z-Z$JXQk zA#W>$hB0c>~)LNZU&Q2sfX1KdYjo;2YF6p_o zW8?@6N|05RhUFYp9xTpj9q+l+t%&;8936=j>xtn`Sq>;HM0HI)Jn`03POFqS+JHEn zU1PygB7Lp=Zl0MaDSt5svkvJl3Jnm)6dBB11I#nPg$9^!!MghzRX1$G_=x*BImU+} zTFV?X41yvR%vHfW6wywuOofz--Tt?t5HO4O?8n>00EhN&QDATpg7YoB(Jy;7(RDJj%`SND4vI6(N z;R{UY(k@#an zTpNvxW=@F@m#?gL&V@#}ouqSzg5oFYvm;8yX~zUR;p=*II{m%0Q3$Q>WYw~Vl2mdm z!4#Ti72y!t6LUE0_^l7Uwz*9)?Juhxg$KkukNsGhm_v1^f1LnxkN8>tVUCGdUOWBM zla4I-4T_{Q`RPJ7t50a?Ij*AD?J5UNHmtniz-U6JsrBm|G0fb-Sy=qj3CgT^*BiHp zU=C?0ea-EQaP(2${CR=Awm{y>qCj42 zO+{H^YB6k4KP(sBEG3d08DOjfusn_}M(5H(=Fg*z$O*ojxk6!bIK)DWyhAgM4@1!t z@^bkdR9LemowB;+cdpXFRbW8sX)|_g1}@G8F#ABK;WRDYC*?AoW4QzmA7Y&rdz8bh zE7vO$x3M%o<`b-15-cyr&8eQf@FoG(ZrOrSsG}L(sIPHLLT`}B zlFc4qAC#+JJ$)_m&ZHgdp}4`03}68V%Vg{sW>rh6MfPqvbxN3d(&st)d!eNWY<9q? z!c%hP`bRqWyc>uk#(V!7zQb_(gLV0P8={8bl`!2wkoqCuJ3PYT+(1^UB-Af z_D67sj+RZVuh(I$Q|lEgYq4rv3OiBtmZp!-@Stqz)6jJqs>|T2U^OyhuGi~H03S7= zA{K7e@x%kl)TUt^WvTkguNj_});PaJL+tSTD!ItTX`YlpHY)ActdGfs%3$JV5;GEg zZ?9_>ZN|zMCkz~S9GyjVrmB@N3`ZAG3-qjwT%T7SRz5+|tQ_&o!nB-mMsY)jK$hUs5i`8p0F`k)scpcY8;E@^bgA>{eAMY&|H^byJ?By>o>AHaRrwP z6|%P_?rY80nvM52)3U^~2K{(Pts(U`#ur;zE%k++e#cDMv=J8C^ty?9cm%&4qo-7D zf!L?4F+tL&tIvWwFd0cDAgh+ z_KgH#JYNl%?2pE?uQ#CKQi@Jyd|o47)1k{M+-u-d@yu4mK`nyY+GOud(~-isT&qqO zbyaeS`!w}$;#>^|d?${oGvIp~r8Hq;A0-Hrv;TLU#6xCy!IBL4bR1Rlq6XhipiJH$ zBv2{`$1nD8@S3JWbSne;^E=n8iH>OAt}dDR>6qd01v0mtd=RU?{M zUcAtH1Kx_=8oX?4odJ0?7%#=LG#R?B z*wHk;Im_!39nQ9T?JwJ6XkziSUY7()rk&gCmQVW^FU#&~h73}`GGmYlNJMDEaZK(60wB8L*l zmc1tO^8~VeuZavMiZS3Taa4@~-;T5P+Ivmp-8fx?sVYyB^Qa;}NXyUKt@vr?l*xX);JCXps$d>te<;N4C-6EWTo%&B`#L}(`>b2Jg- z{v@S|7zZdRO~j~3Qksa-R~kLod8vC%+&CXeY$C?@7-ADQrYVV4(Ze&mE zXMjGcdd^;>H-7P#*O}-{`oo$oCpo`O+C}U&6hGIL6Ak62xMPXa4r-d${zd5~{q28Q zx}o?WUiv2E*i6mir5l4sv{$0*(O8q+5*Eim9m|3V;>ZtHH8lTfm05N(Qv!t}9;3CcusXj;>| zw#G;Jbq0JO-e0e=_nOEP3A*IIhPov8D4u&QYCbz0e73u#VCFR zp-v|`P20&NBf2=#rlvvBYRN8hgoZR+f6{_dB?kUS+)7p%Fp+uq^vuJ_O=A4}3yF*j zXfm{|8UrTsK5f=H!u;|a?a`*|5c6CEn%NwbZ9p^ALUGALnsLLGu2L{}zk|+CiH3aI8p1>=y6K?P0OSl@}pb2ur6)oV}VwM?FPb^ z)g$?B-TnD|NZFEy)hiimZ;ZBE`B;DlrwXy=CsWLmXJ;ggw7y7ZBN;T*i+$4h@`K}kts{Cp!-Uh4a@6J-#})yiNg4i=n%{&EX-dO~Hwxut;`xZZgKx+f ze)HWN6B=)2=ncQl#wrGYmjL7CoBnFTeL7A=CO`F@%s&7eNwgcmTb+pT(jnfH9d;Qx3^EG9$&WWD z064E*WgD*hd;L$eiA zGJSIV0LU^O{6}U7B;%2f$^QtbDWWO=sOCTVG5H@$;x})=4{H8XQkuk1AOT9~Dg4rQ zP5I_KQOEKW;h@%^X+~3jV@H1mnN$9Q1p&!;kgrkVDv`g5AH#3L=aJ;(xAfccI9Xp_ z>|f5y@R{%xBs%%GYW}U7zun0UIFJ;7199eM>Tk_a6vxj{zDfmyzTQE|@-?|ZTZVs0 z9HRiz@-iVOXa<%E`loP?0`@zRfU}a~8DJiXe^B#}xnMX$GVR1O@V~KE@y$)@aLN3+ zXW*aKsQ7FD1^+zFpR7N;`XaCI=>Du?iMkp&7N4Q~$)8mm|CyvP!)Nkaeg^)NUsM!( zk{FV*v&MidZaXGZgj>I)BCC`5lcKWiTn;`1=G>v6BYHfWhUfzP8M`;*7ya`{Jlzv1}I__#J4 zpJkcxYB+vOd^{SC_s74-I2=FLGULZ^{5Z>u3&Zi_E#vQpr8Z8c+Y;$Nc73GBE{vlk~+K2%x&Q)nGr6xHDv@W?dt3^;HRDeA2b?gYRG_ao(h82Bvo$7DI_$H%~a0=hre=&YWV`JC>@2G5ZH zTW7!v3}Pe6bM_hVd=NQOy-LqO-vIm=)N7yCe@M%}8Tc&JYaq$bd`9NuwFa{kp;=Gb zrRkIP^EHXrnK}XbhCLeY6UEl)_Is^U(@YJ(8;K_{FM>O6X6lL(wo*|#NfIrt{ z>Ikzij{2<;#xER45{+R;TRvZ(TV7TxH_?T|*pnOIgCE2D<=AA8^_LM);c!cjK0G7L z_Yd#_a{qvQSECcJiMHg=pF5wgAB;eR4SC%iYLN&p?BgvzY<1pY9GLji?p)Cq_FK?4XHtp=+U zpC?;ENn4ytItu#qNeX2`3p{*SMhX6&SjZT|U|v{XB%{`D9U49l(B#{%`SxqRY{-n6 zu0x@s7PH~s6B=I_&eL=zBrTuL=h894w+9(JI*ci_WkSPu5%?(Q-}@LjyL5T)rogu*FsH=u$tb zMqBR!aA4if&~WgqQGxYmgv7Ve*zpvzW8gVU=Tj)pCy_>Zk~)9WkEt`~81_RObzoFI z>{PDjLBPyM6y#$?B12<|@klBiok&L_R%B#+ByB|o5m=Fyp0-FVJ`o=pNu}cxJ#EdS ziSc+(w0|_N(vF=H8JLVRgXrkU$3S?&`bd|~p?Nf#O2t!FTT@p{(~9L0{O@e-UDeWV znVcd6(b3WV=)gKFGPW^w#b{(;bR<5WjwDAjLnGr5W?(cr5lxRkVkw>51`y>;eG!9a0Rcp)g=E&mO z#kFUtM(|ktXUR_k|DMKI7vLY)_+SD4X^pQbz`vmJ^#yn{$Tbw;9XH_?L#`hKAGa5f!fd90{CkybmYy4yZ{u>&!DI6 zHM;P|M=&Am!gDTYo=z9ut2u*ZM>Wr|3-6wzCS7>9e!`ny5aDgb-#;m6 zg|FL_Ex@&i2a_`XCB~`JgMBjo zImW5VgPk&d6XR6j!A2SX6ysFg!Ft5Q+l$3K)OGu^w@waPz2Q?2hqpc#-h8-hgXi)` zp@_TZDn-0<`H}ssFX;abgrI-YMgQ<&Z+Pn?;fMcne%Rg{{42 z{`ko6$-imYZQ!#q3&NY1eG+g2E$vgJOIwu zgCO1p`q1I$=Y+R~4l{C2=s>1~k>>{#s&O?5!ke!=Y-OJP0o(M4Z0Q3-??G|dl+ycH z(DQYQr}sd3YXwU9j4S~VGT7R=8kkQ=OfRAm`;$jm^{h;fhYm9v(5;bllTF zq{S=0?oow6AwNc)JJIE3=?5Nr7a~#6w1Xl+&mvXP&$}?i??^3C0MIlx(8hC-t&bl# zgZZe$dUs-d7>VV%#2R;E4HI8^C(GIBPHbf2uS_D~pJZXkr;4JU4?+AxR{3=SG+0(Q zC>HItHM4(f@4tt)w(SjXKPw4$`9rGeKn2CgHp7YGZN2-E-vQ7Jf`-Ljg`)1WLHm3y zQc+rQk*R<2TPG?L*fwJyp{>9EAqkJ{mHJcyJpW;EJZb1aL!FLB>D9ZSqYe{+YVqHnM2{NFd}J_xdwvlh@eW_-j-^H zQXW5Wgz(l!B<>x85Fd%W$PXk^a!|AO|Q=u)Qt`abG~vPtMW@!eBZW45xzCNefp1- z%*D^nhP7;)?qQmAAvB6lX0tj6Sv?+lfy4*D$XZIW&W3HF7m$s;7n(kIN~RJ#YqLD1 z;CcRvV^IbqGEu{Y<{ffvaDhEZN2g#*!6DM z^6$?VP?O_ z6SirSq5ZVnd-tHxLGg%Pey>x0?^6%|UfApvWb!D2hxb=xQC@?pw)esiC<5IEGXFJY zr$<4Tc)U@d&PNWGVeCbm(!gaZx^KDYU>}ky!Q==soD^>WF5SK1*kUMr`l+~WDf1iHqXAJn+GU& z^X%OUhp;)ckIgFiez2EiZ0!`p>iWVKW|n{QL$Cn4nCaKCczD1Oh_({XER;;8nCDU6M5L|6D>g7ln2i(B5nth7X>ha)eTK zr=5psq*B;*uPcSgq{k2Rqm>T6OVMQ;X!0pp@{2)!@V6@czoNqbM8Ivj#BGgzn-ANW zOI7Kejd@bshW@C8-uoJS7<^K?8j6%-S1#=yq&@G4m;Efr4_uEJGkLtR5AAL}KcA6p zXgGKEQrV0*klEgGu=0OkBoORK`OSN_gnl8*qLYAOJX~lP524VkVSF1AX&B`{=#^|+ zf6c!5HeDVieY`OY>1Ct9I}tpXM27j)qo6*xMmM!|PTtHA*x63!zMWaioS+8~u+QzY zp*svrDCVDDMKM`(X(XPODLWyW02ZpO^Hk8~7x^fQ{1Y3XJ5^hw+`h=L%IgT)q!P*?VyC)dLQCcX7eK+%mt{WU6|GY&P&JGay>kjw!tmwF;-HqyL>Otl$&D}0!XIDpasJlDT(!R38g>LR>Zx1!Q1u=vd zwzRryy&|-{ca@8=v!&e~?P+NXxl&hzTAS8{R-m-*P?tOR=5SN{s!*gCXczhNmiE?; zRgo3FZEnT(G%at1M)Hwb;7CWWJKL_%D%3dCg~n;R*eytRPg_r z+jA7UJJgQW>1c;MO+8I6DP>;GtsUJ*v7k*tZgCKcbaeK#bmTV+L|GZ?X?8c?il$Io z9u*3=V)4;!;pM`OYHu_@MAG%_%fh#bA^Z#7597o>xDdk_o0A5m~HGd{pq z0PN?NL?_0P`Psqd#Q1nzqJx8piCkg9@kBb9oJb_&6Qdi01EYyl90JU*UpyJK66>t^ z#6)7kn%@vRJ2*du|434B&HUJ@d2ws)xV3h|TAQ{Me!+NRJidT3@R3}mIlFtCX`VI1 ziBx*o{L~qlRD6PAtRJ7J>LPM|kbUkbBu!EybsIJ}wbI~QT0=F+C1|DaVg%X@_HRtb zA@{eqah{jn~~o~*EE`AfwP;O8MvxfPCb z_X4)cDprhTGNS^ke_`R@Qaiu84P>4Vwy$AG^Z>G8buzHIs*fqxn4 z@*QV3|DJ4m5#%}zdW+`qug#{a(B20qZh2O9W510QJM0}Xtjfxkxs=3JRM zM`n09CFjFAJRiJ3md{EZ$GbQ6;4LQ~9c0fZ9H0Kn}xkPgg(VROp=M2@R0M77g zLpSFJ%{f7HF3_9puZ&b6&-ZazC$(~YgsVC%?uX7beFnY9hIi-L>m7cHs(aQ%mamkkXJ zEIc#Pu&{q3IzBKQoQSU_4`_5YI79Le z<5ThP(+=umS;v$mm4GVmGHg32`Wd=B<40C&2W8^<+?M>FhL$&LI<>WRsg0>vJejJS zY*-pudS>0gM0%vQ+o~H*jK%As{h5)`SlvW&OaaS#TUuAt4e`|R!o{_V8tMj88Gh#? z*wh`wiQ*--i!FQ8ODFTY6g=@R>@PtWyA=*$2Aob|1!Exbbo1kv4fynm(#tCm*y3Ao zgrPE&)S_&=Zn&-Y2NnOiiTNhsfYEkWc))fS5q z@mWUgl7B`9o)~&nQT?63l)eYq_7%Ur06G!p!PynR(GRpG{si?FzkMS?u@ug)_#MI( zVmGQ#{4U{YF&#|B?-34)ze3F7BZO;wQM6&PWiwLmyR#e-TSgju`ypAeM@AZbFOt1T zM#8>l;i!v?Wu()`!~ex4GP2s2qEy1Z9n^ik8c1F|#r_f^!@gH2m*4J3IZ5C36s$r< z(!O89dWs!8iIhoS9eJwkm56Ne&BOpue4O2b$Thydu+VDzF66e^w-KMkC)oEP*y7vA z4CdSa0J0svVfdfoT6;SpxB3!{)XB&$-+D&sW#lg3m5eNsk=?#)8Ch&E1kZiGZ%#wx zOexzQ-(M-{Qu_xe=P|4FdepAE#EKy%o`c>>tTwP%q7--q7l{V2msl)bi0jeJ3a+rG zbg_{>S2~E!Rb9M`8WM48_40&i?BG z5KB=c_U8s$3G6Qs2}m0f#m@w!Wv!+tf~{EQEh;(_?d%mrm!b~kUxPrUU9!Fag0}Nr)ZgP= z%mSPc^it%!%8EN{VY?;Hlb|ehZiXB{1}q>mOq4?8^y;Ur>}W8@L};Jbmd!dFW}C~!KCF2Jp^#I7(qpSD}M{PPdtx` z`c}OQc-Wdg8#TSlBmUfr`c>jBkR=Wfw8S}3t2lTWU?E;6;xB|NL?7`It`?txnJG9} zIT*9V^Y5TWHR5i_EM91kGIzoL72F_vNEI&<-za_offO7Tx1ue?Uy1J&H$#UCUM=2$ z=EX~G!1sw4vv5p21dKRD{IEEg%9ik`xRksSPKt80xOka*ON;#!`IRWdpA>b(ze>a0 zBz^-M6R*)2KOttJy~OJaAk{TuUKZXg&L+(p#9u2G68|RQ>%>yRhpCb+;zrW^jq>ae zKV-e$V*WRa^GWkI=|3yNgx?{2i+CJ%E8ZId@Bf-{Ga5r2nI!C~+QLd&%G-F*+t@cs z1T!lU%wBd>(jIp`Vn=L+*aC#?&&5`CJKhxv+olN~|4~%CNcinuj73f}WVD>0!dPwR z4Saf>CxP%f4?t%{&Uxrt#m?`*RpJb=k2>GRr*K}QsW`8IW{PtUG&j|$gou8p5|jbw z9TZUJJcHINcb379DxA*%|Ay^sfdRZ}I}gJy58KYwX!gI^&Qj#{mhIdCgLvC^iV?hH zJDX9=yS7se5#O_&zzo*P*$akEtpAM=u|&`?N*{rxS5G5;knfb41T8TL76rph zq2npDwgXMC7e%_Km!h9QuyhELho>%Bf(}wSK4DEgl^R7I@iFx@!bKvA45psWQib>m zDmV2E!WEUV5zsFrTrEzeqp2l*Q2YwHPpu=qM(oDt)OzCU#Wdm<5k6CVoABc0D78U+ zqZ;rM9uR7@W-^)RSp*<21(Qgo&!Rh)A4mxUQTiqnR_#CkGk|5K{fz`Iu?=eVH!UP6 zY}ku`IbkZGAL*VUeE6FQmbRja8vG#&C%%r_`BxIeK!h6kS4p(^I57S&;R^BZ(1yQ- zxmAlxp=SRDgo9!n#^S%Q4RDQ^OMENgGlZY{vFZ$W{`Naq`lfO;s(wGLiE^}!|09HjxD%%2j}Wd9Unbl~xVkddZ~3DHgCYnO`uhpjRL0_imVbb7 zy`Z1*$7m`I;*YRYf1GfmNTJgHAqp543BtpKTg5hJKSH=ue3I}-3HOK&=Ch9QYVmKZ z+$iC-;xbsae~fUSIG;*N5RQo?+R2|JJS;8`0KS6osPyOliJQ?ilY-sapV|)mq~*Do z?UgHKO=Ge-_cIwzso}Z0HMt z^Kgdj|3`u);u?IG&O>Ua|9|f#Lz*t)ON{_6aXR5IQ%coi{C8{zzCwJAZS|GoAXv5K zd5Nv^)z2Vd+Lxh`BL7_n5jdxT#&WiziET%~3_Okny?C8BU@AqiSSK7;&cIWv*l3C>N>`)sElT0YUa z2Njy*1mT#1&KJNt*Wvrilbq9$^U2OFNS)_=7xKSU>olU6L$yu{vhQ2!c;TU*S?W9j zIi6i=P1^(w6#1V$1z6{~a%$raU~K12w3Nr;?91!aL53oy6!>E2323{-xgV00TBSEZ zl*9fPj_3F|56u1-*$kE(#1vd42R8)^F@uKuSJL>!USy%*3ULp4UupopTD(FNImAv7 z6fclc!ZqU4=N!1 zCt|gw;9;?zS-wj8r1&!J_%*_l;sKiB>x4InCgR^9yhY4|PW*2Y-XU(H^EpiTR*@wB zZ>-8LaR=eII7Z$j$6dc1JHIAoK=%s%x*Vtdavc4J9H$k0w;Y=l{7pGFEBIUDcJjXa z8(7G0@iyg=E13JlV%F;j%i81nM>;yoX5?Ytvk=Aav74clCwzBO>0Y}Zk-ffi*e8nY z6e9b4-vP0|Sf=dv-A%zuS=bJ93Ven_2}>xGdG7ZF>9AGgfT!wbBrKOw9r9F3_fugX0R3T4RT`bdKh37svAk70vhR0n z`l$wQRg7j>DPxV^s*6~_bQueKs{~DPhE2Qa^j1}|gen;@;hm<&dq&!pv^ZEm_ z3w#+2C4%F(oQARpIqRGb?5#+f`!Gt|W4>S+0rlq2rhf{5_7v$G6( z?nR`+*Ta?@mMPV~W~zL|^HZb*eWP?pANBkNks9B2DsG*p44SU@&7=0mJSQX4;Cqh^ zI__yeq|tW+J9R>45cZu5U+hoHluqA9N^pgYtoA*?lnGB8c=~)#GlP_84$2w!O)`VD zX9y`t-*OBG{)}f5kxA6?0IC%z!bgdqrzrmvYUV$~z83s*Gw9`8{I&MIsGIK(%)U-q zs^ybLRWD6N_^yQV{3_!2$&Ro{c7zHar>_3RlE2z_KWn?h=CB;}$sTg1>>)LCCK4Mg z|5-LC0e!wNvQA57WZ3rx75O3iGBiZecN=|wgU!|9-dPjC=wW z;pCu1yoySd&t$WlV;=$RnM7uRCC>me{WgHBy@9*WM|+A6Xg=^Q*;(ZRH1KT}(IBd! zgTQWf#zr|g3j7n{u$)^5zVk3xJLR+^@LhKM)q+Y1+#@@v_*eAL!1oBp#14E0?&W%C zSX|Ar{`tdbmZZ1>ohfi1;Ysl%loPn0f^QO+lKuheX|K2ftrd8XEwfK-hE#!v2tOnK z2-gz$K^Pj@FBfWoJuK_6T!aN4?gsuHxd;n9!iCklo*BOdD)1PYz1|r&qb&nJVr_(X z#*diok7?@_-WeM~9e9FdHQpKY`GF_5gRI^=`9R={GM<-M=2;gYp+t1zb7lk|{F@cW2asI;l!%|>bK2j4DGGe( zQwW^JD9Un5A&Tu>4;SUJrkw`yiUKRCbmw&RYRmaYXv}t|!dg7eCqe3U-hxnA^Z_n* zo@CBo#1puPnU?bFqsc(`_rWG7nt@(gl_j1+V&Gyr3?Ys~&Vfr<;R?Cr4Xh!)T4c}` zflIk84vJ=4(Phsdn;P*BgbS>F3vj)-g!qpTZV%0O&K~4i9}QGm z1`Y2IT>CKS#Yt?7>;D4K61!+TpFBdexP$y#rlY6|F-UkT;c9U+T0ih9!a)&YTWlkJ zjre!!>eIEr*NYcehwa2Sh!p8>AlxX{qRN3A35Uh+*wc5mfxc5L3jqGiDB#uNEW$UF zrceBc`F!@%zz>T*llPY0fRo}N+xk|@Fez5BU)@G{lei3AfzSO2@D}j{D(3TqcZj8I z>)Q$6Dt<~Y@dYj$c8R+w^A`!LRNs69+il0lYD(PnGjM)RU#%# zI`yu~ZA0=bS*r#>pt*@Ol!p2>0^ds{NvTAG{3(&FIyGpJ`-!B^s6JH$*ZQ;mOh-~8 zUPrR$i&Wg#Ik1+9`S_F*U^;Q>)J!K*RidHK19S|2Jt08C34XPR;kk974 zLh_)XD*?-bIKNOCErFOSkA+~GMXAjc`UXSIIfu#OAtE`FbMB$CkiDLTqlAAmJe-ZF zWGNO0A$f;dT;MHdCe>3}7YlNICGPbIP+>70!i1v~Ry{3*afe1qhd+yl5me1-68 z!quVyIs4WS4vNKuFC|Ig!<%-}HTC0pPH> zoN(k^z@6f!@WsA9!mGt+DMR#Pzb}Y&pIy&}LC0_CIg89F z@)9N!W?SZxk+PmC6zpr+6bWfrzYT1F>M0RBNXb0drmm{#A|7BeJ29Ktl}sNE?q^K0 zA1$Vm2_c0lC=rKEI$Knx16d7N7D-bu$c03*Alih=i53m=XKJT z0acgO6AJEDmtCQhEW4zHb(fT|?vhuyx}?PGE_sEkOI{(nq&0uoUb}G)n+;ZCUoJha zSO;P3k5B+1-__WWHo$(d2aRU;k-S1Qz}W35@j>x41hdV4ZNFvf?b?gb)OPGfkYHCF zeZYaJh)vZgH$v@UYGt*!0-yE>Md-t>Jc6w#Rob|1c@}w)_)+TW zlyVrloqP(3<}LiqAm(G=PV53%uOtie>|9;L9SnPd37(z9&^>cKbuV0(eeEbJ$47pp z1EGC;FlhL%HT?IW>)F?9{+dx3iHY@Dr}$Xk?@{J0D2C4`RDis;1d&%8xxYsxK$Zpn z2NQ1lUe?XCvva`|O7_oW_Uvq3@DfG3kFMOav$3WW&dq*+ilNk}9S2G6pOMrYg;@uf zkA3^8Yv8Etr@2qcC$0h{ycmQ4vieVJ@fUD@Z$GWX>07|}3ii`VoYmIpC5-Opdmlb) zRDgVz%tbzub3YvuDsl!dZ?om?ov6sdq-FnJWAC1pI;2c@QJDbxr{LvDb4dn znh(?UJkMmHLGez~h{*LC9-(UeZG?3+dU2%pN z$TH;%LC?+=3r8&bP1OKvt=i|gMVrGfVC=bvm@b>CvkV`mTsEgsm&IXbakq&sG&!ylHhY z^ja?WZlGjqNfo*%A6{+1hsm>aa^Iq`PF-ND)Sn@PGi5W?OPQ@D&tiPy!>`rzA%C0Z z4=2YO=Fy_@|Jl(Zdzw{I(p%ycv&4K+iTx?do@UQ4tDNdBn^iW=c1kPY0kAjg zEjxSaIa6DpBkw$G>eTa)jwyl$;Z=*vfrWAykgBxO_LfbZN+ugo5yqFVS%q!4RFcC#*J`P(Lt@|*0BD+1ARAu0OF_Vgkzj_k0Om2RD6b@^1|%m0U@|B} zD4|)#bSY4=5eP@UOqCi?>nfK(h#n^k2T+XynTag$_-skVlyK$@d%^Ka*jdF=#uF-$ z>A8?;Hj_wmVzyMtGDpj#>m77OQP|w9&Lr_kIu-h)h$qj`nweJ(P0X*fbJ{(n7Aj!P zC=VO0vHF(fvit?b*=l;gR&!=v^wb<*9@lBLnk+9r%R0T*KL2=AQXa<{C`T)s#_DS&%!d)DZn9qb-!*yVq#t0Saf0}8tbo9cc0b`jil@NZRsVo^>yk? z{HZ$mwSTKFlbWc*IYFE##A!->xNu<#UpHPj%0riRICEJyGCnYxiNzJMj@0#!;9gV7 zo=ui#5&u3Z{s%mD@>*A%k&&0Tnj2FGEGQzF1|t)Oq9MLNbfd@~)(Kt&7B6a9{mTP=zAgPZqV zDyEC|8LZ;w{^V8l@1f14@~(TpTYa~;x>k&eZ;PSzPm5h^7Pr=mSD7fTI4HijNG$uw zYhnn%tDEb^gT(iWGb%kEbiT`BF8`&@&e2qNc5@imyTHECgPVET)6Ick@97#BAJ2@b z%ZtZj(u{afa5}DT8i=fqj%MPpX??x1yiPb9!^;+2jx6uyz=aEu_Kx=DtsTu5S}9zl z97*Gnq;O{ek+fb5Hy=kwc@Odk?qzOn>4>z2dcqwm^nDq;CLk7({V9@8 zL~s+qdRz?>O>G<>!2Ju+n8jOSfR^V5n9>Q+u9Rp>g@?zvMqH91BtP*DDHY@Ybgg^a5*)u zNg0ge5@{nh^b&zmhtk8iExRM4blV?I!4zmBbm_AFdb*mLL+UD%SiiEEp?F&75*dr9 zqtZ%p+Qp?NJsr&*t=0fHqi`qo2=22^SrHh*I(MV>w6w2@z=D?JV(hpUEoWifxLBpP zJ0kCMK`XWQSf+HEMFclwN0dD_t%fB=)IBdWhHeY^$Uq{4)8X0Xll4;VN5iz-S8M04 zn=m@(eJ2qZ-pDwD%mgwU&5Vs}*?BF^G45?)o1r&kFTp^|rz7gh3snFwI9cA*jZ0R# zT3~W{cGCtc=r*m-3r%o!OBcfxEnWF`t#V`oMAPYsk^T(2?uKDpf*$F&RBP%!oWxz` z8)8sxBAtNz5p*kle?b>3FV4}oZ@AlJO{l309T^HXy=)`}U5~7fTL4tyrX2=HGpXT7 zW?bD+9*?1^^DGKxxQ_h~m!%kMiQ!HX=>u>9&x#1|vB_O6BRg_?=#mwo=9admRt}me zSPV{WDE&d_tEi<2blkeU34Vvp#5CEl^6JfAsw0atU1wESN3Xg1M*6X)Hr$@m+0tpQ znjsS342hsLbTu?2Y#<(qX3$fVC{olF9lf|RNBSo=HIGE*EXmv^)K8<2pg(1;R~b57 z9rZ%2)V93SL^~9Im!kQ<`hAq(!m)H_JicrwJ|3SK89+2TF)+LgU+7r4^vvMGq2R*7 z&X#7`A$UcL%K)57wSg-0bP2iUEadf+=^bk2^~0T6z>{rWE<$~nO3luOPj@|MMlMHgzSX1w=_pC z47qRDlGkz3$x1U%TZ2&yKOE65SjP&C^lh|od8<*j17zDJ*Cx5~#DbMf@D~+eG8Wre zwhe}d5sYjp{N6$|H#tF*4kuurIqh_Xv^$V?E<08-?e3Pji@yewk;LF2I${@lXv(qB((~-W(ddvVl60>7#U|qE2^gpxcv9&ET!!w@9FXA&Qyk|wa3u!t z6AkO5a;6_iM~5(BpM*QrLsyv6X2L4BA3<%E;IXJuZ0T)N1GgLyU~4*0sSN3sC1pDu ztMscJk=YWab){#)cr}=o-4rVu)x&c7msV-+62tFum{EY8U{$Xi2Gnh8xqH7RGObz-yDqgZZ^6A{*;%vn^R(S{@r_s_4#dYX#fnHf zTI?1?-hw8jQH_{L%1gx9yQC${DU2NW@q-`(={N>e&X(l;WtbG_er+T(J~C;BP7Kd5 zrr2VNLjZklEc^Q=#-Xh2w6yiwY!$)VYnq#xND}8Yj9lmt8S%uN#a`f*{Bu3q|R1p8Nsx@P;6-kW7On2s`X6jd0 zSSQmss$!-uu;WStbKek`?I4s{&fan}EI1(aqM8+RsmU}fAvYs4jnUlF8OH1|Z>(Ya>jJaWVqAPEXT@fM2M%0fCqMYTNGdU9 zVNn2|gCB!Qu0#8|J)x|moCT*w_^U9I0k^8@VknK7TFGUKs$Z^u^6I=?OkPkJOJvkf z@0i5|$AKKpF|Q4jN)NU7w&lD=Z+pw?ZY(SD#hr=x80O&6A%6u&`hU41F)N;EGTJ{f zI+7m2Z}y-c%8^=*gw{~jyUQwad4W-70P}u5--1?PztYPl6YP+x|HR_*_k{-3WqsK> z@Ze}7ih)8aQ`bq^i=M3Js;MrZWPg#)F*2S|iw*bsO#XC`_JJHRK?$<9rih%;%3CAZ z?YTlz3m5e(L-ZL|I+4b5T8`~3MD;>F=y0JbCrU~jZ7ZDouCZVg@rgnHB$1i=DA%wE z^9bn~>J5;^EH;=W1~}6IXBl9r1w)>QsvZ}^sE1Xb9J3=3Ew%^)9fHLwSfYY6RdANp zV>d2_>^c`CCdZBU1N)MpCqgoX*Wno!BP8SStruxvjw7AT!gn zdCLGf4K_~4?1h*)#nD$kCZIOh!G=f_Ge($BlrAd668Ps5#&wgX9`1Bvwuf`Cf%DEZ zf2zuOsN5VNmxh5kfvwS8Mf2}Fl?%nz7OagUEoMO@trA|93rMs%HZ_l^Urni8noRrjFUi3Uy&8XLowzAt$AKMk(huY?2KaeAFnT zR)orM<$_RgX4TVVasI=-gFGh)Ie;vQ*MG;`BuWJO22dvol0 za9|GnngE?iCP`C-&}wS?v@&8|LTi zuF{fRsyq6IS=jU&;W$>eE+{TKeu!2m}Q^ew_0lisLpI&g zGue1@2USiI&2WTm5xL?qt%4TRbKezeuMrDW=@iu>+P$ZO&WiydF=PzP3>+MMG4#Wl zIU~w;7P+bB+T0i%7*CH{G<8>d%T;ERTx4?UfrSP4M7aZyTNctTm`|u}d1%E7EcWzt zf%9=xJGK#{z{mjXdm_3qr#DDs$z}<%bI3ia#6(Ok0*0}BiFrtT65B1<)S}6nJt(CX z+3n=KC1-yHcOPex&{7;WHEGmg%ni*>uYzi**{htlsu5ZhJYhxFD~D{>g>bz|HotUd zauU?ri4GEqv|-D!HPpTeYjC|IF*K3LB&Eo*%||e!z%Z7bO}TgUxG2i@U-%!cxM{w6 z+s-9#c1u)EvU8O-i`+c5w6BpaNR6Ew(B%~N7#i#jcVPUE)zBzgdKvVY-u7lV z515Nw-f&x8kG^~nfErj>`{I2Bw5V;uxUMGk^NeP|SlQ{m-<#O{wTs*@;`~a=AX}4$ zYqpx?eqt!MV8c{Eub*|zQUlmT;z-Srz|~7sFKX+6MYwEV?W40cavNQ_KKXt_vvLeF z`^R#sm%^e!t;`ZoCta7?ky5`C86S*7y%8=eBF0ix56T;7I6BIC7j_{s6Y>W$x%Owf zX^FBMA+jHFqm`QhvYX}Bj_A4CA)A>{?qz^}?V`3}lF{sLC@Z9HEYG+mpQd%NTcvj; z)Jq+>(lni;Y7Oi|<(z|F8mZ|AI*=4jDNs#tF*2>!jB3=z8UfwMC;~PcU+13hWmjkF zB5}DB9gW4%t|?1h_su@a*^nOPv&%NAN|(WK;?)Bq;ZLOS;agq#==ba9)th;-ZeF_? zG7~E99KYe4sq-;>m*cqEF`rueY$)86Rf}$`!eIm9Ch>Uf(0Ha+-qW)XHIuR7XlmH1 zg+JyGj>-_0AXCgjz#WMoZ6ZDzC836rc*#{O*G09mk})r}<5|Nf&I&H~gmlJSHIf1w>wwclc6{IGx3`W_-_xY6cfe_p$-en-vzw8d~2>I)ZwR@ z=z0pv|2ooej4<16%1sRUqX8Fy%k$eA6{zt$|5ly$ zPb>cJg2ck|71gmny^zk#N8A+nI(7E`*?hb=dk<|s9*tNa2h+WnxtMW2pT5L0<99y3 zG&?TmokO8D)d8zo^cewD@eWp|?2nqe7#n~%r$6O12q zC3vmlj(NYV7vCu}e$Pb?ed@0+xRHRp*;|hD6TDVIXVuAGb4hGIIr5oZfKgE|ZVf!v zK80VB`h~_9eyM3c@HX1rS`-9?YaQe#ergUO z>uZhzn=gJbo%q5pbWK48i7)(a6u*UnlWL_ph|3Kdkj;irysgNB6H=fOia$E4=G{@ZWzQ z{8PXOUHk_8>-*3{nb6L0R-v4wz)ypH2K9WVQ)hHM@D7}(nV*~d=SaMX<-#TJgJ1PN z_{)F~=Ge2)N8gA3D&VI<{$1K%g*C@6;42{iq?X@g^c9(p-)hs}@{+%ugv5I^eW4!q zNW9_Bg+JE$6n;nVDd2-SvMlr`;HgiceY{utvOzSfaSWB;+1XNN9m?vh)ckqBOGWM5ULacCFsBC$zw3{)f%OXx2lMEIH( zr#dr}@|8Az{w^M?UAkl`-_jm~2-~OQBWm*ohu(0+32!90-GevlV;jNZis3CVUO37v ze@&`h3gynZxn4RdF#E<`o?cEB?nfSdy5;ECOzu+*g|GWFV@cN=n3Y}Fe2?HICKRW% zXy8~Xp?6oU$ci=CvgTRINKad{p~lIR Date: Fri, 2 Oct 2015 10:05:41 +0200 Subject: [PATCH 11/18] - Changed plugin configuration for cmake - Fixed/finished shared memory plugin for windows --- cmake/plugins.cmake | 58 +++++++++++++++++++ .../{client_plugin.c => client_plugin.c.in} | 0 plugins/{builtin => auth}/my_auth.c | 0 plugins/{builtin => cio}/cio_socket.c | 0 4 files changed, 58 insertions(+) create mode 100644 cmake/plugins.cmake rename libmariadb/{client_plugin.c => client_plugin.c.in} (100%) rename plugins/{builtin => auth}/my_auth.c (100%) rename plugins/{builtin => cio}/cio_socket.c (100%) diff --git a/cmake/plugins.cmake b/cmake/plugins.cmake new file mode 100644 index 00000000..9d929bbc --- /dev/null +++ b/cmake/plugins.cmake @@ -0,0 +1,58 @@ +# plugin configuration + +MACRO(REGISTER_PLUGIN name source struct type allow) + IF(PLUGINS) + LIST(REMOVE_ITEM PLUGINS ${name}) + ENDIF() + SET(${name}_PLUGIN_SOURCE ${source}) + SET(${name}_PLUGIN_TYPE ${type}) + SET(${name}_PLUGIN_STRUCT ${struct}) + SET(${name}_PLUGIN_SOURCE ${source}) + SET(${name}_PLUGIN_CHG ${allow}) + SET(PLUGINS ${PLUGINS} "${name}") +ENDMACRO() + +MARK_AS_ADVANCED(PLUGINS) + +# CIO +REGISTER_PLUGIN("SOCKET" "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_socket.c" "cio_socket_plugin" "STATIC" 0) +IF(WIN32) + REGISTER_PLUGIN("NPIPE" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_npipe.c" "cio_npipe_plugin" "DYNAMIC" 1) + REGISTER_PLUGIN("SHMEM" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_shmem.c" "cio_shmem_plugin" "DYNAMIC" 1) +ENDIF() + +# AUTHENTICATION +REGISTER_PLUGIN("AUTH_NATIVE" "${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c" "native_password_client_plugin" "STATIC" 0) +REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c" "old_password_client_plugin" "STATIC" "0") +REGISTER_PLUGIN("AUTH_DIALOG" "${CMAKE_SOURCE_DIR}/plugins/auth/dialog.c" "" "DYNAMIC" 0) +REGISTER_PLUGIN("AUTH_CLEARTEXT" "${CMAKE_SOURCE_DIR}/plugins/auth/mariadb_clear_text.c" "" "DYNAMIC" 0) + +# Allow registration of additional plugins +IF(PLUGIN_CONF_FILE) + INCLUDE(${PLUGIN_CONF_FILE}) +ENDIF() + + +SET(LIBMARIADB_SOURCES "") + +MESSAGE(STATUS "Plugin configuration") +FOREACH(PLUGIN ${PLUGINS}) + IF(WITH_${PLUGIN}_PLUGIN AND ${${PLUGIN}_PLUGIN_CHG} GREATER 0) + SET(${PLUGIN}_PLUGIN_TYPE ${WITH_${PLUGIN}_PLUGIN}) + ENDIF() + IF(${PLUGIN}_PLUGIN_TYPE MATCHES "STATIC") + SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ${${PLUGIN}_PLUGIN_SOURCE}) + SET(EXTERNAL_PLUGINS "${EXTERNAL_PLUGINS}extern struct st_mysql_client_plugin ${${PLUGIN}_PLUGIN_STRUCT};\n") + SET(BUILTIN_PLUGINS "${BUILTIN_PLUGINS}(struct st_mysql_client_plugin *)&${${PLUGIN}_PLUGIN_STRUCT},\n") + ENDIF() + MESSAGE(STATUS "${PLUGIN}: ${${PLUGIN}_PLUGIN_TYPE}") + MARK_AS_ADVANCED(${PLUGIN}_PLUGIN_TYPE) +ENDFOREACH() + +# since some files contain multiple plugins, remove duplicates from source files +LIST(REMOvE_DUPLICATES LIBMARIADB_SOURCES) + +CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/libmariadb/client_plugin.c.in + ${CMAKE_BINARY_DIR}/libmariadb/client_plugin.c) + +MARK_AS_ADVANCED(LIBMARIADB_SOURCES) diff --git a/libmariadb/client_plugin.c b/libmariadb/client_plugin.c.in similarity index 100% rename from libmariadb/client_plugin.c rename to libmariadb/client_plugin.c.in diff --git a/plugins/builtin/my_auth.c b/plugins/auth/my_auth.c similarity index 100% rename from plugins/builtin/my_auth.c rename to plugins/auth/my_auth.c diff --git a/plugins/builtin/cio_socket.c b/plugins/cio/cio_socket.c similarity index 100% rename from plugins/builtin/cio_socket.c rename to plugins/cio/cio_socket.c From 05659c872e8fc4238a9ac4af69ccd11575298699 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Fri, 2 Oct 2015 10:09:41 +0200 Subject: [PATCH 12/18] More plugin configuration changes (last commit was incomplete) --- CMakeLists.txt | 2 + cmake/plugins.cmake | 21 +- include/mysql.h | 6 +- include/mysql/client_plugin.h | 43 ++- include/mysql_com.h | 12 +- libmariadb/CMakeLists.txt | 11 +- libmariadb/client_plugin.c.in | 77 +++-- libmariadb/libmariadb.c | 62 +++- libmariadb/ma_cio.c | 2 +- libmariadb/ma_io.c | 2 +- libmariadb/secure/ma_schannel.c | 4 +- plugins/auth/CMakeLists.txt | 54 ++-- plugins/auth/dialog.c | 12 +- plugins/auth/mariadb_cleartext.c | 12 +- plugins/cio/CMakeLists.txt | 40 +-- plugins/cio/cio_npipe.c | 34 ++- plugins/cio/cio_shmem.c | 499 ++++++++++++++++++++----------- plugins/cio/cio_socket.c | 12 +- plugins/io/CMakeLists.txt | 26 +- plugins/io/remote_io.c | 10 +- plugins/trace/CMakeLists.txt | 26 +- plugins/trace/trace_example.c | 22 +- plugins/trace/trace_example.so | Bin 43254 -> 36957 bytes unittest/libmariadb/misc.c | 3 +- 24 files changed, 649 insertions(+), 343 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ede4184..d0e22ae1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ IF(WITH_RTC) SET(RTC_OPTIONS "/RTC1 /RTCc") ENDIF() +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/plugins.cmake) + IF(MSVC) # Speedup system tests INCLUDE(${CMAKE_SOURCE_DIR}/cmake/WindowsCache.cmake) diff --git a/cmake/plugins.cmake b/cmake/plugins.cmake index 9d929bbc..dac7de73 100644 --- a/cmake/plugins.cmake +++ b/cmake/plugins.cmake @@ -15,17 +15,26 @@ ENDMACRO() MARK_AS_ADVANCED(PLUGINS) # CIO -REGISTER_PLUGIN("SOCKET" "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_socket.c" "cio_socket_plugin" "STATIC" 0) +REGISTER_PLUGIN("SOCKET" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_socket.c" "cio_socket_plugin" "STATIC" 0) IF(WIN32) REGISTER_PLUGIN("NPIPE" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_npipe.c" "cio_npipe_plugin" "DYNAMIC" 1) REGISTER_PLUGIN("SHMEM" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_shmem.c" "cio_shmem_plugin" "DYNAMIC" 1) ENDIF() # AUTHENTICATION -REGISTER_PLUGIN("AUTH_NATIVE" "${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c" "native_password_client_plugin" "STATIC" 0) -REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c" "old_password_client_plugin" "STATIC" "0") -REGISTER_PLUGIN("AUTH_DIALOG" "${CMAKE_SOURCE_DIR}/plugins/auth/dialog.c" "" "DYNAMIC" 0) -REGISTER_PLUGIN("AUTH_CLEARTEXT" "${CMAKE_SOURCE_DIR}/plugins/auth/mariadb_clear_text.c" "" "DYNAMIC" 0) +REGISTER_PLUGIN("AUTH_NATIVE" "${CMAKE_SOURCE_DIR}/plugins/auth/my_auth.c" "native_password_client_plugin" "STATIC" 0) +REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CMAKE_SOURCE_DIR}/plugins/auth/my_auth.c" "old_password_client_plugin" "STATIC" 0) +REGISTER_PLUGIN("AUTH_DIALOG" "${CMAKE_SOURCE_DIR}/plugins/auth/dialog.c" "auth_dialog_plugin" "DYNAMIC" 1) +REGISTER_PLUGIN("AUTH_CLEARTEXT" "${CMAKE_SOURCE_DIR}/plugins/auth/mariadb_clear_text.c" "auth_cleartext_plugin" "DYNAMIC" 1) + +#Remote_IO +REGISTER_PLUGIN("REMOTEIO" "${CMAKE_SOURCE_DIR}/plugins/io/remote_io.c" "remote_io_plugin" "DYNAMIC" 1) + +#Trace +REGISTER_PLUGIN("TRACE_EXAMPLE" "${CMAKE_SOURCE_DIR}/plugins/trace/trace_example.c" "trace_example_plugin" "DYNAMIC" 1) + +#Connection +REGISTER_PLUGIN("REPLICATION" "${CMAKE_SOURCE_DIR}/plugins/connection/replication.c" "connection_replication_plugin" "STATIC" 1) # Allow registration of additional plugins IF(PLUGIN_CONF_FILE) @@ -50,7 +59,7 @@ FOREACH(PLUGIN ${PLUGINS}) ENDFOREACH() # since some files contain multiple plugins, remove duplicates from source files -LIST(REMOvE_DUPLICATES LIBMARIADB_SOURCES) +LIST(REMOVE_DUPLICATES LIBMARIADB_SOURCES) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/libmariadb/client_plugin.c.in ${CMAKE_BINARY_DIR}/libmariadb/client_plugin.c) diff --git a/include/mysql.h b/include/mysql.h index e2a10e69..c7b204ab 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -212,7 +212,8 @@ extern unsigned int mariadb_deinitialize_ssl; /* MariaDB Connector/C specific */ MYSQL_DATABASE_DRIVER=7000, MARIADB_OPT_SSL_FP, /* single finger print for server certificate verification */ - MARIADB_OPT_SSL_FP_LIST /* finger print white list for server certificate verification */ + MARIADB_OPT_SSL_FP_LIST, /* finger print white list for server certificate verification */ + MARIADB_OPT_CONNECTION_READ_ONLY }; enum mysql_status { MYSQL_STATUS_READY, @@ -231,8 +232,6 @@ extern unsigned int mariadb_deinitialize_ssl; MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY }; -struct st_mysql_options_extension; - struct st_mysql_options { unsigned int connect_timeout, read_timeout, write_timeout; unsigned int port, protocol; @@ -627,6 +626,7 @@ struct st_mysql_methods { int (*db_stmt_fetch)(MYSQL_STMT *stmt, unsigned char **row); int (*db_stmt_fetch_to_bind)(MYSQL_STMT *stmt, unsigned char *row); void (*db_stmt_flush_unbuffered)(MYSQL_STMT *stmt); + void (*set_error)(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, const char *format, ...); }; /* synonyms/aliases functions */ diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index 0f4059a1..76feb01e 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -31,25 +31,31 @@ #include #endif + #ifndef PLUGINDIR #define PLUGINDIR "lib/plugin" #endif /* known plugin types */ -#define MYSQL_CLIENT_GENERIC_PLUGIN 0 -#define MYSQL_CLIENT_CIO_PLUGIN 1 /* communication IO */ +#define MYSQL_CLIENT_PLUGIN_RESERVED 0 +#define MYSQL_CLIENT_PLUGIN_RESERVED2 1 #define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2 /* authentication */ -#define MYSQL_CLIENT_TRACE_PLUGIN 3 /* cio trace */ -#define MYSQL_CLIENT_REMOTEIO_PLUGIN 4 #define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_SSL_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION 0x01000 -#define MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION 0x0100 +#define MYSQL_CLIENT_MAX_PLUGINS 3 -#define MYSQL_CLIENT_MAX_PLUGINS 5 +/* Connector/C specific plugin types */ +#define MARIADB_CLIENT_REMOTEIO_PLUGIN 100 /* communication IO */ +#define MARIADB_CLIENT_CIO_PLUGIN 101 +#define MARIADB_CLIENT_TRACE_PLUGIN 102 +#define MARIADB_CLIENT_CONNECTION_PLUGIN 103 + +#define MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION 0x0100 +#define MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION 0x0100 +#define MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION 0x0100 +#define MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION 0x0100 + +#define MARIADB_CLIENT_MAX_PLUGINS 4 #define mysql_declare_client_plugin(X) \ struct st_mysql_client_plugin_ ## X \ @@ -76,26 +82,31 @@ struct st_mysql_client_plugin struct st_mysql; -/********* database api plugin specific declarations **********/ -struct st_mariadb_client_plugin_DB +/********* connection handler plugin specific declarations **********/ +typedef struct st_ma_connection_plugin { MYSQL_CLIENT_PLUGIN_HEADER /* functions */ - struct st_mysql_methods *methods; -} MARIADB_DB_PLUGIN; + MYSQL *(*connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd, + const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag); + void (*close)(MYSQL *mysql); + int (*options)(MYSQL *mysql, enum mysql_option, void *arg); + int (*set_connection)(MYSQL *mysql,enum enum_server_command command, const char *arg, + size_t length, my_bool skipp_check, void *opt_arg); +} MARIADB_CONNECTION_PLUGIN; #define MARIADB_DB_DRIVER(a) ((a)->ext_db) /******************* Communication IO plugin *****************/ #include +#ifdef HAVE_SSL #include +#endif typedef struct st_mariadb_client_plugin_CIO { MYSQL_CLIENT_PLUGIN_HEADER struct st_ma_cio_methods *methods; - struct st_ma_cio_ssl_methods *ssl_methods; - void *compress_methods; } MARIADB_CIO_PLUGIN; /******** authentication plugin specific declarations *********/ diff --git a/include/mysql_com.h b/include/mysql_com.h index 2974cc1b..77394eaa 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -227,6 +227,14 @@ typedef struct st_ma_cio MARIADB_CIO; #define MAX_INT_WIDTH 10 #define MAX_BIGINT_WIDTH 20 +struct st_ma_connection_plugin; + +typedef struct st_connection_handler +{ + struct st_ma_connection_plugin *plugin; + void *data; + my_bool free_data; +} MA_CONNECTION_HANDLER; typedef struct st_net { MARIADB_CIO *cio; @@ -245,11 +253,11 @@ typedef struct st_net { my_bool unused_1, unused_2; my_bool compress; my_bool unused_3; - unsigned char *unused_4; + MA_CONNECTION_HANDLER *conn_hdlr; unsigned int last_errno; unsigned char error; + my_bool unused_4; my_bool unused_5; - my_bool unused_6; char last_error[MYSQL_ERRMSG_SIZE]; char sqlstate[SQLSTATE_LENGTH+1]; diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index a340f163..520a1d9a 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -12,7 +12,6 @@ SET(EXPORT_SYMBOLS ma_cio_register_callback mariadb_connection mariadb_convert_string - mariadb_deinitialize_ssl mariadb_dyncol_check mariadb_dyncol_column_cmp_named mariadb_dyncol_column_count @@ -221,6 +220,9 @@ SET(EXPORT_SYMBOLS mysql_thread_safe mysql_use_result mysql_warning_count) +IF(WITH_SSL) + SET(EXPORTS ${EXPORTS} mariadb_deinitialize_ssl) +ENDIF() IF(WIN32) SET(EXPORT_CONTENT "EXPORTS\n") @@ -240,7 +242,7 @@ ENDIF() FILE(WRITE ${EXPORT_FILE} ${EXPORT_CONTENT}) -SET(LIBMARIADB_SOURCES +SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} array.c ma_dyncol.c bchange.c @@ -321,14 +323,11 @@ sha1.c my_stmt.c my_loaddata.c my_stmt_codec.c -client_plugin.c +${CMAKE_BINARY_DIR}/libmariadb/client_plugin.c ma_io.c -${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c -${CMAKE_SOURCE_DIR}/plugins/builtin/cio_socket.c ${SSL_SOURCES} ) -MESSAGE(STATUS "${LIBMARIADB_SOURCES}") IF(WIN32) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/win-iconv) diff --git a/libmariadb/client_plugin.c.in b/libmariadb/client_plugin.c.in index c0d594bb..5b9cadcb 100644 --- a/libmariadb/client_plugin.c.in +++ b/libmariadb/client_plugin.c.in @@ -58,13 +58,13 @@ static MEM_ROOT mem_root; #define plugin_declarations_sym "_mysql_client_plugin_declaration_" -static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= -{ - MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */ - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */ - MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, - MYSQL_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION, - MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION +static uint valid_plugins[][2]= { + {MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION}, + {MARIADB_CLIENT_CIO_PLUGIN, MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION}, + {MARIADB_CLIENT_TRACE_PLUGIN, MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION}, + {MARIADB_CLIENT_REMOTEIO_PLUGIN, MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION}, + {MARIADB_CLIENT_CONNECTION_PLUGIN, MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION}, + {0, 0} }; /* @@ -75,21 +75,18 @@ static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= there. The main purpose of a mutex is to prevent two threads from loading the same plugin twice in parallel. */ -struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS]; + + +struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS + MARIADB_CLIENT_MAX_PLUGINS]; #ifdef THREAD static pthread_mutex_t LOCK_load_client_plugin; #endif -extern struct st_mysql_client_plugin old_password_client_plugin; -extern struct st_mysql_client_plugin native_password_client_plugin; - -extern MARIADB_CIO_PLUGIN cio_socket_plugin; +@EXTERNAL_PLUGINS@ struct st_mysql_client_plugin *mysql_client_builtins[]= { - (struct st_mysql_client_plugin *)&old_password_client_plugin, - (struct st_mysql_client_plugin *)&native_password_client_plugin, - (struct st_mysql_client_plugin *)&cio_socket_plugin, + @BUILTIN_PLUGINS@ 0 }; @@ -105,6 +102,25 @@ static int is_not_initialized(MYSQL *mysql, const char *name) return 1; } +static int get_plugin_nr(int type) +{ + uint i= 0; + for(; valid_plugins[i][1]; i++) + if (valid_plugins[i][0]== type) + return i; + return -1; +} + +static my_bool check_plugin_version(struct st_mysql_client_plugin *plugin, int version, char *errmsg) +{ + if (plugin->interface_version < version || + (plugin->interface_version >> 8) > (version >> 8)) + { + errmsg= "Incompatible client plugin interface"; + return 1; + } + return 0; +} /** finds a plugin in the list @@ -116,20 +132,20 @@ static int is_not_initialized(MYSQL *mysql, const char *name) @retval a pointer to a found plugin or 0 */ - static struct st_mysql_client_plugin *find_plugin(const char *name, int type) { struct st_client_plugin_int *p; + int plugin_nr= get_plugin_nr(type); DBUG_ASSERT(initialized); - DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS); - if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) + DBUG_ASSERT(plugin_nr != -1); + if (plugin_nr == -1) return 0; if (!name) - return plugin_list[type]->plugin; + return plugin_list[plugin_nr]->plugin; - for (p= plugin_list[type]; p; p= p->next) + for (p= plugin_list[plugin_nr]; p; p= p->next) { if (strcmp(p->plugin->name, name) == 0) return p->plugin; @@ -137,6 +153,7 @@ static struct st_mysql_client_plugin *find_plugin(const char *name, int type) return NULL; } + /** verifies the plugin and adds it to the list @@ -154,28 +171,23 @@ static struct st_mysql_client_plugin * add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, int argc, va_list args) { - const char *errmsg; + char *errmsg; struct st_client_plugin_int plugin_int, *p; char errbuf[1024]; + int plugin_nr; DBUG_ASSERT(initialized); plugin_int.plugin= plugin; plugin_int.dlhandle= dlhandle; - if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS) + if ((plugin_nr= get_plugin_nr(plugin->type)) == -1) { errmsg= "Unknown client plugin type"; goto err1; } - - if (plugin->interface_version < plugin_version[plugin->type] || - (plugin->interface_version >> 8) > - (plugin_version[plugin->type] >> 8)) - { - errmsg= "Incompatible client plugin interface"; + if (check_plugin_version(plugin, valid_plugins[plugin_nr][1], errmsg)) goto err1; - } /* Call the plugin initialization function, if any */ if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args)) @@ -197,8 +209,8 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, safe_mutex_assert_owner(&LOCK_load_client_plugin); #endif - p->next= plugin_list[plugin->type]; - plugin_list[plugin->type]= p; + p->next= plugin_list[plugin_nr]; + plugin_list[plugin_nr]= p; return plugin; @@ -460,11 +472,12 @@ struct st_mysql_client_plugin * STDCALL mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) { struct st_mysql_client_plugin *p; + int plugin_nr= find_plugin(name, type); if (is_not_initialized(mysql, name)) return NULL; - if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) + if (plugin_nr == -1) { my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, "invalid type"); diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index bbc03e18..c3e839b7 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -67,6 +67,7 @@ #endif #include #include +#include #define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) @@ -342,7 +343,6 @@ void free_rows(MYSQL_DATA *cur) } } - int mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, size_t length, my_bool skipp_check, void *opt_arg) @@ -354,6 +354,13 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, DBUG_PRINT("info", ("server_command: %d packet_size: %u", command, length)); + if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data) + { + result= mysql->net.conn_hdlr->plugin->set_connection(mysql, command, arg, length, skipp_check, opt_arg); + if (result== -1) + DBUG_RETURN(result); + } + if (mysql->net.cio == 0) { /* Do reconnect if possible */ if (mysql_reconnect(mysql)) @@ -552,6 +559,9 @@ static void end_server(MYSQL *mysql) { DBUG_ENTER("end_server"); + + /* if net->error 2 and reconnect is activated, we need to inforn + connection handler */ if (mysql->net.cio != 0) { ma_cio_close(mysql->net.cio); @@ -1300,6 +1310,35 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, db ? db : "(Null)", user ? user : "(Null)")); + if (!mysql->methods) + mysql->methods= &MARIADB_DEFAULT_METHODS; + /* special case: + * If hostname contains "://", e.g. "repl://localhost", we need to process connection + * by connection plugin + */ + if (host && (end= strstr(host, "://"))) + { + MARIADB_CONNECTION_PLUGIN *plugin; + char plugin_name[64]; + + bzero(plugin_name, 64); + strncpy(plugin_name, host, MIN(end - host, 63)); + end+= 3; + if (!(plugin= (MARIADB_CONNECTION_PLUGIN *)mysql_client_find_plugin(mysql, plugin_name, MARIADB_CLIENT_CONNECTION_PLUGIN))) + DBUG_RETURN(NULL); + + if (!(mysql->net.conn_hdlr= (MA_CONNECTION_HANDLER *)my_malloc(sizeof(MA_CONNECTION_HANDLER), MYF(MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + mysql->net.conn_hdlr->plugin= plugin; + + if (plugin->connect) + return plugin->connect(mysql, end, user, passwd, db, port, unix_socket, client_flag); + } + ma_set_connect_attrs(mysql); if (net->cio) /* check if we are already connected */ @@ -1871,8 +1910,6 @@ static void mysql_close_memory(MYSQL *mysql) mysql->host_info= mysql->server_version=mysql->user=mysql->passwd=mysql->db=0; } - - void my_set_error(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, @@ -1908,12 +1945,20 @@ void mysql_close_slow_part(MYSQL *mysql) void STDCALL mysql_close(MYSQL *mysql) { - MYSQL_STMT *stmt; + MYSQL_STMT *stmt; DBUG_ENTER("mysql_close"); if (mysql) /* Some simple safety */ { LIST *li_stmt= mysql->stmts; + if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data) + { + void *p= (void *)mysql->net.conn_hdlr; + mysql->net.conn_hdlr->plugin->close(mysql); + my_free(p); + DBUG_VOID_RETURN; + } + if (mysql->methods) mysql->methods->db_close(mysql); @@ -1931,6 +1976,10 @@ mysql_close(MYSQL *mysql) /* Clear pointers for better safety */ bzero((char*) &mysql->options,sizeof(mysql->options)); + + if (mysql->extension) + my_free(mysql->extension); + mysql->net.cio= 0; if (mysql->free_me) my_free(mysql); @@ -2797,6 +2846,11 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) case MARIADB_OPT_SSL_FP_LIST: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp_list, (char *)arg1); break; + case MARIADB_OPT_CONNECTION_READ_ONLY: + if (mysql->net.conn_hdlr) + DBUG_RETURN(mysql->net.conn_hdlr->plugin->options(mysql, MARIADB_OPT_CONNECTION_READ_ONLY, arg1)); + else + return -1; default: va_end(ap); DBUG_RETURN(-1); diff --git a/libmariadb/ma_cio.c b/libmariadb/ma_cio.c index fabb1417..e9244fca 100644 --- a/libmariadb/ma_cio.c +++ b/libmariadb/ma_cio.c @@ -90,7 +90,7 @@ MARIADB_CIO *ma_cio_init(MA_CIO_CINFO *cinfo) if (!(cio_plugin= (MARIADB_CIO_PLUGIN *) mysql_client_find_plugin(cinfo->mysql, cio_plugins[type], - MYSQL_CLIENT_CIO_PLUGIN))) + MARIADB_CLIENT_CIO_PLUGIN))) { /* error handling */ return NULL; diff --git a/libmariadb/ma_io.c b/libmariadb/ma_io.c index 15277818..4d4e5a6e 100644 --- a/libmariadb/ma_io.c +++ b/libmariadb/ma_io.c @@ -124,7 +124,7 @@ remote: { MYSQL mysql; if (rio_plugin ||(rio_plugin= (struct st_mysql_client_plugin_REMOTEIO *) - mysql_client_find_plugin(&mysql, NULL, MYSQL_CLIENT_REMOTEIO_PLUGIN))) + mysql_client_find_plugin(&mysql, NULL, MARIADB_CLIENT_REMOTEIO_PLUGIN))) return rio_plugin->methods->open(location, mode); return NULL; } diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index d27d895f..fe363283 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -718,12 +718,12 @@ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_CIO *cio, { /* server closed connection */ // todo: error - printf("Server closed connection\n"); return NULL; } if (dwBytesRead < 0) { - printf("Socket error\n"); + /* socket error */ + // todo: error return NULL; } dwOffset+= dwBytesRead; diff --git a/plugins/auth/CMakeLists.txt b/plugins/auth/CMakeLists.txt index 9531ea64..c30c9873 100644 --- a/plugins/auth/CMakeLists.txt +++ b/plugins/auth/CMakeLists.txt @@ -1,32 +1,36 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) # Dialog plugin -SET(DIALOG_SOURCES dialog.c ${CMAKE_SOURCE_DIR}/libmariadb/get_password.c) -IF(WIN32) - SET(DIALOG_SOURCES ${DIALOG_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) -ENDIF() -ADD_LIBRARY(dialog SHARED ${DIALOG_SOURCES}) -SET_TARGET_PROPERTIES(dialog PROPERTIES PREFIX "") +IF(AUTH_DIALOG_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_DIALOG_DYNAMIC=1) + SET(DIALOG_SOURCES dialog.c ${CMAKE_SOURCE_DIR}/libmariadb/get_password.c) + IF(WIN32) + SET(DIALOG_SOURCES ${DIALOG_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + ENDIF() + ADD_LIBRARY(dialog SHARED ${DIALOG_SOURCES}) + SET_TARGET_PROPERTIES(dialog PROPERTIES PREFIX "") + + INSTALL(TARGETS + dialog + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") +ENDIF() -INSTALL(TARGETS - dialog - RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" - ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") # Cleartext -# Dialog plugin -SET(CTEXT_SOURCES mariadb_cleartext.c) -IF(WIN32) - SET(CTEXT_SOURCES ${CTEXT_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) +IF(AUTH_CLEARTEXT_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_CLEARTEXT_DYNAMIC=1) + SET(CTEXT_SOURCES mariadb_cleartext.c) + IF(WIN32) + SET(CTEXT_SOURCES ${CTEXT_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + ENDIF() + ADD_LIBRARY(mysql_clear_password SHARED ${CTEXT_SOURCES}) + SET_TARGET_PROPERTIES(mysql_clear_password PROPERTIES PREFIX "") + + INSTALL(TARGETS + mysql_clear_password + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") ENDIF() -ADD_LIBRARY(mysql_clear_password SHARED ${CTEXT_SOURCES}) -SET_TARGET_PROPERTIES(mysql_clear_password PROPERTIES PREFIX "") - -INSTALL(TARGETS - mysql_clear_password - RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" - ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") - -# Cleartext diff --git a/plugins/auth/dialog.c b/plugins/auth/dialog.c index bf07984e..0424e01f 100644 --- a/plugins/auth/dialog.c +++ b/plugins/auth/dialog.c @@ -41,15 +41,23 @@ static int auth_dialog_init(char *unused1, mysql_authentication_dialog_ask_t auth_dialog_func; -mysql_declare_client_plugin(AUTHENTICATION) +#ifndef HAVE_DIALOG_DYNAMIC +struct st_mysql_client_plugin_AUTHENTICATION auth_dialog_plugin= +#else +struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ = +#endif +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, "dialog", "Sergei Golubchik, Georg Richter", "Dialog Client Authentication Plugin", {0,1,0}, + "LGPL", auth_dialog_init, NULL, auth_dialog_open -mysql_end_client_plugin; +}; /* {{{ static char *auth_dialog_native_prompt */ diff --git a/plugins/auth/mariadb_cleartext.c b/plugins/auth/mariadb_cleartext.c index f9bce576..a1dbb485 100644 --- a/plugins/auth/mariadb_cleartext.c +++ b/plugins/auth/mariadb_cleartext.c @@ -53,14 +53,22 @@ static int clear_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) } /* }}} */ -mysql_declare_client_plugin(AUTHENTICATION) +#ifndef HAVE_DIALOG_DYNAMIC +struct st_mysql_client_plugin_AUTHENTICATION auth_cleartext_plugin= +#else +struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ = +#endif +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, "mysql_clear_password", "Georg Richter", "MariaDB clear password authentication plugin", {0,1,0}, + "LGPL", NULL, NULL, clear_password_auth_client -mysql_end_client_plugin; +}; diff --git a/plugins/cio/CMakeLists.txt b/plugins/cio/CMakeLists.txt index febc3d55..dc0e6616 100644 --- a/plugins/cio/CMakeLists.txt +++ b/plugins/cio/CMakeLists.txt @@ -2,27 +2,31 @@ IF(WIN32) SET(EXPORT_FILE "cio_plugin.def") ENDIF() -IF(GNUTLS_FOUND) - IF (NOT ${DEFAULT_SSL} STREQUAL "GNUTLS") - SET(CMAKE_SHARED_LIBRARY_PREFIX "") - ADD_LIBRARY(cio_gnutls SHARED cio_gnutls.c ${EXPORT_FILE}) - TARGET_LINK_LIBRARIES(cio_gnutls ${GNUTLS_LIBRARIES}) - ENDIF() -ENDIF() +SET(CMAKE_SHARED_LIBRARY_PREFIX "") -IF(OPENSSL_FOUND) - IF (NOT ${DEFAULT_SSL} STREQUAL "OPENSSL") - SET(CMAKE_SHARED_LIBRARY_PREFIX "") - SET(source_files cio_openssl.c ${EXPORT_FILE}) - ADD_LIBRARY(cio_openssl SHARED ${source_files}) - TARGET_LINK_LIBRARIES(cio_openssl ${OPENSSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF() +IF(SOCKET_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_SOCKET_DYNAMIC=1) + ADD_LIBRARY(cio_socket SHARED cio_socket.c ${EXPORT_FILE}) + SET(INSTALL_LIBS cio_socket) ENDIF() IF(WIN32) - SET(CMAKE_SHARED_LIBRARY_PREFIX "") - IF (NOT ${DEFAULT_SSL} STREQUAL "SCHANNEL") - ADD_LIBRARY(cio_schannel SHARED cio_schannel.c ${EXPORT_FILE}) + IF(NPIPE_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_NPIPE_DYNAMIC=1) + ADD_LIBRARY(cio_npipe SHARED cio_npipe.c ${EXPORT_FILE}) + SET(INSTALL_LIBS ${INSTALL_LIBS} cio_npipe) + ENDIF() + IF(SHMEM_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_SHMEM_DYNAMIC=1) + ADD_LIBRARY(cio_shmem SHARED cio_shmem.c ${EXPORT_FILE}) + SET(INSTALL_LIBS ${INSTALL_LIBS} cio_shmem) ENDIF() - ADD_LIBRARY(cio_npipe SHARED cio_npipe.c ${EXPORT_FILE}) +ENDIF() + +IF(INSTALL_LIBS) + INSTALL(TARGETS + ${INSTALL_LIBS} + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") ENDIF() diff --git a/plugins/cio/cio_npipe.c b/plugins/cio/cio_npipe.c index e9ad6439..1a644290 100644 --- a/plugins/cio/cio_npipe.c +++ b/plugins/cio/cio_npipe.c @@ -1,3 +1,25 @@ +/************************************************************************************ + Copyright (C) 2015 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ + /* MariaDB Communication IO (CIO) plugin for named pipe communication */ #ifdef _WIN32 @@ -10,13 +32,13 @@ #include #include -#ifndef HAVE_NPIPE_DEFAULT +#ifdef HAVE_NPIPE_DYNAMIC #define my_malloc(A, B) malloc((A)) #undef my_free #define my_free(A,B) free(((A))) #endif - +/* Function prototypes */ my_bool cio_npipe_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); int cio_npipe_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type); size_t cio_npipe_read(MARIADB_CIO *cio, uchar *buffer, size_t length); @@ -45,10 +67,14 @@ struct st_ma_cio_methods cio_npipe_methods= { cio_npipe_is_blocking }; +#ifndef HAVE_NPIPE_DYNAMIC +MARIADB_CIO_PLUGIN cio_npipe_plugin = +#else MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = +#endif { - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + MARIADB_CLIENT_CIO_PLUGIN, + MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, "cio_npipe", "Georg Richter", "MariaDB communication IO plugin for named pipe communication", diff --git a/plugins/cio/cio_shmem.c b/plugins/cio/cio_shmem.c index ca282587..6c02ede0 100644 --- a/plugins/cio/cio_shmem.c +++ b/plugins/cio/cio_shmem.c @@ -1,9 +1,25 @@ -/* MariaDB Communication IO (CIO) plugin for shate memory communication - * - * During initialization MariaDB serve creates a named file mapping - * object, named : * - * - * */ +/************************************************************************************ + Copyright (C) 2015 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ +/* MariaDB Communication IO (CIO) plugin for named pipe communication */ #ifdef _WIN32 @@ -15,51 +31,56 @@ #include #include -#ifndef HAVE_NPIPE_DEFAULT +#ifdef HAVE_SHMEM_DYNAMIC #define my_malloc(A, B) malloc((A)) #undef my_free #define my_free(A,B) free(((A))) #endif - +#define SHM_DEFAULT_NAME "MYSQL" +#define CIO_SHM_BUFFER_SIZE 16000 + 4 my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type); -size_t cio_shm_read(MARIADB_CIO *cio, uchar *buffer, size_t length); +size_t cio_shm_read(MARIADB_CIO *cio, const uchar *buffer, size_t length); size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length); int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout); my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value); my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo); my_bool cio_shm_close(MARIADB_CIO *cio); -int cio_shm_fast_send(MARIADB_CIO *cio); -int cio_shm_keepalive(MARIADB_CIO *cio); -my_socket cio_shm_get_socket(MARIADB_CIO *cio); -my_bool cio_shm_is_blocking(MARIADB_CIO *cio); + struct st_ma_cio_methods cio_shm_methods= { cio_shm_set_timeout, cio_shm_get_timeout, cio_shm_read, + NULL, cio_shm_write, + NULL, cio_shm_wait_io_or_timeout, cio_shm_blocking, cio_shm_connect, cio_shm_close, - cio_shm_fast_send, - cio_shm_keepalive, - cio_shm_get_socket, - cio_shm_is_blocking + NULL, + NULL, + NULL, + NULL, + NULL }; -MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = +#ifndef HAVE_SHMEM_DYNAMIC +MARIADB_CIO_PLUGIN cio_shmem_plugin= +#else +MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_= +#endif { - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, - "cio_shm", + MARIADB_CLIENT_CIO_PLUGIN, + MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + "cio_shmem", "Georg Richter", - "MariaDB communication IO plugin for named pipe communication", + "MariaDB communication IO plugin for Windows shared memory communication", {1, 0, 0}, - "LGPL", + "LGPPL", NULL, NULL, &cio_shm_methods, @@ -67,19 +88,34 @@ MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = NULL }; +enum enum_shm_events +{ + CIO_SHM_SERVER_WROTE= 0, + CIO_SHM_SERVER_READ, + CIO_SHM_CLIENT_WROTE, + CIO_SHM_CLIENT_READ, + CIO_SHM_CONNECTION_CLOSED +}; + +typedef struct { + HANDLE event[5]; + HANDLE file_map; + LPVOID *map; + char *read_pos; + size_t buffer_size; +} CIO_SHM; + +char *StrEvent[]= {"SERVER_WROTE", "SERVER_READ", "CLIENT_WROTE", "CLIENT_READ", "CONNECTION_CLOSED"}; + struct st_cio_shm { - HANDLE pipe; - OVERLAPPED overlapped; - size_t rw_size; - int fcntl_mode; - MYSQL *mysql; + char *shm_name; }; my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout) { if (!cio) return 1; - cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1; + cio->timeout[type]= (timeout > 0) ? timeout * 1000 : INFINITE; return 0; } @@ -90,79 +126,99 @@ int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type) return cio->timeout[type] / 1000; } -size_t cio_shm_read(MARIADB_CIO *cio, uchar *buffer, size_t length) +size_t cio_shm_read(MARIADB_CIO *cio, const uchar *buffer, size_t length) { - DWORD dwRead= 0; - size_t r= -1; - struct st_cio_shm *cpipe= NULL; - - if (!cio || !cio->data) + CIO_SHM *cio_shm= (CIO_SHM *)cio->data; + size_t copy_size= length; + HANDLE events[2]; + + if (!cio_shm) return -1; - cpipe= (struct st_cio_shm *)cio->data; - - if (ReadFile(cpipe->pipe, buffer, length, &dwRead, &cpipe->overlapped)) + /* we need to wait for write and close events */ + if (!cio_shm->buffer_size) { - r= (size_t)dwRead; - goto end; + events[0]= cio_shm->event[CIO_SHM_CONNECTION_CLOSED]; + events[1]= cio_shm->event[CIO_SHM_SERVER_WROTE]; + + switch(WaitForMultipleObjects(2, events, 0, cio->timeout[CIO_READ_TIMEOUT])) + { + case WAIT_OBJECT_0: /* server closed connection */ + SetLastError(ERROR_GRACEFUL_DISCONNECT); + return -1; + case WAIT_OBJECT_0 +1: /* server_wrote event */ + break; + case WAIT_TIMEOUT: + SetLastError(ETIMEDOUT); + default: + return -1; + } + /* server sent data */ + cio_shm->read_pos= cio_shm->map; + cio_shm->buffer_size= uint4korr(cio_shm->read_pos); + cio_shm->read_pos+= 4; } - if (GetLastError() == ERROR_IO_PENDING) - r= cio_shm_wait_io_or_timeout(cio, 1, 0); + + if (cio_shm->buffer_size < copy_size) + copy_size= cio_shm->buffer_size; - if (!r) - r= cpipe->rw_size; -end: - return r; + if (copy_size) + { + memcpy(buffer, cio_shm->read_pos, cio_shm->buffer_size); + cio_shm->read_pos+= copy_size; + cio_shm->buffer_size-= copy_size; + } + + /* we need to read again */ + if (!cio_shm->buffer_size) + if (!SetEvent(cio_shm->event[CIO_SHM_CLIENT_READ])) + return -1; + + return copy_size; } -size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length) +size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length) { - DWORD dwWrite= 0; - size_t r= -1; - struct st_cio_shm *cpipe= NULL; - - if (!cio || !cio->data) + HANDLE events[2]; + CIO_SHM *cio_shm= (CIO_SHM *)cio->data; + size_t bytes_to_write= length; + uchar *buffer_pos= buffer; + + if (!cio_shm) return -1; - cpipe= (struct st_cio_shm *)cio->data; + events[0]= cio_shm->event[CIO_SHM_CONNECTION_CLOSED]; + events[1]= cio_shm->event[CIO_SHM_SERVER_READ]; - if (WriteFile(cpipe->pipe, buffer, length, &dwWrite, &cpipe->overlapped)) + while (bytes_to_write) { - r= (size_t)dwWrite; - goto end; + size_t pkt_length; + switch (WaitForMultipleObjects(2, events, 0, cio->timeout[CIO_WRITE_TIMEOUT])) { + case WAIT_OBJECT_0: /* connection closed */ + SetLastError(ERROR_GRACEFUL_DISCONNECT); + return -1; + case WAIT_OBJECT_0 + 1: /* server_read */ + break; + case WAIT_TIMEOUT: + SetLastError(ETIMEDOUT); + default: + return -1; + } + pkt_length= MIN(CIO_SHM_BUFFER_SIZE, length); + int4store(cio_shm->map, pkt_length); + memcpy((uchar *)cio_shm->map + 4, buffer_pos, length); + buffer_pos+= length; + bytes_to_write-= length; + + if (!SetEvent(cio_shm->event[CIO_SHM_CLIENT_WROTE])) + return -1; } - if (GetLastError() == ERROR_IO_PENDING) - r= cio_shm_wait_io_or_timeout(cio, 1, 0); - - if (!r) - r= cpipe->rw_size; -end: - return r; + return length; } + int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout) { - int r= -1; - DWORD status; - int save_error; - struct st_cio_shm *cpipe= NULL; - - cpipe= (struct st_cio_shm *)cio->data; - - if (!timeout) - timeout= (is_read) ? cio->timeout[CIO_READ_TIMEOUT] : cio->timeout[CIO_WRITE_TIMEOUT]; - - status= WaitForSingleObject(cpipe->overlapped.hEvent, timeout); - if (status == WAIT_OBJECT_0) - { - if (GetOverlappedResult(cpipe->pipe, &cpipe->overlapped, &cpipe->rw_size, FALSE)) - return 0; - } - /* other status codes are: WAIT_ABANDONED, WAIT_TIMEOUT and WAIT_FAILED */ - save_error= GetLastError(); - CancelIo(cpipe->pipe); - SetLastError(save_error); - return -1; } my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode) @@ -182,122 +238,206 @@ int cio_shm_fast_send(MARIADB_CIO *cio) /* not supported */ return 0; } + my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo) { - struct st_cio_shm *cpipe= NULL; + char *base_memory_name; + char *prefixes[]= {"", "Global\\", NULL}; + char *shm_name, *shm_suffix, *shm_prefix; + uchar i= 0; + int len; + DWORD cid; + char connection_id[28]; + char *connection_id_str; + DWORD dwDesiredAccess= EVENT_MODIFY_STATE | SYNCHRONIZE; + HANDLE hdlConnectRequest= NULL, + hdlConnectRequestAnswer= NULL, + file_map= NULL; + LPVOID map= NULL; + CIO_SHM *cio_shm= (CIO_SHM*)LocalAlloc(LMEM_ZEROINIT, sizeof(CIO_SHM)); - if (!cio || !cinfo) - return 1; - - if (!(cpipe= (struct st_cio_shm *)my_malloc(sizeof(struct st_cio_shm), MYF(0)))) + if (!cio_shm) { CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, ""); - return 1; - } - bzero(cpipe, sizeof(struct st_cio_shm)); - cio->data= (void *)cpipe; - cpipe->pipe= INVALID_HANDLE_VALUE; - cio->mysql= cinfo->mysql; - cio->type= cinfo->type; - - if (cinfo->type == CIO_TYPE_NAMEDPIPE) - { - my_bool has_timedout= 0; - char szPipeName[MAX_PATH]; - DWORD dwMode; - - if ( ! cinfo->unix_socket || (cinfo->unix_socket)[0] == 0x00) - cinfo->unix_socket = MYSQL_NAMEDPIPE; - if (!cinfo->host || !strcmp(cinfo->host,LOCAL_HOST)) - cinfo->host=LOCAL_HOST_NAMEDPIPE; - - szPipeName[MAX_PATH - 1]= 0; - snprintf(szPipeName, MAX_PATH - 1, "\\\\%s\\pipe\\%s", cinfo->host, cinfo->unix_socket); - - while (1) - { - if ((cpipe->pipe = CreateFile(szPipeName, - GENERIC_READ | - GENERIC_WRITE, - 0, /* no sharing */ - NULL, /* default security attributes */ - OPEN_EXISTING, - 0, /* default attributes */ - NULL)) != INVALID_HANDLE_VALUE) - break; - - if (GetLastError() != ERROR_PIPE_BUSY) - { - cio->set_error(cio, CR_NAMEDPIPEOPEN_ERROR, SQLSTATE_UNKNOWN, 0, - cinfo->host, cinfo->unix_socket, GetLastError()); - goto end; - } - - if (has_timedout || !WaitNamedPipe(szPipeName, cio->timeout[CIO_CONNECT_TIMEOUT])) - { - cio->set_error(cio, CR_NAMEDPIPEWAIT_ERROR, SQLSTATE_UNKNOWN, 0, - cinfo->host, cinfo->unix_socket, GetLastError()); - goto end; - } - has_timedout= 1; - } - - dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; - if (!SetNamedPipeHandleState(cpipe->pipe, &dwMode, NULL, NULL)) - { - cio->set_error(cio, CR_NAMEDPIPESETSTATE_ERROR, SQLSTATE_UNKNOWN, 0, - cinfo->host, cinfo->unix_socket, (ulong) GetLastError()); - goto end; - } - - /* Register event handler for overlapped IO */ - if (!(cpipe->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL))) - { - cio->set_error(cio, CR_CREATE_EVENT_FAILED, SQLSTATE_UNKNOWN, 0, - GetLastError()); - goto end; - } return 0; } -end: - if (cpipe) + + /* MariaDB server constructs the event name as follows: + "Global\\base_memory_name" or + "\\base_memory_name" + */ + + + base_memory_name= (cinfo->mysql->options.shared_memory_base_name) ? + cinfo->mysql->options.shared_memory_base_name : SHM_DEFAULT_NAME; + + + if (!(shm_name= LocalAlloc(LMEM_ZEROINIT, strlen(base_memory_name) + 40))) { - if (cpipe->pipe != INVALID_HANDLE_VALUE) - CloseHandle(cpipe->pipe); - my_free((gptr)cpipe, MYF(0)); - cio->data= NULL; + CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, ""); + goto error; + } + + /* iterate through prefixes */ + while (prefixes[i]) + { + len= sprintf(shm_name, "%s%s_", prefixes[i], base_memory_name); + shm_suffix= shm_name + len; + strcpy(shm_suffix, "CONNECT_REQUEST"); + if ((hdlConnectRequest= OpenEvent(dwDesiredAccess, 0, shm_name))) + { + /* save prefix to prevent further loop */ + shm_prefix= prefixes[i]; + break; + } + i++; + } + if (!hdlConnectRequest) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Opening CONNECT_REQUEST event failed", GetLastError()); + goto error; + } + + strcpy(shm_suffix, "CONNECT_ANSWER"); + if (!(hdlConnectRequestAnswer= OpenEvent(dwDesiredAccess, 0, shm_name))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Opening CONNECT_ANSWER event failed", GetLastError()); + goto error; + } + + /* get connection id, so we can build the filename used for connection */ + strcpy(shm_suffix, "CONNECT_DATA"); + if (!(file_map= OpenFileMapping(FILE_MAP_WRITE, 0, shm_name))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "OpenFileMapping failed", GetLastError()); + goto error; + } + + /* try to get first 4 bytes, which represents connection_id */ + if (!(map= MapViewOfFile(file_map, FILE_MAP_WRITE, 0, 0, sizeof(cid)))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Reading connection_id failed", GetLastError()); + goto error; + } + + /* notify server */ + if (!SetEvent(hdlConnectRequest)) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Failed sending connection request", GetLastError()); + goto error; + } + + /* Wait for server answer */ + switch(WaitForSingleObject(hdlConnectRequestAnswer, cio->timeout[CIO_CONNECT_TIMEOUT])) { + case WAIT_ABANDONED: + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Mutex was not released in time", GetLastError()); + goto error; + break; + case WAIT_FAILED: + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Operation wait failed", GetLastError()); + goto error; + break; + case WAIT_TIMEOUT: + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Operation timed out", GetLastError()); + goto error; + break; + case WAIT_OBJECT_0: + break; + default: + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Wait for server failed", GetLastError()); + break; + } + + cid= uint4korr(map); + + len= sprintf(shm_name, "%s%s_%d_", shm_prefix, base_memory_name, cid); + shm_suffix= shm_name + len; + + strcpy(shm_suffix, "DATA"); + cio_shm->file_map= OpenFileMapping(FILE_MAP_WRITE, 0, shm_name); + if (cio_shm->file_map == NULL) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "OpenFileMapping failed", GetLastError()); + goto error; + } + if (!(cio_shm->map= MapViewOfFile(cio_shm->file_map, FILE_MAP_WRITE, 0, 0, CIO_SHM_BUFFER_SIZE))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "MapViewOfFile failed", GetLastError()); + goto error; + } + + for (i=0; i < 5; i++) + { + strcpy(shm_suffix, StrEvent[i]); + if (!(cio_shm->event[i]= OpenEvent(dwDesiredAccess, 0, shm_name))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Couldn't create event", GetLastError()); + goto error; + } + } + /* we will first read from server */ + SetEvent(cio_shm->event[CIO_SHM_SERVER_READ]); + +error: + if (hdlConnectRequest) + CloseHandle(hdlConnectRequest); + if (hdlConnectRequestAnswer) + CloseHandle(hdlConnectRequestAnswer); + if (shm_name) + LocalFree(shm_name); + if (map) + UnmapViewOfFile(map); + if (file_map) + CloseHandle(file_map); + if (cio_shm) + { + /* check if all events are set */ + if (cio_shm->event[4]) + { + cio->data= (void *)cio_shm; + cio->mysql= cinfo->mysql; + cio->type= cinfo->type; + cio_shm->read_pos= cio_shm->map; + cio->mysql->net.cio= cio; + return 0; + } + for (i=0;i < 5; i++) + if (cio_shm->event[i]) + CloseHandle(cio_shm->event[i]); + if (cio_shm->map) + UnmapViewOfFile(cio_shm->map); + if (cio_shm->file_map) + CloseHandle(cio_shm->file_map); + LocalFree(cio_shm); } return 1; + } my_bool cio_shm_close(MARIADB_CIO *cio) { - struct st_cio_shm *cpipe= NULL; - int r= 0; + CIO_SHM *cio_shm= (CIO_SHM *)cio->data; + int i; - if (!cio) + if (!cio_shm) return 1; - if (cio->data) - { - cpipe= (struct st_cio_shm *)cio->data; - CloseHandle(cpipe->overlapped.hEvent); - if (cpipe->pipe != INVALID_HANDLE_VALUE) - { - CloseHandle(cpipe->pipe); - cpipe->pipe= INVALID_HANDLE_VALUE; - } - my_free((gptr)cio->data, MYF(0)); - cio->data= NULL; - } - return r; + /* notify server */ + SetEvent(cio_shm->event[CIO_SHM_CONNECTION_CLOSED]); + + UnmapViewOfFile(cio_shm->map); + CloseHandle(cio_shm->file_map); + + for (i=0; i < 5; i++) + CloseHandle(cio_shm->event[i]); + + LocalFree(cio_shm); + cio->data= NULL; + return 0; } my_socket cio_shm_get_socket(MARIADB_CIO *cio) { - if (cio && cio->data) - return (my_socket)((struct st_cio_shm *)cio->data)->pipe; - return INVALID_SOCKET; } my_bool cio_shm_is_blocking(MARIADB_CIO *cio) @@ -306,3 +446,4 @@ my_bool cio_shm_is_blocking(MARIADB_CIO *cio) } #endif + diff --git a/plugins/cio/cio_socket.c b/plugins/cio/cio_socket.c index 485b1c2c..64c229b6 100644 --- a/plugins/cio/cio_socket.c +++ b/plugins/cio/cio_socket.c @@ -101,10 +101,14 @@ struct st_ma_cio_methods cio_socket_methods= { cio_socket_is_alive }; +#ifndef HAVE_SOCKET_DYNAMIC MARIADB_CIO_PLUGIN cio_socket_plugin= +#else +MARIADB_CIO_PLUGIN _mysql_client_plugin_declare_ +#endif { - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + MARIADB_CLIENT_CIO_PLUGIN, + MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, "cio_socket", "Georg Richter", "MariaDB communication IO plugin for socket communication", @@ -112,9 +116,7 @@ MARIADB_CIO_PLUGIN cio_socket_plugin= "LGPL", &cio_socket_init, &cio_socket_end, - &cio_socket_methods, - NULL, - NULL + &cio_socket_methods }; struct st_cio_socket { diff --git a/plugins/io/CMakeLists.txt b/plugins/io/CMakeLists.txt index d5aee9cf..9ffc27c2 100644 --- a/plugins/io/CMakeLists.txt +++ b/plugins/io/CMakeLists.txt @@ -1,17 +1,19 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) -IF(CURL_FOUND) - # remote file plugin - INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) - SET(REMOTE_IO_SOURCES remote_io.c) - ADD_LIBRARY(remote_io SHARED ${REMOTE_IO_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) - TARGET_LINK_LIBRARIES(remote_io ${CURL_LIBRARIES}) +IF(REMOTEIO_PLUGIN_TYPE MATCHES "DYNAMIC") + IF(CURL_FOUND) + # remote file plugin + INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) + SET(REMOTE_IO_SOURCES remote_io.c) + ADD_LIBRARY(remote_io SHARED ${REMOTE_IO_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + TARGET_LINK_LIBRARIES(remote_io ${CURL_LIBRARIES}) - SET_TARGET_PROPERTIES(remote_io PROPERTIES PREFIX "") + SET_TARGET_PROPERTIES(remote_io PROPERTIES PREFIX "") - INSTALL(TARGETS - remote_io - RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" - ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") + INSTALL(TARGETS + remote_io + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") + ENDIF() ENDIF() diff --git a/plugins/io/remote_io.c b/plugins/io/remote_io.c index 2b1f326f..dd9be58d 100644 --- a/plugins/io/remote_io.c +++ b/plugins/io/remote_io.c @@ -89,7 +89,15 @@ typedef struct CURLM *multi_handle= NULL; -mysql_declare_client_plugin(REMOTEIO) +#ifndef HAVE_REMOTE_IO_DYNAMIC +struct st_mysql_client_plugin remote_io_plugin= +{ +#else +struct st_mysql_client_plugin _mysql_client_plugin_declare_ = +#endif +{ + MARIADB_CLIENT_REMOTEIO_PLUGIN, + MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION, "remote_io", "Georg Richter", "Remote IO plugin", diff --git a/plugins/trace/CMakeLists.txt b/plugins/trace/CMakeLists.txt index 124e05e6..7c36f294 100644 --- a/plugins/trace/CMakeLists.txt +++ b/plugins/trace/CMakeLists.txt @@ -1,16 +1,18 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) # Trace example plugin -SET(TRACE_EXAMPLE_SOURCES trace_example.c) -IF(WIN32) - SET(TRACE_EXAMPLE_SOURCES ${TRACE_EXAMPLE_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) +IF(TRACE_EXAMPLE_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_TRACE_EXAMPLE_PLUGIN_DYNAMIC=1) + SET(TRACE_EXAMPLE_SOURCES trace_example.c) + IF(WIN32) + SET(TRACE_EXAMPLE_SOURCES ${TRACE_EXAMPLE_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + ENDIF() + ADD_LIBRARY(trace_example SHARED ${TRACE_EXAMPLE_SOURCES}) + SET_TARGET_PROPERTIES(trace_example PROPERTIES PREFIX "") + + INSTALL(TARGETS + trace_example + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") ENDIF() -ADD_LIBRARY(trace_example SHARED ${TRACE_EXAMPLE_SOURCES}) -SET_TARGET_PROPERTIES(trace_example PROPERTIES PREFIX "") - -INSTALL(TARGETS - trace_example - RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" - ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") - diff --git a/plugins/trace/trace_example.c b/plugins/trace/trace_example.c index 1ce5d1b8..0dd85d44 100644 --- a/plugins/trace/trace_example.c +++ b/plugins/trace/trace_example.c @@ -44,15 +44,22 @@ int (*register_callback)(my_bool register_callback, void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length)); void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length); -mysql_declare_client_plugin(TRACE) +#ifndef HAVE_TRACE_EXAMPLE_PLUGIN_DYNAMIC +struct st_mysql_client_plugin trace_example_plugin= +#else +struct st_mysql_client_plugin _mysql_client_plugin_declare_= +#endif +{ + MARIADB_CLIENT_TRACE_PLUGIN, + MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION, "trace_example", "Georg Richter", "Trace example plugin", {1,0,0}, "LGPL", &trace_init, - &trace_deinit, -mysql_end_client_plugin; + &trace_deinit +}; static char *commands[]= { "MYSQL_COM_SLEEP", @@ -222,7 +229,7 @@ static void trace_set_command(TRACE_INFO *info, char *buffer, size_t size) void dump_buffer(uchar *buffer, size_t len) { - char *p= buffer; + uchar *p= buffer; while (p < buffer + len) { printf("%02x ", *p); @@ -267,7 +274,7 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) * and set thread_id */ if (!thread_id && mode == READ) { - char *p= buffer; + char *p= (char *)buffer; p+= 4; /* packet length */ if (*p != 0xFF) /* protocol version 0xFF indicates error */ { @@ -279,7 +286,7 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) } else { - char *p= buffer; + char *p= (char *)buffer; info= get_trace_info(thread_id); if (info->last_command == -1) @@ -300,7 +307,6 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) * 36 username (zero terminated) * len (1 byte) + password or */ - int len; p+= 4; info->client_flags= uint4korr(p); @@ -329,7 +335,7 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) } } else { - char *p= buffer; + char *p= (char *)buffer; int len; if (mode == WRITE) diff --git a/plugins/trace/trace_example.so b/plugins/trace/trace_example.so index c26ca8261e24bde91e467fe4e5d004be123b6773..91fec60a1744d99194e31b041ca637931ec19857 100755 GIT binary patch literal 36957 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 E8-ApIdjJ3c literal 43254 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 Date: Sun, 4 Oct 2015 08:58:27 +0200 Subject: [PATCH 13/18] removed shared object file (likely added by git add directory before) --- plugins/trace/trace_example.so | Bin 36957 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 plugins/trace/trace_example.so diff --git a/plugins/trace/trace_example.so b/plugins/trace/trace_example.so deleted file mode 100755 index 91fec60a1744d99194e31b041ca637931ec19857..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36957 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 E8-ApIdjJ3c From ba301da12775b6935e20fb4b731c57cb8344085d Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 5 Oct 2015 08:06:48 +0200 Subject: [PATCH 14/18] Fixed windows build --- plugins/cio/cio_npipe.c | 4 +--- plugins/cio/cio_shmem.c | 3 +-- plugins/connection/replication.c | 9 ++++++++- plugins/trace/trace_example.c | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/plugins/cio/cio_npipe.c b/plugins/cio/cio_npipe.c index 1a644290..cb4de1a8 100644 --- a/plugins/cio/cio_npipe.c +++ b/plugins/cio/cio_npipe.c @@ -82,9 +82,7 @@ MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = "LGPL", NULL, NULL, - &cio_npipe_methods, - NULL, - NULL + &cio_npipe_methods }; struct st_cio_npipe { diff --git a/plugins/cio/cio_shmem.c b/plugins/cio/cio_shmem.c index 6c02ede0..d632713f 100644 --- a/plugins/cio/cio_shmem.c +++ b/plugins/cio/cio_shmem.c @@ -84,8 +84,7 @@ MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_= NULL, NULL, &cio_shm_methods, - NULL, - NULL + }; enum enum_shm_events diff --git a/plugins/connection/replication.c b/plugins/connection/replication.c index 2caaec84..7a21bf1e 100644 --- a/plugins/connection/replication.c +++ b/plugins/connection/replication.c @@ -29,7 +29,10 @@ #include #include #include + +#ifndef WIN32 #include +#endif /* function prototypes */ MYSQL *repl_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, @@ -129,10 +132,14 @@ my_bool repl_parse_url(const char *url, REPL_DATA *data) data->host[MARIADB_SLAVE]= slaves[0]; else { - struct timeval tp; int random_nr; +#ifndef WIN32 + struct timeval tp; gettimeofday(&tp,NULL); srand(tp.tv_usec / 1000 + tp.tv_sec * 1000); +#else + srand(GetTickCount()); +#endif random_nr= rand() % num_slaves; data->host[MARIADB_SLAVE]= slaves[random_nr]; diff --git a/plugins/trace/trace_example.c b/plugins/trace/trace_example.c index 0dd85d44..a8fc75ef 100644 --- a/plugins/trace/trace_example.c +++ b/plugins/trace/trace_example.c @@ -47,7 +47,7 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length); #ifndef HAVE_TRACE_EXAMPLE_PLUGIN_DYNAMIC struct st_mysql_client_plugin trace_example_plugin= #else -struct st_mysql_client_plugin _mysql_client_plugin_declare_= +struct st_mysql_client_plugin _mysql_client_plugin_declaration_ = #endif { MARIADB_CLIENT_TRACE_PLUGIN, From 76996c3c5d21ca8821d3acf65f47395c73b36576 Mon Sep 17 00:00:00 2001 From: Lawrin Novitsky Date: Thu, 8 Oct 2015 13:36:22 +0300 Subject: [PATCH 15/18] Fix for mariadb_convert_string function. Fix of the the charset name in case of Utf16 - iconv digests UTF-16, and not UTF16, as destination charset. Also we always use either BE or LE for utf16, to avoid BOM in the result string. --- libmariadb/my_charset.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libmariadb/my_charset.c b/libmariadb/my_charset.c index 79ac4665..5d37b518 100644 --- a/libmariadb/my_charset.c +++ b/libmariadb/my_charset.c @@ -1138,14 +1138,18 @@ size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, CHARSE *errorcode= EINVAL; return rc; } - snprintf(to_encoding, 128, "%s//TRANSLIT", to_cs->encoding); + /* UTF16 does not work form iconv, while UTF-16 does. + Besides we don't want iconv to generate BOM, thus we used either UTF-16LE or BE by default + TODO: Need to do the same for UTF-32(at leased re BOM) */ + snprintf(to_encoding, 128, "%s//TRANSLIT", strncmp(to_cs->encoding, "UTF16", 5) == 0 + ? (strcmp(to_cs->encoding + 5, "LE") == 0 ? "UTF-16LE" : "UTF-16BE") + : to_cs->encoding); if ((conv= iconv_open(to_encoding, from_cs->encoding)) == (iconv_t)-1) { *errorcode= errno; goto error; } - if ((rc= iconv(conv, (char **)&from, from_len, &to, to_len)) == -1) { *errorcode= errno; From 1d80a7fc6cda7f69127faa96b15dc968d48073c5 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sun, 18 Oct 2015 11:47:33 +0200 Subject: [PATCH 16/18] Added mariadb_client_plugin_info which displays information and configuration of client plugins Following options are supported: -a all plugins -b builtin plugins -d dynamic plugins -p plugin only specified plugin --- CMakeLists.txt | 2 + client/CMakeLists.txt | 2 + client/ma_plugin_info.c | 140 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 client/CMakeLists.txt create mode 100644 client/ma_plugin_info.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 10cd1bda..d5cd0231 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,6 +221,8 @@ IF(NOT WIN32) ADD_SUBDIRECTORY(mariadb_config) ENDIF() +ADD_SUBDIRECTORY(client) + IF(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/unittest) ADD_SUBDIRECTORY(unittest/mytap) ADD_SUBDIRECTORY(unittest/libmariadb) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 00000000..0afd8205 --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,2 @@ +ADD_EXECUTABLE(mariadb_client_plugin_info ma_plugin_info.c) +TARGET_LINK_LIBRARIES(mariadb_client_plugin_info mariadbclient) diff --git a/client/ma_plugin_info.c b/client/ma_plugin_info.c new file mode 100644 index 00000000..f6fb317c --- /dev/null +++ b/client/ma_plugin_info.c @@ -0,0 +1,140 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct option long_options[]= +{ + {"builtin", no_argument, 0, 'b'}, + {"dynamic", no_argument, 0, 'd'}, + {"all", no_argument, 0, 'a'}, + {"plugin", 1, 0, 'p'}, + {NULL, 0, 0, 0} +}; + +void usage(void) +{ + int i=0; + puts("Copyright 2015 MariaDB Corporation AB"); + puts("Show client plugin information for MariaDB Connector/C."); + printf("Usage: %s [OPTIONS] [plugin_name]\n", my_progname); + while (long_options[i].name) + { + printf(" --%-12s -%c\n", long_options[i].name, long_options[i].val); + i++; + } +} + +void show_plugin_info(struct st_mysql_client_plugin *plugin, my_bool builtin) +{ + printf("Type: %s\n", builtin ? "builtin" : "dynamic"); + printf("Name: %s\n", plugin->name); + printf("Desc: %s\n", plugin->desc); + printf("Author: %s\n", plugin->author); + printf("License: %s\n", plugin->license); + printf("Version: %d.%d.%d\n", plugin->version[0], plugin->version[1], plugin->version[2]); + printf("API Version: 0x%04X\n", plugin->interface_version); + printf("\n"); +} + +void show_builtin() +{ + struct st_mysql_client_plugin **builtin; + + for (builtin= mysql_client_builtins; *builtin; builtin++) + show_plugin_info(*builtin, TRUE); +} + +void show_file(char *filename) +{ + char dlpath[FN_REFLEN+1]; + void *sym, *dlhandle; + struct st_mysql_client_plugin *plugin; + char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR"); + +#ifdef _WIN32 + if (!strchr(filename, '\\')) +#else + if (!strchr(filename, '/')) +#endif + strxnmov(dlpath, sizeof(dlpath) - 1, + (env_plugin_dir) ? env_plugin_dir : PLUGINDIR, "/", filename, SO_EXT, NullS); + else + strcpy(dlpath, filename); + if ((dlhandle= dlopen((const char *)dlpath, RTLD_NOW))) + { + if (sym= dlsym(dlhandle, plugin_declarations_sym)) + { + plugin= (struct st_mysql_client_plugin *)sym; + show_plugin_info(plugin, 0); + } + dlclose(dlhandle); + } +} + +void show_dynamic() +{ + MY_DIR *dir= NULL; + int i; + char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR"); + + dir= my_dir(env_plugin_dir ? env_plugin_dir : PLUGINDIR, 0); + + if (!dir->number_off_files) + { + printf("No plugins found in %s\n", env_plugin_dir ? env_plugin_dir : PLUGINDIR); + return; + } + + for (i=0; i < dir->number_off_files; i++) + { + char *p= strstr(dir->dir_entry[i].name, SO_EXT); + if (p) + show_file(dir->dir_entry[i].name); + } + if (dir) + my_dirend(dir); +} + +int main(int argc, char *argv[]) +{ + int option_index= 0; + my_progname= argv[0]; + char c; + + if (argc <= 1) + { + usage(); + exit(1); + } + + c= getopt_long(argc, argv, "bdap", long_options, &option_index); + + switch(c) { + case 'a': /* builtin */ + show_builtin(); + show_dynamic(); + break; + case 'b': /* builtin */ + show_builtin(); + break; + case 'd': /* builtin */ + show_dynamic(); + break; + case 'p': + if (argc > 2) + { + show_file(argv[2]); + break; + } + default: + usage(); + exit(1); + } + +} From 13c79e2263b08e1a2a83dfa33bad7db3bb9b9e3a Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sun, 18 Oct 2015 13:06:41 +0200 Subject: [PATCH 17/18] Added type information for plugin tool --- client/ma_plugin_info.c | 40 +++++++++++++++++++++++++++++------ include/mysql/client_plugin.h | 2 ++ libmariadb/client_plugin.c.in | 3 --- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/client/ma_plugin_info.c b/client/ma_plugin_info.c index f6fb317c..1594549c 100644 --- a/client/ma_plugin_info.c +++ b/client/ma_plugin_info.c @@ -17,6 +17,22 @@ static struct option long_options[]= {NULL, 0, 0, 0} }; +struct st_plugin_type +{ + int type; + char *typename; +}; + +static struct st_plugin_type plugin_types[]= +{ + {MYSQL_CLIENT_AUTHENTICATION_PLUGIN, "authentication"}, + {MARIADB_CLIENT_CIO_PLUGIN, "client/server protocol"}, + {MARIADB_CLIENT_TRACE_PLUGIN, "trace"}, + {MARIADB_CLIENT_REMOTEIO_PLUGIN, "remote file access"}, + {MARIADB_CLIENT_CONNECTION_PLUGIN, "connection handler"}, + {0, "unknown"} +}; + void usage(void) { int i=0; @@ -30,15 +46,28 @@ void usage(void) } } +char *get_type_name(int type) +{ + int i=0; + while (plugin_types[i].type) + { + if (type== plugin_types[i].type) + return plugin_types[i].typename; + i++; + } + return plugin_types[i].typename; +} + void show_plugin_info(struct st_mysql_client_plugin *plugin, my_bool builtin) { - printf("Type: %s\n", builtin ? "builtin" : "dynamic"); + printf("Type: %s\n", get_type_name(plugin->type)); printf("Name: %s\n", plugin->name); printf("Desc: %s\n", plugin->desc); printf("Author: %s\n", plugin->author); printf("License: %s\n", plugin->license); printf("Version: %d.%d.%d\n", plugin->version[0], plugin->version[1], plugin->version[2]); printf("API Version: 0x%04X\n", plugin->interface_version); + printf("Build type: %s\n", builtin ? "builtin" : "dynamic"); printf("\n"); } @@ -56,14 +85,11 @@ void show_file(char *filename) void *sym, *dlhandle; struct st_mysql_client_plugin *plugin; char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR"); + char *has_so_ext= strstr(filename, SO_EXT); -#ifdef _WIN32 - if (!strchr(filename, '\\')) -#else - if (!strchr(filename, '/')) -#endif + if (!strchr(filename, FN_LIBCHAR)) strxnmov(dlpath, sizeof(dlpath) - 1, - (env_plugin_dir) ? env_plugin_dir : PLUGINDIR, "/", filename, SO_EXT, NullS); + (env_plugin_dir) ? env_plugin_dir : PLUGINDIR, "/", filename, has_so_ext ? "" : SO_EXT, NullS); else strcpy(dlpath, filename); if ((dlhandle= dlopen((const char *)dlpath, RTLD_NOW))) diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index ed386b72..f79388cf 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -36,6 +36,8 @@ #define PLUGINDIR "lib/plugin" #endif +#define plugin_declarations_sym "_mysql_client_plugin_declaration_" + /* known plugin types */ #define MYSQL_CLIENT_PLUGIN_RESERVED 0 #define MYSQL_CLIENT_PLUGIN_RESERVED2 1 diff --git a/libmariadb/client_plugin.c.in b/libmariadb/client_plugin.c.in index 88bf0c5f..bbb3df51 100644 --- a/libmariadb/client_plugin.c.in +++ b/libmariadb/client_plugin.c.in @@ -56,9 +56,6 @@ struct st_client_plugin_int { static my_bool initialized= 0; static MEM_ROOT mem_root; -#define plugin_declarations_sym "_mysql_client_plugin_declaration_" - - static uint valid_plugins[][2]= { {MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION}, {MARIADB_CLIENT_CIO_PLUGIN, MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION}, From 3a6e2fd16d660143e60bedbc9907e95a7d7ae4bb Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sun, 18 Oct 2015 13:48:33 +0200 Subject: [PATCH 18/18] Windows build fixes --- client/ma_plugin_info.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/client/ma_plugin_info.c b/client/ma_plugin_info.c index 1594549c..c2b7768d 100644 --- a/client/ma_plugin_info.c +++ b/client/ma_plugin_info.c @@ -33,7 +33,7 @@ static struct st_plugin_type plugin_types[]= {0, "unknown"} }; -void usage(void) +static void usage(void) { int i=0; puts("Copyright 2015 MariaDB Corporation AB"); @@ -46,7 +46,7 @@ void usage(void) } } -char *get_type_name(int type) +static char *get_type_name(int type) { int i=0; while (plugin_types[i].type) @@ -58,7 +58,7 @@ char *get_type_name(int type) return plugin_types[i].typename; } -void show_plugin_info(struct st_mysql_client_plugin *plugin, my_bool builtin) +static void show_plugin_info(struct st_mysql_client_plugin *plugin, my_bool builtin) { printf("Type: %s\n", get_type_name(plugin->type)); printf("Name: %s\n", plugin->name); @@ -71,7 +71,7 @@ void show_plugin_info(struct st_mysql_client_plugin *plugin, my_bool builtin) printf("\n"); } -void show_builtin() +static void show_builtin() { struct st_mysql_client_plugin **builtin; @@ -79,7 +79,7 @@ void show_builtin() show_plugin_info(*builtin, TRUE); } -void show_file(char *filename) +static void show_file(char *filename) { char dlpath[FN_REFLEN+1]; void *sym, *dlhandle; @@ -103,7 +103,7 @@ void show_file(char *filename) } } -void show_dynamic() +static void show_dynamic() { MY_DIR *dir= NULL; int i; @@ -130,8 +130,8 @@ void show_dynamic() int main(int argc, char *argv[]) { int option_index= 0; + int c; my_progname= argv[0]; - char c; if (argc <= 1) { @@ -142,14 +142,14 @@ int main(int argc, char *argv[]) c= getopt_long(argc, argv, "bdap", long_options, &option_index); switch(c) { - case 'a': /* builtin */ + case 'a': /* all */ show_builtin(); show_dynamic(); break; case 'b': /* builtin */ show_builtin(); break; - case 'd': /* builtin */ + case 'd': /* dynamic */ show_dynamic(); break; case 'p':