diff --git a/client/maxadmin.c b/client/maxadmin.c index 5710cc35a..683916e9f 100644 --- a/client/maxadmin.c +++ b/client/maxadmin.c @@ -58,6 +58,7 @@ static int authMaxScale(int so, char *user, char *password); static int sendCommand(int so, char *cmd); static void DoSource(int so, char *cmd); static void DoUsage(); +static int isquit(char *buf); #ifdef HISTORY static char * @@ -289,7 +290,7 @@ int argno = 0; history(hist, &ev, H_ENTER, buf); #endif - if (!strcasecmp(buf, "quit")) + if (isquit(buf)) { break; } @@ -552,3 +553,23 @@ DoUsage() printf("Any remaining arguments are treated as MaxScale commands or a file\n"); printf("containing commands to execute.\n"); } + +/** + * Check command to see if it is a quit command + * + * @param buf The command buffer + * @return Non-zero if the command should cause maxadmin to quit + */ +static int +isquit(char *buf) +{ +char *ptr = buf; + + if (!buf) + return 0; + while (*ptr && isspace(*ptr)) + ptr++; + if (strncasecmp(ptr, "quit", 4) == 0 || strncasecmp(ptr, "exit", 4) == 0) + return 1; + return 0; +} diff --git a/server/core/config.c b/server/core/config.c index ae477b07c..1ba2bcbf4 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -35,6 +35,9 @@ * 23/05/14 Massimiliano Pinto Added automatic set of maxscale-id: first listening ipv4_raw + port + pid * 28/05/14 Massimiliano Pinto Added detect_replication_lag parameter * 28/08/14 Massimiliano Pinto Added detect_stale_master parameter + * 09/09/14 Massimiliano Pinto Added localhost_match_wildcard_host parameter + * 12/09/14 Mark Riddoch Addition of checks on servers list and + * internal router suppression of messages * * @endverbatim */ @@ -62,6 +65,7 @@ static int handle_global_item(const char *, const char *); static void global_defaults(); static void check_config_objects(CONFIG_CONTEXT *context); static int config_truth_value(char *str); +static int internalService(char *router); static char *config_file = NULL; static GATEWAY_CONF gateway; @@ -288,6 +292,9 @@ int error_count = 0; is_rwsplit = true; } + char *allow_localhost_match_wildcard_host = + config_get_value(obj->parameters, "localhost_match_wildcard_host"); + if (obj->element == NULL) /*< if module load failed */ { LOGIF(LE, (skygw_log_write_flush( @@ -322,6 +329,11 @@ int error_count = 0; if (weightby) serviceWeightBy(obj->element, weightby); + if (allow_localhost_match_wildcard_host) + serviceEnableLocalhostMatchWildcardHost( + obj->element, + config_truth_value(allow_localhost_match_wildcard_host)); + if (!auth) auth = config_get_value(obj->parameters, "auth"); @@ -605,36 +617,50 @@ int error_count = 0; { char *servers; char *roptions; + char *router; char *filters = config_get_value(obj->parameters, "filters"); servers = config_get_value(obj->parameters, "servers"); roptions = config_get_value(obj->parameters, "router_options"); + router = config_get_value(obj->parameters, "router"); if (servers && obj->element) { char *s = strtok(servers, ","); while (s) { CONFIG_CONTEXT *obj1 = context; + int found = 0; while (obj1) { if (strcmp(trim(s), obj1->object) == 0 && obj->element && obj1->element) { + found = 1; serviceAddBackend( obj->element, obj1->element); } obj1 = obj1->next; } + if (!found) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Unable to find " + "server '%s' that is " + "configured as part of " + "service '%s'.", + s, obj->object))); + } s = strtok(NULL, ","); } } - else if (servers == NULL) + else if (servers == NULL && internalService(router) == 0) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Error : The service '%s' is missing a " + "Warning: The service '%s' is missing a " "definition of the servers that provide " "the service.", obj->object))); @@ -787,17 +813,29 @@ int error_count = 0; while (s) { CONFIG_CONTEXT *obj1 = context; + int found = 0; while (obj1) { if (strcmp(s, obj1->object) == 0 && obj->element && obj1->element) { + found = 1; monitorAddServer( obj->element, obj1->element); } obj1 = obj1->next; } + if (!found) + LOGIF(LE, + (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Unable to find " + "server '%s' that is " + "configured in the " + "monitor '%s'.", + s, obj->object))); + s = strtok(NULL, ","); } } @@ -1162,6 +1200,7 @@ SERVER *server; char* max_slave_conn_str; char* max_slave_rlag_str; char *version_string; + char *allow_localhost_match_wildcard_host; enable_root_user = config_get_value(obj->parameters, "enable_root_user"); @@ -1172,6 +1211,8 @@ SERVER *server; version_string = config_get_value(obj->parameters, "version_string"); + allow_localhost_match_wildcard_host = config_get_value(obj->parameters, "localhost_match_wildcard_host"); + if (version_string) { if (service->version_string) { free(service->version_string); @@ -1185,6 +1226,11 @@ SERVER *server; auth); if (enable_root_user) serviceEnableRootUser(service, atoi(enable_root_user)); + + if (allow_localhost_match_wildcard_host) + serviceEnableLocalhostMatchWildcardHost( + service, + atoi(allow_localhost_match_wildcard_host)); /** Read, validate and set max_slave_connections */ max_slave_conn_str = @@ -1279,10 +1325,13 @@ SERVER *server; char *user; char *auth; char *enable_root_user; + char *allow_localhost_match_wildcard_host; enable_root_user = config_get_value(obj->parameters, "enable_root_user"); + allow_localhost_match_wildcard_host = + config_get_value(obj->parameters, "localhost_match_wildcard_host"); user = config_get_value(obj->parameters, "user"); @@ -1298,6 +1347,11 @@ SERVER *server; auth); if (enable_root_user) serviceEnableRootUser(service, atoi(enable_root_user)); + + if (allow_localhost_match_wildcard_host) + serviceEnableLocalhostMatchWildcardHost( + service, + atoi(allow_localhost_match_wildcard_host)); } } } @@ -1391,11 +1445,13 @@ SERVER *server; while (s) { CONFIG_CONTEXT *obj1 = context; + int found = 0; while (obj1) { if (strcmp(s, obj1->object) == 0 && obj->element && obj1->element) { + found = 1; if (!serviceHasBackend(obj->element, obj1->element)) { serviceAddBackend( @@ -1405,6 +1461,16 @@ SERVER *server; } obj1 = obj1->next; } + if (!found) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Unable to find " + "server '%s' that is " + "configured as part of " + "service '%s'.", + s, obj->object))); + } s = strtok(NULL, ","); } } @@ -1503,6 +1569,7 @@ static char *service_params[] = "user", "passwd", "enable_root_user", + "localhost_match_wildcard_host", "max_slave_connections", "max_slave_replication_lag", "use_sql_variables_in", /*< rwsplit only */ @@ -1667,3 +1734,29 @@ config_truth_value(char *str) return atoi(str); } +static char *InternalRouters[] = { + "debugcli", + "cli", + NULL +}; + +/** + * Determine if the router is one of the special internal services that + * MaxScale offers. + * + * @param router The router name + * @return Non-zero if the router is in the InternalRouters table + */ +static int +internalService(char *router) +{ +int i; + + if (router) + { + for (i = 0; InternalRouters[i]; i++) + if (strcmp(router, InternalRouters[i]) == 0) + return 1; + } + return 0; +} diff --git a/server/core/server.c b/server/core/server.c index 631bb2010..43c535d0e 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -375,23 +375,23 @@ char *stat; if (ptr) { dcb_printf(dcb, "Servers.\n"); - dcb_printf(dcb, "-------------------+-----------------+-------+----------------------+------------\n"); - dcb_printf(dcb, "%-18s | %-15s | Port | %-20s | Connections\n", + dcb_printf(dcb, "-------------------+-----------------+-------+-------------+--------------------\n"); + dcb_printf(dcb, "%-18s | %-15s | Port | Connections | %-20s\n", "Server", "Address", "Status"); - dcb_printf(dcb, "-------------------+-----------------+-------+----------------------+------------\n"); + dcb_printf(dcb, "-------------------+-----------------+-------+-------------+--------------------\n"); } while (ptr) { stat = server_status(ptr); - dcb_printf(dcb, "%-18s | %-15s | %5d | %-20s | %4d\n", + dcb_printf(dcb, "%-18s | %-15s | %5d | %11d | %s\n", ptr->unique_name, ptr->name, - ptr->port, stat, - ptr->stats.n_current); + ptr->port, + ptr->stats.n_current, stat); free(stat); ptr = ptr->next; } if (allServers) - dcb_printf(dcb, "-------------------+-----------------+-------+----------------------+------------\n\n"); + dcb_printf(dcb, "-------------------+-----------------+-------+-------------+--------------------\n"); spinlock_release(&server_spin); } @@ -424,6 +424,8 @@ char *status = NULL; strcat(status, "Slave of External Server, "); if (server->status & SERVER_STALE_STATUS) strcat(status, "Stale Status, "); + if (server->status & SERVER_AUTH_ERROR) + strcat(status, "Auth Error, "); if (server->status & SERVER_RUNNING) strcat(status, "Running"); else diff --git a/server/core/service.c b/server/core/service.c index 69b446b09..a5e08f937 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -31,6 +31,7 @@ * 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL * 23/05/14 Mark Riddoch Addition of service validation call * 29/05/14 Mark Riddoch Filter API implementation + * 09/09/14 Massimiliano Pinto Added service option for localhost authentication * * @endverbatim */ @@ -128,6 +129,7 @@ SERVICE *service; service->credentials.name = NULL; service->credentials.authdata = NULL; service->enable_root = 0; + service->localhost_match_wildcard_host = 0; service->routerOptions = NULL; service->databases = NULL; service->svc_config_param = NULL; @@ -1288,3 +1290,23 @@ serviceGetWeightingParameter(SERVICE *service) { return service->weightby; } + +/** + * Enable/Disable localhost authentication match criteria + * associated with this service. + * + * @param service The service we are setting the data for + * @param action 1 for enable, 0 for disable access + * @return 0 on failure + */ + +int +serviceEnableLocalhostMatchWildcardHost(SERVICE *service, int action) +{ + if (action != 0 && action != 1) + return 0; + + service->localhost_match_wildcard_host = action; + + return 1; +} diff --git a/server/core/session.c b/server/core/session.c index db59f9e6e..eb9d42a2b 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -693,7 +693,17 @@ int i; service->name))); return 0; } - session->tail = *tail; + + /* + * filterUpstream may simply return the 3 parameter if + * the filter has no upstream entry point. So no need + * to copy the contents or free tail in this case. + */ + if (tail != &session->tail) + { + session->tail = *tail; + free(tail); + } } return 1; diff --git a/server/include/server.h b/server/include/server.h index 2b75ddf61..81392243c 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -105,6 +105,7 @@ typedef struct server { #define SERVER_MAINT 0x0020 /**<< Server is in maintenance mode */ #define SERVER_SLAVE_OF_EXTERNAL_MASTER 0x0040 /**<< Server is slave of a Master outside the provided replication topology */ #define SERVER_STALE_STATUS 0x0080 /**<< Server stale status, monitor didn't update it */ +#define SERVER_AUTH_ERROR 0x1000 /**<< Authentication erorr from monitor */ /** * Is the server running - the macro returns true if the server is marked as running diff --git a/server/include/service.h b/server/include/service.h index abedaec7c..139a08056 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -44,6 +44,7 @@ * struct * 29/05/14 Mark Riddoch Filter API mechanism * 26/06/14 Mark Riddoch Added WeightBy support + * 09/09/14 Massimiliano Pinto Added service option for localhost authentication * * @endverbatim */ @@ -122,6 +123,7 @@ typedef struct service { SERVICE_STATS stats; /**< The service statistics */ struct users *users; /**< The user data for this service */ int enable_root; /**< Allow root user access */ + int localhost_match_wildcard_host; /**< Match localhost against wildcard */ CONFIG_PARAMETER* svc_config_param; /*< list of config params and values */ int svc_config_version; /*< Version number of configuration */ @@ -161,6 +163,7 @@ extern void serviceSetFilters(SERVICE *, char *); extern int serviceEnableRootUser(SERVICE *, int ); extern void serviceWeightBy(SERVICE *, char *); extern char *serviceGetWeightingParameter(SERVICE *); +extern int serviceEnableLocalhostMatchWildcardHost(SERVICE *, int); extern void service_update(SERVICE *, char *, char *, char *); extern int service_refresh_users(SERVICE *); extern void printService(SERVICE *); diff --git a/server/modules/monitor/galera_mon.c b/server/modules/monitor/galera_mon.c index 761078453..211407f86 100644 --- a/server/modules/monitor/galera_mon.c +++ b/server/modules/monitor/galera_mon.c @@ -335,10 +335,18 @@ char *server_string; database->server->port, mysql_error(database->con)))); server_clear_status(database->server, SERVER_RUNNING); + if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR) + { + server_set_status(database->server, SERVER_AUTH_ERROR); + } database->server->node_id = -1; free(dpwd); return; } + else + { + server_clear_status(database->server, SERVER_AUTH_ERROR); + } free(dpwd); } @@ -351,7 +359,9 @@ char *server_string; /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); if (server_string) { - database->server->server_string = strdup(server_string); + database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); + if (database->server->server_string) + strcpy(database->server->server_string, server_string); } /* Check if the the Galera FSM shows this node is joined to the cluster */ diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index e78759f82..976aa315d 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -400,6 +400,11 @@ char *server_string; * Store server NOT running in server and monitor server pending struct * */ + if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR) + { + server_set_status(database->server, SERVER_AUTH_ERROR); + monitor_set_pending_status(database, SERVER_AUTH_ERROR); + } server_clear_status(database->server, SERVER_RUNNING); monitor_clear_pending_status(database, SERVER_RUNNING); @@ -417,6 +422,11 @@ char *server_string; return; } + else + { + server_clear_status(database->server, SERVER_AUTH_ERROR); + monitor_clear_pending_status(database, SERVER_AUTH_ERROR); + } free(dpwd); } /* Store current status in both server and monitor server pending struct */ @@ -429,7 +439,9 @@ char *server_string; /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); if (server_string) { - database->server->server_string = strdup(server_string); + database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); + if (database->server->server_string) + strcpy(database->server->server_string, server_string); } /* get server_id form current node */ diff --git a/server/modules/monitor/ndbcluster_mon.c b/server/modules/monitor/ndbcluster_mon.c index c565c4002..840e30691 100644 --- a/server/modules/monitor/ndbcluster_mon.c +++ b/server/modules/monitor/ndbcluster_mon.c @@ -329,10 +329,18 @@ char *server_string; database->server->port, mysql_error(database->con)))); server_clear_status(database->server, SERVER_RUNNING); + if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR) + { + server_set_status(database->server, SERVER_AUTH_ERROR); + } database->server->node_id = -1; free(dpwd); return; } + else + { + server_clear_status(database->server, SERVER_AUTH_ERROR); + } free(dpwd); } @@ -345,7 +353,9 @@ char *server_string; /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); if (server_string) { - database->server->server_string = strdup(server_string); + database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); + if (database->server->server_string) + strcpy(database->server->server_string, server_string); } /* Check if the the SQL node is able to contact one or more data nodes */ diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index ca365066e..2772e8e7b 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -492,6 +492,7 @@ static int gw_read_backend_event(DCB *dcb) { dcb->dcb_readqueue = NULL; } } + /** This may be either short prefix of a packet, or the tail of it. */ else { if (nbytes_read < 5) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 3c8a70bd9..5425667d8 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -42,6 +42,7 @@ #include #include #include +#include MODULE_INFO info = { MODULE_API_PROTOCOL, @@ -552,15 +553,16 @@ int gw_read_client_event( */ if (dcb->dcb_readqueue) { - uint8_t* data = (uint8_t *)GWBUF_DATA(read_buffer); + uint8_t* data; - read_buffer = gwbuf_append(dcb->dcb_readqueue, read_buffer); - nbytes_read = gwbuf_length(read_buffer); + dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); + nbytes_read = gwbuf_length(dcb->dcb_readqueue); + data = (uint8_t *)GWBUF_DATA(dcb->dcb_readqueue); if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data)) { - rc = 0; - goto return_rc; + rc = 0; + goto return_rc; } else { @@ -578,8 +580,8 @@ int gw_read_client_event( if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data)+4) { - gwbuf_append(dcb->dcb_readqueue, read_buffer); - rc = 0; + dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); + rc = 0; goto return_rc; } } @@ -789,7 +791,7 @@ int gw_read_client_event( if (read_buffer != NULL) { /** add incomplete mysql packet to read queue */ - gwbuf_append(dcb->dcb_readqueue, read_buffer); + dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); } } else @@ -1443,7 +1445,11 @@ static int route_by_statement( do { ss_dassert(GWBUF_IS_TYPE_MYSQL((*p_readbuf))); - + + /** + * Collect incoming bytes to a buffer until complete packet has + * arrived and then return the buffer. + */ packetbuf = gw_MySQL_get_next_packet(p_readbuf); if (packetbuf != NULL) diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index f9c0ebdea..0dad65350 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -26,6 +26,11 @@ * 04/09/2013 Massimiliano Pinto Added dcb NULL assert in mysql_send_custom_error * 12/09/2013 Massimiliano Pinto Added checks in gw_decode_mysql_server_handshake and gw_read_backend_handshake * 10/02/2014 Massimiliano Pinto Added MySQL Authentication with user@host + * 10/09/2014 Massimiliano Pinto Added MySQL Authentication option enabling localhost match with any host (wildcard %) + * Backend server configuration may differ so default is 0, don't match and an explicit + * localhost entry should be added for the selected user in the backends. + * Setting to 1 allow localhost (127.0.0.1 or socket) to match the any host grant via + * user@% * */ @@ -1345,12 +1350,12 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, * The check for localhost is 127.0.0.1 (IPv4 only) */ - if (key.ipv4.sin_addr.s_addr == 0x0100007F) { + if ((key.ipv4.sin_addr.s_addr == 0x0100007F) && !dcb->service->localhost_match_wildcard_host) { /* Skip the wildcard check and return 1 */ - LOGIF(LD, + LOGIF(LE, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [MySQL Client Auth], user [%s@%s] not existent", + LOGFILE_ERROR, + "%lu [MySQL Client Auth], user [%s@%s] not found, please try with 'localhost_match_wildcard_host=1' in service definition", pthread_self(), key.user, dcb->remote))); @@ -1530,9 +1535,7 @@ GWBUF* gw_MySQL_get_next_packet( { packetbuf = NULL; goto return_packetbuf; - } - - buflen = GWBUF_LENGTH((readbuf)); + } totalbuflen = gwbuf_length(readbuf); data = (uint8_t *)GWBUF_DATA((readbuf)); packetlen = MYSQL_GET_PACKET_LEN(data)+4; @@ -1556,6 +1559,7 @@ GWBUF* gw_MySQL_get_next_packet( uint8_t* src = GWBUF_DATA((*p_readbuf)); size_t bytestocopy; + buflen = GWBUF_LENGTH((*p_readbuf)); bytestocopy = MIN(buflen,packetlen-nbytes_copied); memcpy(target+nbytes_copied, src, bytestocopy); @@ -1569,7 +1573,6 @@ return_packetbuf: return packetbuf; } - /** * Move from buffer pointed to by <*p_readbuf>. */ @@ -1694,8 +1697,9 @@ void protocol_add_srv_command( MySQLProtocol* p, mysql_server_cmd_t cmd) { +#if defined(SS_DEBUG) server_command_t* c; - +#endif spinlock_acquire(&p->protocol_lock); if (p->protocol_state != MYSQL_PROTOCOL_ACTIVE) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 47e21a029..d7ba011bc 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1949,7 +1949,7 @@ static int routeQuery( } rses_end_locked_router_action(router_cli_ses); retblock: -#if defined(SS_DEBUG) +#if defined(SS_DEBUG2) { char* canonical_query_str;