Brad House's validation that DNS response address matches the request address

This commit is contained in:
Yang Tse 2008-08-25 03:34:50 +00:00
parent f164260eee
commit 423a18cecc
3 changed files with 28 additions and 1 deletions

View File

@ -1,5 +1,17 @@
Changelog for the c-ares project
* Aug 25 2008 (Yang Tse)
- Improvement by Brad House:
This patch addresses an issue in which a response could be sent back to the
source port of a client from a different address than the request was made to.
This is one form of a DNS cache poisoning attack.
The patch simply uses recvfrom() rather than recv() and validates that the
address returned from recvfrom() matches the address of the server we have
connected to. Only necessary on UDP sockets as they are connection-less, TCP
is unaffected.
* Aug 4 2008 (Daniel Stenberg)
- Fix by Tofu Linden:

View File

@ -9,6 +9,7 @@ This is what's new and changed in the c-ares 1.5.3 release:
o library will now be built with _REENTRANT symbol defined if needed
o Improved configure detection of number of arguments for getservbyport_r
o Improved query-ID randomness
o Validate that DNS response address matches the request address
Thanks go to these friendly people for their efforts and contributions:

View File

@ -429,6 +429,8 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds,
int i;
ssize_t count;
unsigned char buf[PACKETSZ + 1];
struct sockaddr_in from;
socklen_t fromlen;
if(!read_fds && (read_fd == ARES_SOCKET_BAD))
/* no possible action */
@ -462,11 +464,23 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds,
/* To reduce event loop overhead, read and process as many
* packets as we can. */
do {
count = sread(server->udp_socket, buf, sizeof(buf));
/* Must memset 'from' to 0 as recvfrom() on some systems may
* not use 'from' at all if it doesn't support receiving the
* source address of the response */
memset(&from, 0, sizeof(from));
fromlen = sizeof(from);
count = (ssize_t)recvfrom(server->udp_socket, (void *)buf, sizeof(buf),
0, (struct sockaddr *)&from, &fromlen);
if (count == -1 && try_again(SOCKERRNO))
continue;
else if (count <= 0)
handle_error(channel, i, now);
else if (fromlen && from.sin_addr.s_addr != 0 &&
from.sin_addr.s_addr != server->addr.s_addr)
/* Address response came from did not match the address
* we sent the request to. Someone may be attempting
* to perform a cache poisoning attack */
break;
else
process_answer(channel, buf, (int)count, i, 0, now);
} while (count > 0);