#At file:///home/thek/Development/cpp/mysqlbzr/mysql-6.0-bug38247/
2781 Kristofer Pettersson 2008-08-25
Bug#38247 Server does not resolve connecting ip's
Server does not resolve the ip's of incoming connections.
This patch provides a quick solution for this bug by striping the "::ffff:"
prefix from a mapped IPv4.
modified:
sql/hostname.cc
vio/viosocket.c
per-file messages:
sql/hostname.cc
* Memcpy memory block size isn't sizeof(struct addrinfo) but sizeof(struct sockaddr_storage). This caused a crash.
* Request getnameinfo with host name representation and not numeric representation.
* Don't call freeaddrinfo if we failed to get a res_lst. (caused crash)
* Make sure we project the character representation of an IPv4 address to the address space of IPv4 and not as a mapped IPv4.
vio/viosocket.c
* Transform mapped IPv4 to old IPv4 format.
=== modified file 'sql/hostname.cc'
--- a/sql/hostname.cc 2008-07-08 16:01:41 +0000
+++ b/sql/hostname.cc 2008-08-25 12:52:43 +0000
@@ -94,7 +94,7 @@ static void add_hostname(struct sockaddr
if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1)))
{
char *new_name;
- memcpy_fixed(&entry->ip, in, sizeof(struct addrinfo));
+ memcpy_fixed(&entry->ip, in, sizeof(struct sockaddr_storage));
if (length)
memcpy(new_name= (char *) (entry+1), name, length+1);
else
@@ -149,18 +149,21 @@ char *ip_to_hostname(struct sockaddr_sto
DBUG_ENTER("ip_to_hostname");
*errors=0;
- /* Historical comparison for 127.0.0.1 */
- gxi_error= getnameinfo((struct sockaddr *)in, addrLen,
- hostname_buff, NI_MAXHOST,
- NULL, 0, NI_NUMERICHOST);
- if (gxi_error)
+ /*
+ Get host name in numeric form.
+ */
+ if ((gxi_error= getnameinfo((struct sockaddr *)in, addrLen,
+ hostname_buff, NI_MAXHOST,
+ NULL, 0, NI_NAMEREQD)))
{
- DBUG_PRINT("error",("getnameinfo returned %d", gxi_error));
- DBUG_RETURN(0);
- }
- DBUG_PRINT("info",("resolved: %s", hostname_buff));
+ add_wrong_ip(in);
+ DBUG_RETURN(0);
+ };
- /* The next three compares are to solve historical solutions with localhost */
+ /*
+ The next three compares are to solve historical solutions with
+ localhost.
+ */
if (!memcmp(hostname_buff, "127.0.0.1", sizeof("127.0.0.1")))
{
DBUG_RETURN((char *)my_localhost);
@@ -173,8 +176,17 @@ char *ip_to_hostname(struct sockaddr_sto
{
DBUG_RETURN((char *)my_localhost);
}
+
+ if (!(name= my_strdup(hostname_buff, MYF(0))))
+ {
+ DBUG_PRINT("error",("out of memory"));
+ DBUG_RETURN(0);
+ }
+
+ DBUG_PRINT("info",("resolved: %s", hostname_buff));
/* Check first if we have name in cache */
+
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
{
pthread_mutex_lock(&hostname_cache->lock);
@@ -193,14 +205,10 @@ char *ip_to_hostname(struct sockaddr_sto
pthread_mutex_unlock(&hostname_cache->lock);
}
- if (!(name= my_strdup(hostname_buff, MYF(0))))
- {
- DBUG_PRINT("error",("out of memory"));
- DBUG_RETURN(0);
- }
-
/* Don't accept hostnames that starts with digits because they may be
- false ip:s */
+ false ip:s. This can be used to exploit the current privilege system compare
+ function.
+ */
if (my_isdigit(&my_charset_latin1, name[0]))
{
char *pos;
@@ -236,23 +244,62 @@ char *ip_to_hostname(struct sockaddr_sto
#else
if (gxi_error == EAI_NONAME )
#endif
- add_wrong_ip(in);
+ add_wrong_ip(in);
my_free(name,MYF(0));
- freeaddrinfo(res_lst);
DBUG_RETURN(0);
}
/* Check that 'getaddrinfo' returned the used ip */
+ char str_tmp_addr_representation[INET6_ADDRSTRLEN];
+ if (in->ss_family == AF_INET6)
+ {
+ sockaddr_in6 *addr2= (sockaddr_in6 *)in;
+ if (!inet_ntop(AF_INET6, &(addr2->sin6_addr), str_tmp_addr_representation, INET6_ADDRSTRLEN))
+ {
+ /*
+ Address conversion failed. This should normally never happen.
+ */
+ my_free(name,MYF(0));
+ add_wrong_ip(in);
+ DBUG_RETURN(0);
+ }
+
+ /*
+ We expect the address to be a mapped IPv4 address with a prefix
+ of ::ffff: (7 characters long). Since the privilege system currently
+ expects regular IPv4 address we convert the representation IP here.
+ */
+ strmov(str_tmp_addr_representation, str_tmp_addr_representation+7);
+
+ }
+ else
+ {
+ sockaddr_in *addr2= (sockaddr_in *)in;
+ inet_ntop(AF_INET, &(addr2->sin_addr), str_tmp_addr_representation, INET_ADDRSTRLEN);
+ }
+
for (t_res= res_lst; t_res; t_res=t_res->ai_next)
{
- if (!memcmp(&(t_res->ai_addr), in,
- sizeof(struct sockaddr_storage) ) )
+ char str_addr[INET6_ADDRSTRLEN];
+ if (t_res->ai_family == AF_INET)
+ {
+ sockaddr_in *addr= (sockaddr_in *)t_res->ai_addr;
+ inet_ntop(AF_INET,&addr->sin_addr, str_addr, INET_ADDRSTRLEN);
+ }
+ else
+ {
+ sockaddr_in6 *addr= (sockaddr_in6 *)t_res->ai_addr;
+ inet_ntop(AF_INET6,&addr->sin6_addr, str_addr, INET6_ADDRSTRLEN);
+ }
+
+ if (!memcmp(str_addr,str_tmp_addr_representation,
+ strlen(str_addr)))
{
add_hostname(in, name);
freeaddrinfo(res_lst);
DBUG_RETURN(name);
- }
+ }
}
freeaddrinfo(res_lst);
DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo"));
=== modified file 'vio/viosocket.c'
--- a/vio/viosocket.c 2008-05-14 13:33:43 +0000
+++ b/vio/viosocket.c 2008-08-25 12:52:43 +0000
@@ -350,7 +350,17 @@ my_bool vio_peer_addr(Vio *vio, char *bu
*/
if (!memcmp(buf, "::ffff:127.0.0.1", sizeof("::ffff:127.0.0.1")))
strmov(buf, "127.0.0.1");
+ /*
+ We currently only support IPv4 addresses in the privilege system. If the
+ current address is a mapped IPv6 address, we strip the leading
+ characters by matching the prefix "::ffff"
+ */
+
+ if (buf[0] == ':' && buf[1] == ':' && buf[2] == 'f' && buf[3] == 'f' &&
+ buf[4] == 'f' && buf[5] == 'f')
+ strmov(buf,buf+7);
}
+
DBUG_PRINT("exit", ("addr: %s", buf));
DBUG_RETURN(0);
}