diff --git a/CMakeLists.txt b/CMakeLists.txt index 232ecc09d..9ede41848 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 33f5458cf..cdd331629 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 7be559911..a340f1639 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 5186bf2b8..c0d594bb9 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 671172f96..4710f6444 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 f94a6f265..0b234a48a 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 7b2e43788..272df9260 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 26949a716..892b9dfe9 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 657ed62fd..bb22230ba 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; -#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) +#define MAX_SSL_ERR_LEN 100 + +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 @@ int cio_gnutls_start(char *errmsg, size_t errmsg_len, int count, va_list list) 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 f6e836f30..18a140c6b 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 @@ int cio_openssl_start(char *errmsg, size_t errmsg_len, int count, va_list list) 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 @@ void *cio_openssl_init(MARIADB_SSL *cssl, MYSQL *mysql) 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 2c8d3510d..9b9792ef3 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 7fa688e57..e9ad64399 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 70deedbff..ca2825872 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 5694ab08e..e92d7a540 100755 Binary files a/plugins/trace/trace_example.so and b/plugins/trace/trace_example.so differ diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index 73061ecac..a7990e705 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 0ba3dd25b..4866796cd 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; }