diff --git a/server/core/dbusers.c b/server/core/dbusers.c index bfb5bdc2e..95500691f 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -137,6 +137,27 @@ int add_wildcard_users(USERS *users, static int gw_mysql_set_timeouts(MYSQL* handle); +/** + * Check if the IP address of the user matches the one in the grant. This assumes + * that the grant has one or more single-character wildcards in it. + * @param userhost User host address + * @param wildcardhost Host address in the grant + * @return True if the host address matches + */ +bool host_matches_singlechar_wildcard(const char* user, const char* wild) +{ + while (*user != '\0' && *wild != '\0') + { + if (*user != *wild && *wild != '_') + { + return false; + } + user++; + wild++; + } + return true; +} + /** * Load the user/passwd form mysql.user table into the service users' hashtable * environment. @@ -252,6 +273,53 @@ HASHTABLE *oldresources; return i; } +/** + * Check if the IP address is a valid MySQL IP address. The IP address can contain + * single or multi-character wildcards as used by MySQL. + * @param host IP address to check + * @return True if the address is a valid, MySQL type IP address + */ +bool is_ipaddress(const char* host) +{ + while (*host != '\0') + { + if (!isdigit(*host) && *host != '.' && *host != '_' && *host != '%') + { + return false; + } + host++; + } + return true; +} + +/** + * Check if an IP address has single-character wildcards. A single-character + * wildcard is represented by an underscore in the MySQL hostnames. + * @param host Hostname to check + * @return True if the hostname is a valid IP address with a single character wildcard + */ +bool host_has_singlechar_wildcard(const char *host) +{ + const char* chrptr = host; + bool retval = false; + + while (*chrptr != '\0') + { + if (!isdigit(*chrptr) && *chrptr != '.') + { + if (*chrptr == '_') + { + retval = true; + } + else + { + return false; + } + } + chrptr++; + } + return retval; +} /** * Add a new MySQL user with host, password and netmask into the service users table @@ -308,6 +376,12 @@ int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *p if (strcmp(host, "%") == 0) { strcpy(ret_ip, "0.0.0.0"); key.netmask = 0; + } else if (strnlen(host, MYSQL_HOST_MAXLEN + 1) <= MYSQL_HOST_MAXLEN && + is_ipaddress(host) && + host_has_singlechar_wildcard(host)) { + strcpy(key.hostname, host); + strcpy(ret_ip, "0.0.0.0"); + key.netmask = 0; } else { /* hostname without % wildcards has netmask = 32 */ key.netmask = normalize_hostname(host, ret_ip); @@ -1719,7 +1793,17 @@ static int uh_cmpfun( void* v1, void* v2) { if (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) && (hu1->netmask >= hu2->netmask)) { + /** If the stored user has the unmodified address stored, that means we were not able + * to resolve it at the time we loaded the users. We need to check if the + * address contains wildcards and if the user's address matches that. */ + + const bool wildcard_host = strlen(hu2->hostname) > 0 && strlen(hu1->hostname) > 0; + + if ((strcmp(hu1->user, hu2->user) == 0) && + /** Check for wildcard hostnames */ + ((wildcard_host && host_matches_singlechar_wildcard(hu1->hostname, hu2->hostname)) || + /** If no wildcard hostname is stored, check for network address. */ + (!wildcard_host && (hu1->ipv4.sin_addr.s_addr == hu2->ipv4.sin_addr.s_addr) && (hu1->netmask >= hu2->netmask)))) { /* if no database name was passed, auth is ok */ if (hu1->resource == NULL || (hu1->resource && !strlen(hu1->resource))) { @@ -1806,6 +1890,10 @@ static void *uh_keydup(void* key) { return NULL; } + ss_dassert(strnlen(rval->hostname, MYSQL_HOST_MAXLEN + 1) <= MYSQL_HOST_MAXLEN); + strncpy(rval->hostname, current_key->hostname, MYSQL_HOST_MAXLEN); + rval->hostname[MYSQL_HOST_MAXLEN] = '\0'; + memcpy(&rval->ipv4, ¤t_key->ipv4, sizeof(struct sockaddr_in)); memcpy(&rval->netmask, ¤t_key->netmask, sizeof(int)); diff --git a/server/include/dbusers.h b/server/include/dbusers.h index ab7ff1638..17e5b97ef 100644 --- a/server/include/dbusers.h +++ b/server/include/dbusers.h @@ -62,6 +62,7 @@ typedef struct mysql_user_host_key { struct sockaddr_in ipv4; int netmask; char *resource; + char hostname[MYSQL_HOST_MAXLEN + 1]; } MYSQL_USER_HOST; extern int load_mysql_users(SERVICE *service); diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index c2bec47da..553af4090 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -1463,6 +1463,10 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, memcpy(&key.ipv4, client, sizeof(struct sockaddr_in)); key.netmask = 32; key.resource = client_data->db; + if(strlen(dcb->remote) < MYSQL_HOST_MAXLEN) + { + strcpy(key.hostname, dcb->remote); + } LOGIF(LD, (skygw_log_write_flush(