From e58148356dc96389051b5824a92b498a8f4ae123 Mon Sep 17 00:00:00 2001 From: counterpoint Date: Tue, 26 Jan 2016 16:08:02 +0000 Subject: [PATCH] Split off listener functionality and move SSL initiation from service to listener. Put GWPROTOCOL in its own header file. --- server/core/CMakeLists.txt | 2 +- server/core/config.c | 215 +++++++++++++----- server/core/listener.c | 276 ++++++++++++++++++++++++ server/core/service.c | 43 ++-- server/core/test/testservice.c | 6 +- server/include/dcb.h | 50 +---- server/include/gw_protocol.h | 92 ++++++++ server/include/listener.h | 89 ++++++++ server/include/service.h | 34 +-- server/modules/protocol/httpd.c | 1 + server/modules/protocol/maxscaled.c | 1 + server/modules/protocol/mysql_backend.c | 1 + server/modules/protocol/mysql_client.c | 8 +- server/modules/protocol/telnetd.c | 1 + server/modules/protocol/testprotocol.c | 1 + 15 files changed, 663 insertions(+), 157 deletions(-) create mode 100644 server/core/listener.c create mode 100644 server/include/gw_protocol.h create mode 100644 server/include/listener.h diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 7d7a03630..26f4123ab 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(maxscale-common SHARED adminusers.c atomic.c buffer.c config.c dbusers.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c maxscale_pcre2.c memlog.c modutil.c monitor.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc ${CMAKE_SOURCE_DIR}/utils/skygw_utils.cc) +add_library(maxscale-common SHARED adminusers.c atomic.c buffer.c config.c dbusers.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c gw_utils.c hashtable.c hint.c housekeeper.c listener.c load_utils.c maxscale_pcre2.c memlog.c modutil.c monitor.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc ${CMAKE_SOURCE_DIR}/utils/skygw_utils.cc) target_link_libraries(maxscale-common ${EMBEDDED_LIB} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++) diff --git a/server/core/config.c b/server/core/config.c index 5bbcc69c6..ee4794e3c 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -43,7 +43,8 @@ * 20/02/15 Markus Mäkelä Added connection_timeout parameter for services * 05/03/15 Massimiliano Pinto Added notification_feedback support * 20/04/15 Guillaume Lefranc Added available_when_donor parameter - * 22/04/15 Martin Brampton Added disable_master_role_setting parameter + * 22/04/15 Martin Brampton Added disable_master_role_setting parameter + * 26/01/16 Martin Brampton Transfer SSL processing to listener * * @endverbatim */ @@ -90,6 +91,8 @@ static void global_defaults(); static void feedback_defaults(); static void check_config_objects(CONFIG_CONTEXT *context); static int maxscale_getline(char** dest, int* size, FILE* file); +static SSL_LISTENER *make_ssl_structure(CONFIG_CONTEXT *obj); + int config_truth_value(char *str); int config_get_ifaddr(unsigned char *output); int config_get_release_string(char* release); @@ -99,7 +102,7 @@ bool config_has_duplicate_sections(const char* config); int create_new_service(CONFIG_CONTEXT *obj); int create_new_server(CONFIG_CONTEXT *obj); int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE* monitorhash); -int create_new_listener(CONFIG_CONTEXT *obj); +int create_new_listener(CONFIG_CONTEXT *obj, bool startnow); int create_new_filter(CONFIG_CONTEXT *obj); int configure_new_service(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj); @@ -150,6 +153,13 @@ static char *listener_params[] = "port", "address", "socket", + "authenticator", + "ssl_cert", + "ssl_ca_cert", + "ssl", + "ssl_key", + "ssl_version", + "ssl_cert_verify_depth", NULL }; @@ -585,7 +595,7 @@ process_config_context(CONFIG_CONTEXT *context) } else if (!strcmp(type, "listener")) { - error_count += create_new_listener(obj); + error_count += create_new_listener(obj, false); } else if (!strcmp(type, "monitor")) { @@ -909,6 +919,7 @@ static struct { "log_info", LOG_INFO, NULL }, { NULL, 0 } }; + /** * Configuration handler for items in the global [MaxScale] section * @@ -1022,6 +1033,118 @@ handle_global_item(const char *name, const char *value) return 1; } +/** + * Form an SSL structure from listener section parameters + * + * @param name The item name + * @param value The item value + * @return 0 on error + */ +static SSL_LISTENER * +make_ssl_structure (CONFIG_CONTEXT *obj) +{ + char *ssl, *ssl_version, *ssl_cert, *ssl_key, *ssl_ca_cert, *ssl_cert_verify_depth; + SSL_LISTENER *new_ssl; + int error_count = 0; + + ssl = config_get_value(obj->parameters, "ssl"); + if (ssl && !strcmp(ssl, "required")) + { + if ((new_ssl = malloc(sizeof(SSL_LISTENER))) == NULL) + { + return NULL; + } + new_ssl->ssl_method_type = SERVICE_SSL_TLS_MAX; + ssl_cert = config_get_value(obj->parameters, "ssl_cert"); + ssl_key = config_get_value(obj->parameters, "ssl_key"); + ssl_ca_cert = config_get_value(obj->parameters, "ssl_ca_cert"); + ssl_version = config_get_value(obj->parameters, "ssl_version"); + ssl_cert_verify_depth = config_get_value(obj->parameters, "ssl_cert_verify_depth"); + new_ssl->ssl_init_done = false; + + if (ssl_version) + { + if (listener_set_ssl_version(new_ssl, ssl_version) != 0) + { + MXS_ERROR("Unknown parameter value for 'ssl_version' for" + " service '%s': %s", obj->object, ssl_version); + error_count++; + } + } + + if (ssl_cert_verify_depth) + { + new_ssl->ssl_cert_verify_depth = atoi(ssl_cert_verify_depth); + if (new_ssl->ssl_cert_verify_depth < 0) + { + MXS_ERROR("Invalid parameter value for 'ssl_cert_verify_depth" + " for service '%s': %s", obj->object, ssl_cert_verify_depth); + new_ssl->ssl_cert_verify_depth = 0; + error_count++; + } + } + + listener_set_certificates(new_ssl, ssl_cert, ssl_key, ssl_ca_cert); + + if (new_ssl->ssl_cert == NULL) + { + error_count++; + MXS_ERROR("Server certificate missing for service '%s'." + "Please provide the path to the server certificate by adding " + "the ssl_cert= parameter", obj->object); + } + + if (new_ssl->ssl_ca_cert == NULL) + { + error_count++; + MXS_ERROR("CA Certificate missing for service '%s'." + "Please provide the path to the certificate authority " + "certificate by adding the ssl_ca_cert= parameter", + obj->object); + } + + if (new_ssl->ssl_key == NULL) + { + error_count++; + MXS_ERROR("Server private key missing for service '%s'. " + "Please provide the path to the server certificate key by " + "adding the ssl_key= parameter", + obj->object); + } + + if (access(new_ssl->ssl_ca_cert, F_OK) != 0) + { + MXS_ERROR("Certificate authority file for service '%s' not found: %s", + obj->object, + new_ssl->ssl_ca_cert); + error_count++; + } + + if (access(new_ssl->ssl_cert, F_OK) != 0) + { + MXS_ERROR("Server certificate file for service '%s' not found: %s", + obj->object, + new_ssl->ssl_cert); + error_count++; + } + + if (access(new_ssl->ssl_key, F_OK) != 0) + { + MXS_ERROR("Server private key file for service '%s' not found: %s", + obj->object, + new_ssl->ssl_key); + error_count++; + } + + if (error_count == 0) + { + return new_ssl; + } + free(new_ssl); + } + return NULL; +} + /** * Configuration handler for items in the feedback [feedback] section * @@ -1386,52 +1509,7 @@ process_config_update(CONFIG_CONTEXT *context) } else if (!strcmp(type, "listener")) { - char *service; - char *port; - char *protocol; - char *address; - char *socket; - - service = config_get_value(obj->parameters, "service"); - address = config_get_value(obj->parameters, "address"); - port = config_get_value(obj->parameters, "port"); - protocol = config_get_value(obj->parameters, "protocol"); - socket = config_get_value(obj->parameters, "socket"); - - if (service && socket && protocol) - { - CONFIG_CONTEXT *ptr = context; - while (ptr && strcmp(ptr->object, service) != 0) - { - ptr = ptr->next; - } - - if (ptr && - ptr->element && - serviceHasProtocol(ptr->element, protocol, 0) == 0) - { - serviceAddProtocol(ptr->element, protocol, socket, 0); - serviceStartProtocol(ptr->element, protocol, 0); - } - } - - if (service && port && protocol) - { - CONFIG_CONTEXT *ptr = context; - - while (ptr && strcmp(ptr->object, service) != 0) - { - ptr = ptr->next; - } - - if (ptr && - ptr->element && - serviceHasProtocol(ptr->element, protocol, atoi(port)) == 0) - { - serviceAddProtocol(ptr->element, protocol, address, atoi(port)); - serviceStartProtocol(ptr->element, protocol, atoi(port)); - } - } + create_new_listener(obj, true); } else if (strcmp(type, "server") != 0 && strcmp(type, "monitor") != 0 && @@ -2633,9 +2711,10 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE* /** * Create a new listener for a service * @param obj Listener configuration context + * @param startnow If true, start the listener now * @return Number of errors */ -int create_new_listener(CONFIG_CONTEXT *obj) +int create_new_listener(CONFIG_CONTEXT *obj, bool startnow) { int error_count = 0; char *service_name = config_get_value(obj->parameters, "service"); @@ -2643,20 +2722,52 @@ int create_new_listener(CONFIG_CONTEXT *obj) char *address = config_get_value(obj->parameters, "address"); char *protocol = config_get_value(obj->parameters, "protocol"); char *socket = config_get_value(obj->parameters, "socket"); + char *authenticator = config_get_value(obj->parameters, "authenticator"); if (service_name && protocol && (socket || port)) { SERVICE *service = service_find(service_name); if (service) { + SSL_LISTENER *ssl_info = make_ssl_structure(obj); if (socket) { - serviceAddProtocol(service, protocol, socket, 0); + if (serviceHasProtocol(service, protocol, 0)) + { + MXS_ERROR("Listener '%s', for service '%s', socket %s, already have socket.", + obj->object, + service_name, + socket); + error_count++; + } + else + { + serviceAddProtocol(service, protocol, socket, 0, authenticator, ssl_info); + if (startnow) + { + serviceStartProtocol(service, protocol, 0); + } + } } if (port) { - serviceAddProtocol(service, protocol, address, atoi(port)); + if (serviceHasProtocol(service, protocol, atoi(port))) + { + MXS_ERROR("Listener '%s', for service '%s', already have port %s.", + obj->object, + service_name, + port); + error_count++; + } + else + { + serviceAddProtocol(service, protocol, address, atoi(port), authenticator, ssl_info); + if (startnow) + { + serviceStartProtocol(service, protocol, atoi(port)); + } + } } } else diff --git a/server/core/listener.c b/server/core/listener.c new file mode 100644 index 000000000..015f47272 --- /dev/null +++ b/server/core/listener.c @@ -0,0 +1,276 @@ +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It 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. + * + * 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 General Public License for more + * details. + * + * 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +/** + * @file listener.c - Listener generic functions + * + * Listeners wait for new client connections and, if the connection is successful + * a new session is created. A listener typically knows about a port or a socket, + * and a few other things. It may know about SSL if it is expecting an SSL + * connection. + * + * @verbatim + * Revision History + * + * Date Who Description + * 26/01/16 Martin Brampton Initial implementation + * + * @endverbatim + */ +#include +#include +#include +#include +#include +#include + +static RSA *rsa_512 = NULL; +static RSA *rsa_1024 = NULL; + +static RSA *tmp_rsa_callback(SSL *s, int is_export, int keylength); + +/** + * Set the maximum SSL/TLS version the listener will support + * @param ssl_listener Listener data to configure + * @param version SSL/TLS version string + * @return 0 on success, -1 on invalid version string + */ +int +listener_set_ssl_version(SSL_LISTENER *ssl_listener, char* version) +{ + if (strcasecmp(version,"SSLV3") == 0) + { + ssl_listener->ssl_method_type = SERVICE_SSLV3; + } + else if (strcasecmp(version,"TLSV10") == 0) + { + ssl_listener->ssl_method_type = SERVICE_TLS10; + } +#ifdef OPENSSL_1_0 + else if (strcasecmp(version,"TLSV11") == 0) + { + ssl_listener->ssl_method_type = SERVICE_TLS11; + } + else if (strcasecmp(version,"TLSV12") == 0) + { + ssl_listener->ssl_method_type = SERVICE_TLS12; + } +#endif + else if (strcasecmp(version,"MAX") == 0) + { + ssl_listener->ssl_method_type = SERVICE_SSL_TLS_MAX; + } + else + { + return -1; + } + return 0; +} + +/** + * Set the locations of the listener's SSL certificate, listener's private key + * and the CA certificate which both the client and the listener should trust. + * @param ssl_listener Listener data to configure + * @param cert SSL certificate + * @param key SSL private key + * @param ca_cert SSL CA certificate + */ +void +listener_set_certificates(SSL_LISTENER *ssl_listener, char* cert, char* key, char* ca_cert) +{ + if (ssl_listener->ssl_cert) + { + free(ssl_listener->ssl_cert); + } + ssl_listener->ssl_cert = strdup(cert); + + if (ssl_listener->ssl_key) + { + free(ssl_listener->ssl_key); + } + ssl_listener->ssl_key = strdup(key); + + if (ssl_listener->ssl_ca_cert) + { + free(ssl_listener->ssl_ca_cert); + } + ssl_listener->ssl_ca_cert = strdup(ca_cert); +} + +/** + * Initialize the listener's SSL context. This sets up the generated RSA + * encryption keys, chooses the listener encryption level and configures the + * listener certificate, private key and certificate authority file. + * @param ssl_listener Listener data to initialize + * @return 0 on success, -1 on error + */ +int +listener_init_SSL(SSL_LISTENER *ssl_listener) +{ + DH* dh; + RSA* rsa; + + if (!ssl_listener->ssl_init_done) + { + switch(ssl_listener->ssl_method_type) + { + case SERVICE_SSLV3: + ssl_listener->method = (SSL_METHOD*)SSLv3_server_method(); + break; + case SERVICE_TLS10: + ssl_listener->method = (SSL_METHOD*)TLSv1_server_method(); + break; +#ifdef OPENSSL_1_0 + case SERVICE_TLS11: + ssl_listener->method = (SSL_METHOD*)TLSv1_1_server_method(); + break; + case SERVICE_TLS12: + ssl_listener->method = (SSL_METHOD*)TLSv1_2_server_method(); + break; +#endif + /** Rest of these use the maximum available SSL/TLS methods */ + case SERVICE_SSL_MAX: + ssl_listener->method = (SSL_METHOD*)SSLv23_server_method(); + break; + case SERVICE_TLS_MAX: + ssl_listener->method = (SSL_METHOD*)SSLv23_server_method(); + break; + case SERVICE_SSL_TLS_MAX: + ssl_listener->method = (SSL_METHOD*)SSLv23_server_method(); + break; + default: + ssl_listener->method = (SSL_METHOD*)SSLv23_server_method(); + break; + } + + if ((ssl_listener->ctx = SSL_CTX_new(ssl_listener->method)) == NULL) + { + MXS_ERROR("SSL context initialization failed."); + return -1; + } + + /** Enable all OpenSSL bug fixes */ + SSL_CTX_set_options(ssl_listener->ctx,SSL_OP_ALL); + + /** Generate the 512-bit and 1024-bit RSA keys */ + if (rsa_512 == NULL) + { + rsa_512 = RSA_generate_key(512,RSA_F4,NULL,NULL); + if (rsa_512 == NULL) + { + MXS_ERROR("512-bit RSA key generation failed."); + return -1; + } + } + if (rsa_1024 == NULL) + { + rsa_1024 = RSA_generate_key(1024,RSA_F4,NULL,NULL); + if (rsa_1024 == NULL) + { + MXS_ERROR("1024-bit RSA key generation failed."); + return -1; + } + } + + if (rsa_512 != NULL && rsa_1024 != NULL) + { + SSL_CTX_set_tmp_rsa_callback(ssl_listener->ctx,tmp_rsa_callback); + } + + /** Load the server certificate */ + if (SSL_CTX_use_certificate_file(ssl_listener->ctx, ssl_listener->ssl_cert, SSL_FILETYPE_PEM) <= 0) + { + MXS_ERROR("Failed to set server SSL certificate."); + return -1; + } + + /* Load the private-key corresponding to the server certificate */ + if (SSL_CTX_use_PrivateKey_file(ssl_listener->ctx, ssl_listener->ssl_key, SSL_FILETYPE_PEM) <= 0) + { + MXS_ERROR("Failed to set server SSL key."); + return -1; + } + + /* Check if the server certificate and private-key matches */ + if (!SSL_CTX_check_private_key(ssl_listener->ctx)) + { + MXS_ERROR("Server SSL certificate and key do not match."); + return -1; + } + + /* Load the RSA CA certificate into the SSL_CTX structure */ + if (!SSL_CTX_load_verify_locations(ssl_listener->ctx, ssl_listener->ssl_ca_cert, NULL)) + { + MXS_ERROR("Failed to set Certificate Authority file."); + return -1; + } + + /* Set to require peer (client) certificate verification */ + SSL_CTX_set_verify(ssl_listener->ctx,SSL_VERIFY_PEER,NULL); + + /* Set the verification depth */ + SSL_CTX_set_verify_depth(ssl_listener->ctx,ssl_listener->ssl_cert_verify_depth); + ssl_listener->ssl_init_done = true; + } + return 0; +} + +/** + * The RSA key generation callback function for OpenSSL. + * @param s SSL structure + * @param is_export Not used + * @param keylength Length of the key + * @return Pointer to RSA structure + */ +static RSA * +tmp_rsa_callback(SSL *s, int is_export, int keylength) +{ + RSA *rsa_tmp=NULL; + + switch (keylength) { + case 512: + if (rsa_512) + { + rsa_tmp = rsa_512; + } + else + { + /* generate on the fly, should not happen in this example */ + rsa_tmp = RSA_generate_key(keylength,RSA_F4,NULL,NULL); + rsa_512 = rsa_tmp; /* Remember for later reuse */ + } + break; + case 1024: + if (rsa_1024) + { + rsa_tmp=rsa_1024; + } + break; + default: + /* Generating a key on the fly is very costly, so use what is there */ + if (rsa_1024) + { + rsa_tmp=rsa_1024; + } + else + { + rsa_tmp=rsa_512; /* Use at least a shorter key */ + } + } + return(rsa_tmp); +} diff --git a/server/core/service.c b/server/core/service.c index b0241191b..f4030e5d2 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include #include @@ -218,7 +220,7 @@ service_isvalid(SERVICE *service) * @return The number of listeners started */ static int -serviceStartPort(SERVICE *service, SERV_PROTOCOL *port) +serviceStartPort(SERVICE *service, SERV_LISTENER *port) { int listeners = 0; char config_bind[40]; @@ -232,6 +234,11 @@ serviceStartPort(SERVICE *service, SERV_PROTOCOL *port) goto retblock; } + if (port->ssl) + { + listener_init_SSL(port->ssl); + } + if (strcmp(port->protocol, "MySQLClient") == 0) { int loaded; @@ -420,7 +427,7 @@ retblock: */ int serviceStartAllPorts(SERVICE* service) { - SERV_PROTOCOL *port = service->ports; + SERV_LISTENER *port = service->ports; int listeners = 0; while (!service->svc_do_shutdown && port) { @@ -507,7 +514,7 @@ serviceStart(SERVICE *service) void serviceStartProtocol(SERVICE *service, char *protocol, int port) { - SERV_PROTOCOL *ptr; + SERV_LISTENER *ptr; ptr = service->ports; while (ptr) @@ -560,7 +567,7 @@ serviceStartAll() int serviceStop(SERVICE *service) { - SERV_PROTOCOL *port; + SERV_LISTENER *port; int listeners = 0; port = service->ports; @@ -592,7 +599,7 @@ serviceStop(SERVICE *service) int serviceRestart(SERVICE *service) { - SERV_PROTOCOL *port; + SERV_LISTENER *port; int listeners = 0; port = service->ports; @@ -686,25 +693,20 @@ service_free(SERVICE *service) * @return TRUE if the protocol/port could be added */ int -serviceAddProtocol(SERVICE *service, char *protocol, char *address, unsigned short port) +serviceAddProtocol(SERVICE *service, char *protocol, char *address, unsigned short port, char *authenticator, SSL_LISTENER *ssl) { - SERV_PROTOCOL *proto; + SERV_LISTENER *proto; - if ((proto = (SERV_PROTOCOL *)malloc(sizeof(SERV_PROTOCOL))) == NULL) + if ((proto = (SERV_LISTENER *)malloc(sizeof(SERV_LISTENER))) == NULL) { return 0; } proto->listener = NULL; proto->protocol = strdup(protocol); - if (address) - { - proto->address = strdup(address); - } - else - { - proto->address = NULL; - } + proto->address = address ? strdup(address) : NULL; proto->port = port; + proto->authenticator = authenticator ? strdup(authenticator) : NULL; + proto->ssl = ssl; spinlock_acquire(&service->spin); proto->next = service->ports; service->ports = proto; @@ -724,7 +726,7 @@ serviceAddProtocol(SERVICE *service, char *protocol, char *address, unsigned sho int serviceHasProtocol(SERVICE *service, char *protocol, unsigned short port) { - SERV_PROTOCOL *proto; + SERV_LISTENER *proto; spinlock_acquire(&service->spin); proto = service->ports; @@ -1457,7 +1459,7 @@ void dListListeners(DCB *dcb) { SERVICE *service; - SERV_PROTOCOL *lptr; + SERV_LISTENER *lptr; spinlock_acquire(&service_spin); service = allServices; @@ -1930,7 +1932,7 @@ serviceListenerRowCallback(RESULTSET *set, void *data) char buf[20]; RESULT_ROW *row; SERVICE *service; - SERV_PROTOCOL *lptr = NULL; + SERV_LISTENER *lptr = NULL; spinlock_acquire(&service_spin); service = allServices; @@ -2135,6 +2137,9 @@ int serviceInitSSL(SERVICE* service) DH* dh; RSA* rsa; + /* Pending removal in favour of processing in listener. */ + return 0; + if (!service->ssl_init_done) { switch(service->ssl_method_type) diff --git a/server/core/test/testservice.c b/server/core/test/testservice.c index d58d37ac1..b75dfcbae 100644 --- a/server/core/test/testservice.c +++ b/server/core/test/testservice.c @@ -58,7 +58,7 @@ init_test_env(NULL); /* Service tests */ ss_dfprintf(stderr, - "testservice : creating service called MyService with router nonexistent"); + "testservice : creating service called MyService with router nonexistent"); service = service_alloc("MyService", "non-existent"); mxs_log_flush_sync(); ss_info_dassert(NULL == service, "New service with invalid router should be null"); @@ -70,7 +70,7 @@ init_test_env(NULL); ss_info_dassert(0 != service_isvalid(service), "Service must be valid after creation"); ss_info_dassert(0 == strcmp("MyService", service_get_name(service)), "Service must have given name"); ss_dfprintf(stderr, "\t..done\nAdding protocol testprotocol."); - ss_info_dassert(0 != serviceAddProtocol(service, "testprotocol", "localhost", 9876), "Add Protocol should succeed"); + ss_info_dassert(0 != serviceAddProtocol(service, "testprotocol", "localhost", 9876, "MySQL", NULL), "Add Protocol should succeed"); ss_info_dassert(0 != serviceHasProtocol(service, "testprotocol", 9876), "Service should have new protocol as requested"); serviceStartProtocol(service, "testprotocol", 9876); mxs_log_flush_sync(); @@ -90,7 +90,7 @@ init_test_env(NULL); ss_dfprintf(stderr, "\t..done\n"); return 0; - + } int main(int argc, char **argv) diff --git a/server/include/dcb.h b/server/include/dcb.h index 94e1f3992..3d7ea4ee6 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -19,13 +19,11 @@ */ #include #include +#include #include #include #include #include -#include -#include -#include #define ERRHANDLE @@ -59,50 +57,13 @@ struct service; * 27/08/2014 Mark Riddoch Addition of write event queuing * 23/09/2014 Mark Riddoch New poll processing queue * 19/06/2015 Martin Brampton Provision of persistent connections + * 20/01/2016 Martin Brampton Moved GWPROTOCOL to gw_protocol.h * * @endverbatim */ struct dcb; -/** - * @verbatim - * The operations that can be performed on the descriptor - * - * read EPOLLIN handler for the socket - * write MaxScale data write entry point - * write_ready EPOLLOUT handler for the socket, indicates - * that the socket is ready to send more data - * error EPOLLERR handler for the socket - * hangup EPOLLHUP handler for the socket - * accept Accept handler for listener socket only - * connect Create a connection to the specified server - * for the session pased in - * close MaxScale close entry point for the socket - * listen Create a listener for the protocol - * auth Authentication entry point - * session Session handling entry point - * @endverbatim - * - * This forms the "module object" for protocol modules within the gateway. - * - * @see load_module - */ -typedef struct gw_protocol -{ - int (*read)(struct dcb *); - int (*write)(struct dcb *, GWBUF *); - int (*write_ready)(struct dcb *); - int (*error)(struct dcb *); - int (*hangup)(struct dcb *); - int (*accept)(struct dcb *); - int (*connect)(struct dcb *, struct server *, struct session *); - int (*close)(struct dcb *); - int (*listen)(struct dcb *, char *); - int (*auth)(struct dcb *, struct server *, struct session *, GWBUF *); - int (*session)(struct dcb *, void *); -} GWPROTOCOL; - /** * The event queue structure used in the polling loop to maintain a queue * of events that need to be processed for the DCB. @@ -128,13 +89,6 @@ typedef struct unsigned long started; } DCBEVENTQ; -/** - * The GWPROTOCOL version data. The following should be updated whenever - * the GWPROTOCOL structure is changed. See the rules defined in modinfo.h - * that define how these numbers should change. - */ -#define GWPROTOCOL_VERSION {1, 0, 0} - #define DCBFD_CLOSED -1 /** diff --git a/server/include/gw_protocol.h b/server/include/gw_protocol.h new file mode 100644 index 000000000..826a87c9d --- /dev/null +++ b/server/include/gw_protocol.h @@ -0,0 +1,92 @@ +#ifndef GW_PROTOCOL_H +#define GW_PROTOCOL_H +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It 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. + * + * 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 General Public License for more + * details. + * + * 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +/** + * @file protocol.h + * + * The listener definitions for MaxScale + * + * @verbatim + * Revision History + * + * Date Who Description + * 22/01/16 Martin Brampton Initial implementation + * + * @endverbatim + */ + +#include +#include +#include +#include +#include + +struct dcb; +struct server; +struct session; + +/** + * @verbatim + * The operations that can be performed on the descriptor + * + * read EPOLLIN handler for the socket + * write MaxScale data write entry point + * write_ready EPOLLOUT handler for the socket, indicates + * that the socket is ready to send more data + * error EPOLLERR handler for the socket + * hangup EPOLLHUP handler for the socket + * accept Accept handler for listener socket only + * connect Create a connection to the specified server + * for the session pased in + * close MaxScale close entry point for the socket + * listen Create a listener for the protocol + * auth Authentication entry point + * session Session handling entry point + * @endverbatim + * + * This forms the "module object" for protocol modules within the gateway. + * + * @see load_module + */ +typedef struct gw_protocol +{ + int (*read)(struct dcb *); + int (*write)(struct dcb *, GWBUF *); + int (*write_ready)(struct dcb *); + int (*error)(struct dcb *); + int (*hangup)(struct dcb *); + int (*accept)(struct dcb *); + int (*connect)(struct dcb *, struct server *, struct session *); + int (*close)(struct dcb *); + int (*listen)(struct dcb *, char *); + int (*auth)(struct dcb *, struct server *, struct session *, GWBUF *); + int (*session)(struct dcb *, void *); +} GWPROTOCOL; + +/** + * The GWPROTOCOL version data. The following should be updated whenever + * the GWPROTOCOL structure is changed. See the rules defined in modinfo.h + * that define how these numbers should change. + */ +#define GWPROTOCOL_VERSION {1, 0, 0} + + +#endif /* GW_PROTOCOL_H */ + diff --git a/server/include/listener.h b/server/include/listener.h new file mode 100644 index 000000000..b9dbfd37a --- /dev/null +++ b/server/include/listener.h @@ -0,0 +1,89 @@ +#ifndef _LISTENER_H +#define _LISTENER_H +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It 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. + * + * 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 General Public License for more + * details. + * + * 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +/** + * @file listener.h + * + * The listener definitions for MaxScale + * + * @verbatim + * Revision History + * + * Date Who Description + * 19/01/16 Martin Brampton Initial implementation + * + * @endverbatim + */ + +#include +#include + +enum +{ + SERVICE_SSLV3, + SERVICE_TLS10, +#ifdef OPENSSL_1_0 + SERVICE_TLS11, + SERVICE_TLS12, +#endif + SERVICE_SSL_MAX, + SERVICE_TLS_MAX, + SERVICE_SSL_TLS_MAX +}; + +/** + * The ssl_listener structure is used to aggregate the SSL configuration items + * and data for a particular listener + */ +typedef struct ssl_listener +{ + SSL_CTX *ctx; + SSL_METHOD *method; /*< SSLv3 or TLS1.0/1.1/1.2 methods + * see: https://www.openssl.org/docs/ssl/SSL_CTX_new.html */ + int ssl_cert_verify_depth; /*< SSL certificate verification depth */ + int ssl_method_type; /*< Which of the SSLv3 or TLS1.0/1.1/1.2 methods to use */ + char *ssl_cert; /*< SSL certificate */ + char *ssl_key; /*< SSL private key */ + char *ssl_ca_cert; /*< SSL CA certificate */ + bool ssl_init_done; /*< If SSL has already been initialized for this service */ +} SSL_LISTENER; + +/** + * The servlistener structure is used to link a service to the protocols that + * are used to support that service. It defines the name of the protocol module + * that should be loaded to support the client connection and the port that the + * protocol should use to listen for incoming client connections. + */ +typedef struct servlistener +{ + char *protocol; /**< Protocol module to load */ + unsigned short port; /**< Port to listen on */ + char *address; /**< Address to listen with */ + char *authenticator; /**< Name of authenticator */ + SSL_LISTENER *ssl; /**< Structure of SSL data or NULL */ + DCB *listener; /**< The DCB for the listener */ + struct servlistener *next; /**< Next service protocol */ +} SERV_LISTENER; + +int listener_set_ssl_version(SSL_LISTENER *ssl_listener, char* version); +void listener_set_certificates(SSL_LISTENER *ssl_listener, char* cert, char* key, char* ca_cert); +int listener_init_SSL(SSL_LISTENER *ssl_listener); + +#endif \ No newline at end of file diff --git a/server/include/service.h b/server/include/service.h index db9d76ad0..4c8f60c9a 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -19,9 +19,11 @@ */ #include +#include #include #include #include +#include #include #include #include @@ -59,21 +61,6 @@ struct router; struct router_object; struct users; -/** - * The servprotocol structure is used to link a service to the protocols that - * are used to support that service. It defines the name of the protocol module - * that should be loaded to support the client connection and the port that the - * protocol should use to listen for incoming client connections. - */ -typedef struct servprotocol -{ - char *protocol; /**< Protocol module to load */ - unsigned short port; /**< Port to listen on */ - char *address; /**< Address to listen with */ - DCB *listener; /**< The DCB for the listener */ - struct servprotocol *next; /**< Next service protocol */ -} SERV_PROTOCOL; - /** * The service statistics structure */ @@ -120,19 +107,6 @@ typedef enum SSL_REQUIRED } ssl_mode_t; -enum -{ - SERVICE_SSLV3, - SERVICE_TLS10, -#ifdef OPENSSL_1_0 - SERVICE_TLS11, - SERVICE_TLS12, -#endif - SERVICE_SSL_MAX, - SERVICE_TLS_MAX, - SERVICE_SSL_TLS_MAX -}; - #define DEFAULT_SSL_CERT_VERIFY_DEPTH 100 /*< The default certificate verification depth */ #define SERVICE_MAX_RETRY_INTERVAL 3600 /*< The maximum interval between service start retries */ @@ -156,7 +130,7 @@ typedef struct service { char *name; /**< The service name */ int state; /**< The service state */ - SERV_PROTOCOL *ports; /**< Linked list of ports and protocols + SERV_LISTENER *ports; /**< Linked list of ports and protocols * that this service will listen on. */ char *routerModule; /**< Name of router module to use */ @@ -218,7 +192,7 @@ extern SERVICE *service_alloc(const char *, const char *); extern int service_free(SERVICE *); extern SERVICE *service_find(char *); extern int service_isvalid(SERVICE *); -extern int serviceAddProtocol(SERVICE *, char *, char *, unsigned short); +extern int serviceAddProtocol(SERVICE *, char *, char *, unsigned short, char *, SSL_LISTENER *); extern int serviceHasProtocol(SERVICE *, char *, unsigned short); extern void serviceAddBackend(SERVICE *, SERVER *); extern int serviceHasBackend(SERVICE *, SERVER *); diff --git a/server/modules/protocol/httpd.c b/server/modules/protocol/httpd.c index b574adc17..39a738978 100644 --- a/server/modules/protocol/httpd.c +++ b/server/modules/protocol/httpd.c @@ -38,6 +38,7 @@ */ #include +#include #include #include #include diff --git a/server/modules/protocol/maxscaled.c b/server/modules/protocol/maxscaled.c index 9c37e93c8..d22c4187b 100644 --- a/server/modules/protocol/maxscaled.c +++ b/server/modules/protocol/maxscaled.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index 3a5fb4e77..127c50f14 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -50,6 +50,7 @@ * */ #include +#include MODULE_INFO info = { diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 12d740d77..bee021e48 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -47,6 +47,7 @@ * 11/01/2016 Martin Brampton Remove SSL write code, now handled at lower level; * replace gwbuf_consume by gwbuf_free (multiple). */ +#include #include #include #include @@ -74,16 +75,15 @@ static int gw_MySQLWrite_client(DCB *dcb, GWBUF *queue); static int gw_error_client_event(DCB *dcb); static int gw_client_close(DCB *dcb); static int gw_client_hangup_event(DCB *dcb); -int gw_read_client_event_SSL(DCB* dcb); -int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message); -int MySQLSendHandshake(DCB* dcb); +static int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message); +static int MySQLSendHandshake(DCB* dcb); static int gw_mysql_do_authentication(DCB *dcb, GWBUF **queue); static int route_by_statement(SESSION *, GWBUF **); extern char* get_username_from_auth(char* ptr, uint8_t* data); extern int check_db_name_after_auth(DCB *, char *, int); extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db,int); -int do_ssl_accept(MySQLProtocol* protocol); +static int do_ssl_accept(MySQLProtocol* protocol); /* * The "module object" for the mysqld client protocol module. diff --git a/server/modules/protocol/telnetd.c b/server/modules/protocol/telnetd.c index d9ff6bcf3..11e325ec3 100644 --- a/server/modules/protocol/telnetd.c +++ b/server/modules/protocol/telnetd.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/server/modules/protocol/testprotocol.c b/server/modules/protocol/testprotocol.c index 7ceb867d9..b12488b44 100644 --- a/server/modules/protocol/testprotocol.c +++ b/server/modules/protocol/testprotocol.c @@ -33,6 +33,7 @@ #include #include #include +#include MODULE_INFO info = {