diff --git a/CMakeLists.txt b/CMakeLists.txt index e6fbec0d8..0c133c311 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,6 +144,7 @@ add_custom_target(buildtests add_custom_target(testall COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DBUILD_TYPE=Debug -DINSTALL_DIR=${CMAKE_BINARY_DIR} -DINSTALL_SYSTEM_FILES=N ${CMAKE_SOURCE_DIR} COMMAND make install + COMMAND cp -vn ${CMAKE_SOURCE_DIR}/server/test/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -c ${CMAKE_BINARY_DIR} &>/dev/null" COMMAND /bin/sh -c "make test || echo \"Test results written to: ${CMAKE_BINARY_DIR}/Testing/Temporary/\"" COMMAND killall maxscale diff --git a/VERSION b/VERSION index 1f6191473..ed69ddf2c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.1-beta +1.0.2-beta diff --git a/macros.cmake b/macros.cmake index 3b75fcfa6..d8f1b7827 100644 --- a/macros.cmake +++ b/macros.cmake @@ -127,13 +127,17 @@ debugmsg("Search returned: ${MYSQL_DIR_LOC}") # Find the embedded mysql library if (DEFINED EMBEDDED_LIB) - debugmsg("Searching for the embedded library at: ${EMBEDDED_LIB}") - if(${CMAKE_VERSION} VERSION_LESS 2.12 ) - set(COMP_VAR PATH) - else() - set(COMP_VAR DIRECTORY) + if( NOT (IS_DIRECTORY ${EMBEDDED_LIB}) ) + debugmsg("EMBEDDED_LIB is not a directory: ${EMBEDDED_LIB}") + if(${CMAKE_VERSION} VERSION_LESS 2.12 ) + set(COMP_VAR PATH) + else() + set(COMP_VAR DIRECTORY) + endif() + get_filename_component(EMBEDDED_LIB ${EMBEDDED_LIB} ${COMP_VAR}) + debugmsg("EMBEDDED_LIB directory component: ${EMBEDDED_LIB}") endif() - get_filename_component(EMBEDDED_LIB ${EMBEDDED_LIB} ${COMP_VAR}) + debugmsg("Searching for the embedded library at: ${EMBEDDED_LIB}") endif() if(STATIC_EMBEDDED) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index a42c625ae..6cd174190 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,6 +1,3 @@ add_subdirectory(core) add_subdirectory(modules) add_subdirectory(inih) -if(BUILD_TESTS) -add_subdirectory(test) -endif() \ No newline at end of file diff --git a/server/core/dbusers.c b/server/core/dbusers.c index c65aafdc9..3b2b36361 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -28,6 +28,9 @@ * 06/02/2014 Massimiliano Pinto Mysql user root selected based on configuration flag * 26/02/2014 Massimiliano Pinto Addd: replace_mysql_users() routine may replace users' table based on a checksum * 28/02/2014 Massimiliano Pinto Added Mysql user@host authentication + * 29/09/2014 Massimiliano Pinto Added Mysql user@host authentication with wildcard in IPv4 hosts: + * x.y.z.%, x.y.%.%, x.%.%.% + * 03/10/14 Massimiliano Pinto Added netmask to user@host authentication for wildcard in IPv4 hosts * * @endverbatim */ @@ -50,13 +53,14 @@ extern int lm_enabled_logfiles_bitmask; -static int getUsers(SERVICE *service, struct users *users); +static int getUsers(SERVICE *service, USERS *users); static int uh_cmpfun( void* v1, void* v2); static void *uh_keydup(void* key); static void uh_keyfree( void* key); static int uh_hfun( void* key); char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key); char *mysql_format_user_entry(void *data); +int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *passwd); /** * Load the user/passwd form mysql.user table into the service users' hashtable @@ -82,7 +86,7 @@ int reload_mysql_users(SERVICE *service) { int i; -struct users *newusers, *oldusers; +USERS *newusers, *oldusers; if ((newusers = mysql_users_alloc()) == NULL) return 0; @@ -108,15 +112,17 @@ int replace_mysql_users(SERVICE *service) { int i; -struct users *newusers, *oldusers; +USERS *newusers, *oldusers; if ((newusers = mysql_users_alloc()) == NULL) return -1; i = getUsers(service, newusers); - if (i <= 0) + if (i <= 0) { + users_free(newusers); return i; + } spinlock_acquire(&service->spin); oldusers = service->users; @@ -148,6 +154,92 @@ struct users *newusers, *oldusers; return i; } + +/** + * Add a new MySQL user with host, password and netmask into the service users table + * + * The netmask values are: + * 0 for any, 32 for single IPv4 + * 24 for a class C from a.b.c.%, 16 for a Class B from a.b.%.% and 8 for a Class A from a.%.%.% + * + * @param users The users table + * @param user The user name + * @param host The host to add, with possible wildcards + * @param passwd The sha1(sha1(passoword)) to add + * @return 1 on success, 0 on failure + */ + +int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *passwd) { + struct sockaddr_in serv_addr; + MYSQL_USER_HOST key; + char ret_ip[INET_ADDRSTRLEN + 1]=""; + int found_range=0; + int found_any=0; + int ret = 0; + + /* prepare the user@host data struct */ + memset(&serv_addr, 0, sizeof(serv_addr)); + memset(&key, 0, sizeof(key)); + + /* set user */ + key.user = strdup(user); + + if(key.user == NULL) { + return ret; + } + + /* handle ANY, Class C,B,A */ + + /* ANY */ + if (strcmp(host, "%") == 0) { + strcpy(ret_ip, "0.0.0.0"); + found_any = 1; + } else { + char *tmp; + strcpy(ret_ip, host); + tmp = ret_ip+strlen(ret_ip)-1; + + /* start from Class C */ + while(*tmp) { + if (*tmp == '%') { + /* set only the last IPv4 byte to 1 + * avoiding setipadress() failure + * for Class C address + */ + found_range++; + if (found_range == 1) + *tmp = '1'; + else + *tmp = '0'; + } + tmp--; + } + } + + /* fill IPv4 data struct */ + if (setipaddress(&serv_addr.sin_addr, ret_ip)) { + + /* copy IPv4 data into key.ipv4 */ + memcpy(&key.ipv4, &serv_addr, sizeof(serv_addr)); + + if (found_range) { + /* let's zero the last IP byte: a.b.c.0 we set above to 1*/ + key.ipv4.sin_addr.s_addr &= 0x00FFFFFF; + key.netmask = 32 - (found_range * 8); + } else { + key.netmask = 32 - (found_any * 32); + } + + /* add user@host as key and passwd as value in the MySQL users hash table */ + if (mysql_users_add(users, &key, passwd)) + ret = 1; + } + + free(key.user); + + return ret; +} + /** * Load the user/passwd form mysql.user table into the service users' hashtable * environment. @@ -157,7 +249,7 @@ struct users *newusers, *oldusers; * @return -1 on any error or the number of users inserted (0 means no users at all) */ static int -getUsers(SERVICE *service, struct users *users) +getUsers(SERVICE *service, USERS *users) { MYSQL *con = NULL; MYSQL_ROW row; @@ -173,8 +265,6 @@ getUsers(SERVICE *service, struct users *users) char *users_data = NULL; int nusers = 0; int users_data_row_len = MYSQL_USER_MAXLEN + MYSQL_HOST_MAXLEN + MYSQL_PASSWORD_LEN; - struct sockaddr_in serv_addr; - MYSQL_USER_HOST key; /* enable_root for MySQL protocol module means load the root user credentials from backend databases */ if(service->enable_root) { @@ -311,63 +401,26 @@ getUsers(SERVICE *service, struct users *users) * added to hashtable. */ - char ret_ip[INET_ADDRSTRLEN + 1]=""; - const char *rc; + int rc = 0; - /* prepare the user@host data struct */ - memset(&serv_addr, 0, sizeof(serv_addr)); - memset(&key, 0, sizeof(key)); + rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], strlen(row[2]) ? row[2]+1 : row[2]); - /* if host == '%', 0 is passed */ - if (setipaddress(&serv_addr.sin_addr, strcmp(row[1], "%") ? row[1] : "0.0.0.0")) { + if (rc == 1) { + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "%lu [mysql_users_add()] Added user %s@%s", + pthread_self(), + row[0], + row[1]))); - key.user = strdup(row[0]); - - if(key.user == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [getUsers()] strdup() failed for user %s", - pthread_self(), - row[0]))); - - continue; - } - - memcpy(&key.ipv4, &serv_addr, sizeof(serv_addr)); - - rc = inet_ntop(AF_INET, &(serv_addr).sin_addr, ret_ip, INET_ADDRSTRLEN); - - /* add user@host as key and passwd as value in the MySQL users hash table */ - if (mysql_users_add(users, &key, strlen(row[2]) ? row[2]+1 : row[2])) { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [mysql_users_add()] Added user %s@%s(%s)", - pthread_self(), - row[0], - row[1], - rc == NULL ? "NULL" : ret_ip))); - - /* Append data in the memory area for SHA1 digest */ - strncat(users_data, row[3], users_data_row_len); - - total_users++; - } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [mysql_users_add()] Failed adding user %s@%s(%s)", - pthread_self(), - row[0], - row[1], - rc == NULL ? "NULL" : ret_ip))); - } - - free(key.user); + /* Append data in the memory area for SHA1 digest */ + strncat(users_data, row[3], users_data_row_len); + total_users++; } else { - /* setipaddress() failed, skip user add and log this*/ LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "%lu [getUsers()] setipaddress failed: user %s@%s not added", + "%lu [mysql_users_add()] Failed adding user %s@%s", pthread_self(), row[0], row[1]))); @@ -475,7 +528,7 @@ static int uh_hfun( void* key) { * Currently only IPv4 addresses are supported * * @param key1 The key value, i.e. username@host (IPv4) - * @param key1 The key value, i.e. username@host (IPv4) + * @param key2 The key value, i.e. username@host (IPv4) * @return The compare value */ @@ -486,7 +539,7 @@ static int uh_cmpfun( void* v1, void* v2) { if (v1 == NULL || v2 == NULL || hu1 == NULL || hu2 == NULL || hu1->user == NULL || hu2->user == NULL) return 0; - if (strcmp(hu1->user, hu2->user) == 0 && (hu1->ipv4.sin_addr.s_addr == hu2->ipv4.sin_addr.s_addr)) { + if (strcmp(hu1->user, hu2->user) == 0 && (hu1->ipv4.sin_addr.s_addr == hu2->ipv4.sin_addr.s_addr) && (hu1->netmask >= hu2->netmask)) { return 0; } else { return 1; @@ -513,6 +566,7 @@ static void *uh_keydup(void* key) { return NULL; memcpy(&rval->ipv4, ¤t_key->ipv4, sizeof(struct sockaddr_in)); + memcpy(&rval->netmask, ¤t_key->netmask, sizeof(int)); return (void *) rval; } @@ -561,13 +615,25 @@ char *mysql_format_user_entry(void *data) if (mysql_user == NULL) return NULL; - if (entry->ipv4.sin_addr.s_addr == INADDR_ANY) { - snprintf(mysql_user, mysql_user_len, "%s@%%", entry->user); - } else { + if (entry->ipv4.sin_addr.s_addr == INADDR_ANY && entry->netmask == 0) { + snprintf(mysql_user, mysql_user_len-1, "%s@%%", entry->user); + } else if ( (entry->ipv4.sin_addr.s_addr & 0xFF000000) == 0 && entry->netmask == 24) { + snprintf(mysql_user, mysql_user_len-1, "%s@%i.%i.%i.%%", entry->user, entry->ipv4.sin_addr.s_addr & 0x000000FF, (entry->ipv4.sin_addr.s_addr & 0x0000FF00) / (256), (entry->ipv4.sin_addr.s_addr & 0x00FF0000) / (256 * 256)); + } else if ( (entry->ipv4.sin_addr.s_addr & 0xFFFF0000) == 0 && entry->netmask == 16) { + snprintf(mysql_user, mysql_user_len-1, "%s@%i.%i.%%.%%", entry->user, entry->ipv4.sin_addr.s_addr & 0x000000FF, (entry->ipv4.sin_addr.s_addr & 0x0000FF00) / (256)); + } else if ( (entry->ipv4.sin_addr.s_addr & 0xFFFFFF00) == 0 && entry->netmask == 8) { + snprintf(mysql_user, mysql_user_len-1, "%s@%i.%%.%%.%%", entry->user, entry->ipv4.sin_addr.s_addr & 0x000000FF); + } else if (entry->netmask == 32) { strncpy(mysql_user, entry->user, MYSQL_USER_MAXLEN); strcat(mysql_user, "@"); inet_ntop(AF_INET, &(entry->ipv4).sin_addr, mysql_user+strlen(mysql_user), INET_ADDRSTRLEN); + } else { + snprintf(mysql_user, MYSQL_USER_MAXLEN-6, "warn: %s", entry->user); + strcat(mysql_user, "@"); + inet_ntop(AF_INET, &(entry->ipv4).sin_addr, mysql_user+strlen(mysql_user), INET_ADDRSTRLEN); + } return mysql_user; } + diff --git a/server/core/gateway.c b/server/core/gateway.c index 4131366a4..58a7153e5 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -539,7 +539,7 @@ return_succp: static bool resolve_maxscale_homedir( char** p_home_dir) { - bool succp = false; + bool succp; char* tmp; char* log_context = NULL; @@ -627,15 +627,25 @@ check_home_dir: free(errstr); free(logstr); + succp = false; } - else if (!daemon_mode) + else { - fprintf(stderr, - "Using %s as MAXSCALE_HOME = %s\n", - log_context, - tmp); + succp = true; + + if (!daemon_mode) + { + fprintf(stderr, + "Using %s as MAXSCALE_HOME = %s\n", + log_context, + tmp); + } } } + else + { + succp = false; + } free (tmp); if (log_context != NULL) @@ -1392,7 +1402,7 @@ int main(int argc, char **argv) { if (!resolve_maxscale_homedir(&home_dir)) { - ss_dassert(home_dir == NULL); + ss_dassert(home_dir != NULL); rc = MAXSCALE_HOMELESS; goto return_main; } diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 14a80cd36..ea2cb4a95 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -1,3 +1,4 @@ +add_executable(test_mysql_users test_mysql_users.c) add_executable(test_hash testhash.c) add_executable(test_hint testhint.c) add_executable(test_spinlock testspinlock.c) @@ -10,6 +11,7 @@ add_executable(test_service testservice.c) add_executable(test_server testserver.c) add_executable(test_users testusers.c) add_executable(test_adminusers testadminusers.c) +target_link_libraries(test_mysql_users fullcore MySQLClient) target_link_libraries(test_hash fullcore) target_link_libraries(test_hint fullcore) target_link_libraries(test_spinlock fullcore) @@ -22,6 +24,7 @@ target_link_libraries(test_service fullcore) target_link_libraries(test_server fullcore) target_link_libraries(test_users fullcore) target_link_libraries(test_adminusers fullcore) +add_test(testMySQLUsers test_mysql_users) add_test(TestHash test_hash) add_test(TestHint test_hint) add_test(TestSpinlock test_spinlock) diff --git a/server/core/test/makefile.mysql_users b/server/core/test/makefile.mysql_users deleted file mode 100644 index 074188555..000000000 --- a/server/core/test/makefile.mysql_users +++ /dev/null @@ -1,32 +0,0 @@ -# cleantests - clean local and subdirectories' tests -# buildtests - build all local and subdirectories' tests -# runtests - run all local tests -# testall - clean, build and run local and subdirectories' tests - -include ../../../build_gateway.inc -include ../../../makefile.inc - -CC=cc -DEBUG=Y -cleantests: - - $(DEL) *.o - - $(DEL) test_mysql_users - - $(DEL) *~ - -testall: cleantests buildtests runtests - -buildtests : - $(CC) $(CFLAGS) \ - -I$(ROOT_PATH)/server/include \ - -I$(ROOT_PATH)/utils \ - -I$(ROOT_PATH)/log_manager \ - test_mysql_users.c ../secrets.o ../service.o ../gwbitmask.o ../load_utils.o ../session.o ../poll.o ../dcb.o ../utils.o ../buffer.o ../gw_utils.o ../hashtable.o ../atomic.o ../spinlock.o ../users.o ../dbusers.o ../../../utils/skygw_utils.o ../../../log_manager/log_manager.o -o test_mysql_users -L$(EMBEDDED_LIB) -lmysqlclient -lpthread -lssl -lz -lm -lcrypt -lcrypto -ldl -laio -lrt -lstdc++ -runtests: - @echo "" - @echo "-------------------------------" - @echo $(shell date) - @echo "Test MaxScale core" - @echo "-------------------------------" - @echo "" - @echo "MaxSclale Load MySQL users" - @./test_mysql_users diff --git a/server/core/test/test_mysql_users.c b/server/core/test/test_mysql_users.c index 7dc789d83..1d0193c45 100644 --- a/server/core/test/test_mysql_users.c +++ b/server/core/test/test_mysql_users.c @@ -24,6 +24,7 @@ * Date Who Description * 14/02/2014 Massimiliano Pinto Initial implementation * 17/02/2014 Massimiliano Pinto Added check ipv4 + * 03/10/2014 Massimiliano Pinto Added check for wildcard hosts * * @endverbatim */ @@ -77,6 +78,7 @@ int set_and_get_single_mysql_users_ipv4(char *username, unsigned long ipv4, char /* add user@host as key and passwd as value in the MySQL users hash table */ if (!mysql_users_add(mysql_users, &key, password)) { fprintf(stderr, "Failed adding %s@%s(%lu)\n", username, ret_ip, fix_ipv4); + users_free(mysql_users); return 1; } @@ -107,6 +109,7 @@ int set_and_get_single_mysql_users(char *username, char *hostname, char *passwor char *fetch_data; mysql_users = mysql_users_alloc(); + /* prepare the user@host data struct */ memset(&serv_addr, 0, sizeof(serv_addr)); memset(&key, 0, sizeof(key)); @@ -115,6 +118,7 @@ int set_and_get_single_mysql_users(char *username, char *hostname, char *passwor if (hostname) if(!setipaddress(&serv_addr.sin_addr, hostname)) { fprintf(stderr, "setipaddress failed for host [%s]\n", hostname); + users_free(mysql_users); return 1; } if (username) @@ -129,6 +133,7 @@ int set_and_get_single_mysql_users(char *username, char *hostname, char *passwor /* add user@host as key and passwd as value in the MySQL users hash table */ if (!mysql_users_add(mysql_users, &key, password)) { fprintf(stderr, "mysql_users_add() failed for %s@%s\n", username, hostname); + users_free(mysql_users); return 1; } @@ -138,6 +143,7 @@ int set_and_get_single_mysql_users(char *username, char *hostname, char *passwor if (hostname) if(!setipaddress(&serv_addr.sin_addr, hostname)) { fprintf(stderr, "setipaddress failed for host [%s]\n", hostname); + users_free(mysql_users); return 1; } key.user = username; @@ -153,6 +159,73 @@ int set_and_get_single_mysql_users(char *username, char *hostname, char *passwor return 0; } +int set_and_get_mysql_users_wildcards(char *username, char *hostname, char *password, char *from) { + USERS *mysql_users; + int ret; + struct sockaddr_in client_addr; + DCB *dcb; + SERVICE *service; + + dcb = dcb_alloc(DCB_ROLE_INTERNAL); + + if (dcb == NULL) { + fprintf(stderr, "dcb_alloc() failed\n"); + return 1; + } + if ((service = (SERVICE *)calloc(1, sizeof(SERVICE))) == NULL) { + fprintf(stderr, "service_alloc() failed\n"); + dcb_free(dcb); + return 1; + } + + memset(&client_addr, 0, sizeof(client_addr)); + + if (hostname) { + if(!setipaddress(&client_addr.sin_addr, from)) { + fprintf(stderr, "setipaddress failed for host [%s]\n", from); + free(service); + dcb_free(dcb); + return 1; + } + } + + /* client IPv4 in raw data*/ + memcpy(&dcb->ipv4, (struct sockaddr_in *)&client_addr, sizeof(struct sockaddr_in)); + + dcb->service = service; + + mysql_users = mysql_users_alloc(); + + service->users = mysql_users; + + + // the routine returns 1 on success + ret = add_mysql_users_with_host_ipv4(mysql_users, username, hostname, password); + if (!ret) { + fprintf(stderr, "add_mysql_users_with_host_ipv4 passed(%s@%s, %s) FAILED\n", username, hostname, password); + users_free(mysql_users); + free(service); + dcb_free(dcb); + + return 1; + } else { + char db_passwd[100]=""; + + dcb->remote=strdup(from); + //fprintf(stderr, "add_mysql_users_with_host_ipv4 passed(%s@%s, %s) OK\n", username, hostname, password); + + fprintf(stderr, "Checking '%s' @ '%s' against (%s@%s)\n", username, from, username, hostname); + + // returns 0 on success + ret = gw_find_mysql_user_password_sha1(username, db_passwd, dcb); + } + + users_free(mysql_users); + free(service); + dcb_free(dcb); + + return ret; +} int main() { int ret; @@ -180,6 +253,7 @@ int main() { assert(ret == 1); ret = set_and_get_single_mysql_users(NULL, NULL, NULL); assert(ret == 1); + ret = set_and_get_single_mysql_users_ipv4("negative", -467295, "_ncd"); assert(ret == 1); ret = set_and_get_single_mysql_users_ipv4("extra", 0xFFFFFFFFFUL * 100, "JJcd"); @@ -189,19 +263,77 @@ int main() { ret = set_and_get_single_mysql_users_ipv4(NULL, '\0', "JJcd"); assert(ret == 1); + for (i = 256*256*256; i <= 256*256*256 + 5; i++) { char user[129] = ""; snprintf(user, 128, "user_%i", k); ret = set_and_get_single_mysql_users_ipv4(user, i, "JJcd"); + assert(ret == 0); k++; } + ret = set_and_get_mysql_users_wildcards("pippo", "%", "one", "127.0.0.1"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + + ret = set_and_get_mysql_users_wildcards("pippo", "%", "", "127.0.0.1"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + + ret = set_and_get_mysql_users_wildcards("pippo", "%", "two", "192.168.2.2"); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.168.1.%", "foo", "192.168.2.2"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.168.%.%", "foo", "192.168.2.2"); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.%.%.%", "foo", "192.68.0.2"); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.%.%.%", "foo", "192.0.0.2"); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.0.%.%", "foo", "192.2.0.2"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.0.0.1", "foo", "192.0.0.2"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.0.%.%", "foo", "192.1.0.2"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.0.0.%", "y78764o", "192.3.2.1"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.0.%.%", "1234567890123456789012345678901234567890", "192.3.2.1"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.%.%.%", "1234567890123456789012345678901234567890f8__uuo5", "192.3.2.1"); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.0.0.%", "fo887778o", "192.134.0.2"); + if (ret) fprintf(stderr, "\t-- Expecting no match\n"); + assert(ret == 1); + fprintf(stderr, "----------------\n"); fprintf(stderr, "<<< Test completed\n"); time(&t); fprintf(stderr, "%s\n", asctime(localtime(&t))); - return ret; + return 0; } diff --git a/server/include/dbusers.h b/server/include/dbusers.h index f9f4a50ab..bcaff05ad 100644 --- a/server/include/dbusers.h +++ b/server/include/dbusers.h @@ -32,6 +32,7 @@ * 25/06/13 Mark Riddoch Initial implementation * 25/02/13 Massimiliano Pinto Added users table refresh rate default values * 28/02/14 Massimiliano Pinto Added MySQL user and host data structure + * 03/10/14 Massimiliano Pinto Added netmask to MySQL user and host data structure * * @endverbatim */ @@ -52,11 +53,13 @@ typedef struct mysql_user_host_key { char *user; struct sockaddr_in ipv4; + int netmask; } MYSQL_USER_HOST; extern int load_mysql_users(SERVICE *service); extern int reload_mysql_users(SERVICE *service); extern int mysql_users_add(USERS *users, MYSQL_USER_HOST *key, char *auth); +extern int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *passwd); extern USERS *mysql_users_alloc(); extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key); extern int replace_mysql_users(SERVICE *service); diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 06f83f3e1..92317cb40 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -558,6 +558,7 @@ static char* create_auth_fail_str( { sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES")); } + free(uname); retblock: return errstr; diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 684829fdf..c098263c2 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -31,6 +31,9 @@ * 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@% + * 29/09/2014 Massimiliano Pinto Added Mysql user@host authentication with wildcard in IPv4 hosts: + * x.y.z.%, x.y.%.%, x.%.%.% + * 03/10/2014 Massimiliano Pinto Added netmask for wildcard in IPv4 hosts. * */ @@ -1310,7 +1313,7 @@ int gw_check_mysql_scramble_data(DCB *dcb, uint8_t *token, unsigned int token_le /** * gw_find_mysql_user_password_sha1 * - * The routine fetches look for an user int he MaxScale users' table + * The routine fetches look for an user int the MaxScale users' table * The users' table is dcb->service->users or a different one specified with void *repository * * If found the HEX password, representing sha1(sha1(password)), is converted in binary data and @@ -1334,6 +1337,7 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, key.user = username; memcpy(&key.ipv4, client, sizeof(struct sockaddr_in)); + key.netmask = 32; LOGIF(LD, (skygw_log_write_flush( @@ -1343,73 +1347,119 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, key.user, dcb->remote))); - /* look for user@current_host now */ + /* look for user@current_ipv4 now */ user_password = mysql_users_fetch(service->users, &key); if (!user_password) { - /* The user is not authenticated @ current host */ + /* The user is not authenticated @ current IPv4 */ - /* 1) Check for localhost first. - * The check for localhost is 127.0.0.1 (IPv4 only) - */ - - if ((key.ipv4.sin_addr.s_addr == 0x0100007F) && !dcb->service->localhost_match_wildcard_host) { - /* Skip the wildcard check and return 1 */ - LOGIF(LE, - (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : user %s@%s not found, try set " - "'localhost_match_wildcard_host=1' in " - "service definition of the configuration " - "file.", - key.user, - dcb->remote))); - - return 1; - } - - /* 2) Continue and check for wildcard host, user@% - * Return 1 if no match - */ - - memset(&key.ipv4, 0, sizeof(struct sockaddr_in)); - - LOGIF(LD, - (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [MySQL Client Auth], checking user [%s@%s] with wildcard host [%%]", - pthread_self(), - key.user, - dcb->remote))); - - user_password = mysql_users_fetch(service->users, &key); - - if (!user_password) { - /* the user@% was not found. - * Return 1 + while (1) { + /* + * (1) Check for localhost first: 127.0.0.1 (IPv4 only) */ + + if ((key.ipv4.sin_addr.s_addr == 0x0100007F) && !dcb->service->localhost_match_wildcard_host) { + /* Skip the wildcard check and return 1 */ + LOGIF(LE, + (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : user %s@%s not found, try set " + "'localhost_match_wildcard_host=1' in " + "service definition of the configuration " + "file.", + key.user, + dcb->remote))); + + break; + } + + /* + * (2) check for possible IPv4 class C,B,A networks + */ + + /* Class C check */ + key.ipv4.sin_addr.s_addr &= 0x00FFFFFF; + key.netmask -= 8; + + user_password = mysql_users_fetch(service->users, &key); + + if (user_password) { + break; + } + + /* Class B check */ + key.ipv4.sin_addr.s_addr &= 0x0000FFFF; + key.netmask -= 8; + + user_password = mysql_users_fetch(service->users, &key); + + if (user_password) { + break; + } + + /* Class A check */ + key.ipv4.sin_addr.s_addr &= 0x000000FF; + key.netmask -= 8; + + user_password = mysql_users_fetch(service->users, &key); + + if (user_password) { + break; + } + + /* + * (3) Continue check for wildcard host, user@% + */ + + memset(&key.ipv4, 0, sizeof(struct sockaddr_in)); + key.netmask = 0; + LOGIF(LD, (skygw_log_write_flush( LOGFILE_DEBUG, - "%lu [MySQL Client Auth], user [%s@%s] not existent", + "%lu [MySQL Client Auth], checking user [%s@%s] with wildcard host [%%]", pthread_self(), key.user, dcb->remote))); - return 1; + + user_password = mysql_users_fetch(service->users, &key); + + if (!user_password) { + /* + * the user@% has not been found. + */ + + LOGIF(LD, + (skygw_log_write_flush( + LOGFILE_DEBUG, + "%lu [MySQL Client Auth], user [%s@%s] not existent", + pthread_self(), + key.user, + dcb->remote))); + break; + } + + break; } } - /* user@host found: now check the password - * - * Convert the hex data (40 bytes) to binary (20 bytes). - * The gateway_password represents the SHA1(SHA1(real_password)). - * Please note: the real_password is unknown and SHA1(real_password) is unknown as well - */ + /* If user@host has been found we get the the password in binary format*/ + if (user_password) { + /* + * Convert the hex data (40 bytes) to binary (20 bytes). + * The gateway_password represents the SHA1(SHA1(real_password)). + * Please note: the real_password is unknown and SHA1(real_password) is unknown as well + */ + int passwd_len=strlen(user_password); + if (passwd_len) { + passwd_len = (passwd_len <= (SHA_DIGEST_LENGTH * 2)) ? passwd_len : (SHA_DIGEST_LENGTH * 2); + gw_hex2bin(gateway_password, user_password, passwd_len); + } - if (strlen(user_password)) - gw_hex2bin(gateway_password, user_password, SHA_DIGEST_LENGTH * 2); - - return 0; + return 0; + } else { + return 1; + } } /** @@ -1652,11 +1702,9 @@ void protocol_archive_srv_command( s1 = &p->protocol_command; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [protocol_archive_srv_command] Move command %s from fd %d " - "to command history.", - pthread_self(), + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Move command %s from fd %d to command history.", STRPACKETTYPE(s1->scom_cmd), p->owner_dcb->fd))); @@ -1728,8 +1776,8 @@ void protocol_add_srv_command( p->protocol_command.scom_next = server_command_init(NULL, cmd); } - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, "Added command %s to fd %d.", STRPACKETTYPE(cmd), p->owner_dcb->fd))); @@ -1739,8 +1787,8 @@ void protocol_add_srv_command( while (c != NULL && c->scom_cmd != MYSQL_COM_UNDEFINED) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, "fd %d : %d %s", p->owner_dcb->fd, c->scom_cmd, diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 505c7c5d1..07820b076 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1708,6 +1708,12 @@ static int routeQuery( } goto retblock; } + /** If buffer is not contiguous, make it such */ + if (querybuf->next != NULL) + { + querybuf = gwbuf_make_contiguous(querybuf); + } + master_dcb = router_cli_ses->rses_master_ref->bref_dcb; CHK_DCB(master_dcb); diff --git a/server/test/CMakeLists.txt b/server/test/CMakeLists.txt deleted file mode 100644 index 47eb22199..000000000 --- a/server/test/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -if(BUILD_TESTS) - install(FILES MaxScale_test.cnf DESTINATION etc RENAME MaxScale.cnf) -endif()