From 64732cdb0d95320d9274b26fcac6e617d6473248 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 27 Sep 2016 22:25:05 +0000 Subject: [PATCH] fixes relating to intercepting DNS queries over IPv6 --- src/dns/Makefile.am | 1 + src/dns/dnsstub.c | 10 ++-- src/dns/gnunet-helper-dns.c | 95 +++++++++++++++++++++++++++++++++++- src/dns/gnunet-service-dns.c | 8 +-- src/dns/test_gnunet_dns.sh | 6 +-- src/include/gnunet_tun_lib.h | 14 ++++++ src/tun/tun.c | 42 +++++++++++++++- 7 files changed, 163 insertions(+), 13 deletions(-) diff --git a/src/dns/Makefile.am b/src/dns/Makefile.am index 45b86c71b..f5fcf3782 100644 --- a/src/dns/Makefile.am +++ b/src/dns/Makefile.am @@ -89,6 +89,7 @@ libgnunetdnsparser_la_LDFLAGS = \ libgnunetdnsstub_la_SOURCES = \ dnsstub.c libgnunetdnsstub_la_LIBADD = \ + $(top_builddir)/src/tun/libgnunettun.la \ $(top_builddir)/src/util/libgnunetutil.la $(XLIB) libgnunetdnsstub_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) \ diff --git a/src/dns/dnsstub.c b/src/dns/dnsstub.c index b3cd2817e..68cd55275 100644 --- a/src/dns/dnsstub.c +++ b/src/dns/dnsstub.c @@ -24,6 +24,7 @@ */ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_tun_lib.h" #include "gnunet_dnsstub_lib.h" /** @@ -381,9 +382,7 @@ GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx, _("Failed to send DNS request to %s\n"), GNUNET_a2s (sa, salen)); rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); - return rs; - } @@ -441,9 +440,10 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, } dns = (struct GNUNET_TUN_DnsHeader *) buf; if ( (addrlen != rs->addrlen) || - (0 != memcmp (&rs->addr, - &addr, - addrlen)) || + (GNUNET_YES != + GNUNET_TUN_sockaddr_cmp ((struct sockaddr *) &rs->addr, + (struct sockaddr *) &addr, + GNUNET_YES)) || (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value_us) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, diff --git a/src/dns/gnunet-helper-dns.c b/src/dns/gnunet-helper-dns.c index 2f723d09d..1d411379f 100644 --- a/src/dns/gnunet-helper-dns.c +++ b/src/dns/gnunet-helper-dns.c @@ -100,6 +100,11 @@ struct in6_ifreq */ static const char *sbin_iptables; +/** + * Name and full path of IPTABLES binary. + */ +static const char *sbin_ip6tables; + /** * Name and full path of sysctl binary */ @@ -757,7 +762,7 @@ main (int argc, char *const*argv) return 254; } #endif - if (0 == strncmp(argv[6], "1", 2)) + if (0 == strncmp (argv[6], "1", 2)) nortsetup = 1; if (0 == nortsetup) @@ -774,6 +779,17 @@ main (int argc, char *const*argv) strerror (errno)); return 3; } + if (0 == access ("/sbin/ip6tables", X_OK)) + sbin_ip6tables = "/sbin/ip6tables"; + else if (0 == access ("/usr/sbin/ip6tables", X_OK)) + sbin_ip6tables = "/usr/sbin/ip6tables"; + else + { + fprintf (stderr, + "Fatal: executable ip6tables not found in approved directories: %s\n", + strerror (errno)); + return 3; + } if (0 == access ("/sbin/ip", X_OK)) sbin_ip = "/sbin/ip"; else if (0 == access ("/usr/sbin/ip", X_OK)) @@ -942,6 +958,16 @@ main (int argc, char *const*argv) if (0 != fork_and_exec (sbin_iptables, mangle_args)) goto cleanup_rest; } + { + char *const mangle_args[] = + { + "ip6tables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p", + "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j", + "ACCEPT", NULL + }; + if (0 != fork_and_exec (sbin_ip6tables, mangle_args)) + goto cleanup_rest; + } /* Mark all of the other DNS traffic using our mark DNS_MARK */ { char *const mark_args[] = @@ -953,6 +979,16 @@ main (int argc, char *const*argv) if (0 != fork_and_exec (sbin_iptables, mark_args)) goto cleanup_mangle_1; } + { + char *const mark_args[] = + { + "ip6tables", "-t", "mangle", "-I", "OUTPUT", "2", "-p", + "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, + NULL + }; + if (0 != fork_and_exec (sbin_ip6tables, mark_args)) + goto cleanup_mangle_1; + } /* Forward all marked DNS traffic to our DNS_TABLE */ { char *const forward_args[] = @@ -962,6 +998,14 @@ main (int argc, char *const*argv) if (0 != fork_and_exec (sbin_ip, forward_args)) goto cleanup_mark_2; } + { + char *const forward_args[] = + { + "ip", "-6", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL + }; + if (0 != fork_and_exec (sbin_ip, forward_args)) + goto cleanup_mark_2; + } /* Finally, add rule in our forwarding table to pass to our virtual interface */ { char *const route_args[] = @@ -972,6 +1016,15 @@ main (int argc, char *const*argv) if (0 != fork_and_exec (sbin_ip, route_args)) goto cleanup_forward_3; } + { + char *const route_args[] = + { + "ip", "-6", "route", "add", "default", "dev", dev, + "table", DNS_TABLE, NULL + }; + if (0 != fork_and_exec (sbin_ip, route_args)) + goto cleanup_forward_3; + } } /* drop privs *except* for the saved UID; this is not perfect, but better @@ -1028,6 +1081,16 @@ main (int argc, char *const*argv) if (0 != fork_and_exec (sbin_ip, route_clean_args)) r += 1; } + if (0 == nortsetup) + { + char *const route_clean_args[] = + { + "ip", "-6", "route", "del", "default", "dev", dev, + "table", DNS_TABLE, NULL + }; + if (0 != fork_and_exec (sbin_ip, route_clean_args)) + r += 1; + } cleanup_forward_3: if (0 == nortsetup) { @@ -1038,6 +1101,15 @@ main (int argc, char *const*argv) if (0 != fork_and_exec (sbin_ip, forward_clean_args)) r += 2; } + if (0 == nortsetup) + { + char *const forward_clean_args[] = + { + "ip", "-6", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL + }; + if (0 != fork_and_exec (sbin_ip, forward_clean_args)) + r += 2; + } cleanup_mark_2: if (0 == nortsetup) { @@ -1049,6 +1121,16 @@ main (int argc, char *const*argv) if (0 != fork_and_exec (sbin_iptables, mark_clean_args)) r += 4; } + if (0 == nortsetup) + { + char *const mark_clean_args[] = + { + "ip6tables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp", + "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL + }; + if (0 != fork_and_exec (sbin_ip6tables, mark_clean_args)) + r += 4; + } cleanup_mangle_1: if (0 == nortsetup) { @@ -1061,6 +1143,17 @@ main (int argc, char *const*argv) if (0 != fork_and_exec (sbin_iptables, mangle_clean_args)) r += 8; } + if (0 == nortsetup) + { + char *const mangle_clean_args[] = + { + "ip6tables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp", + "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT", + NULL + }; + if (0 != fork_and_exec (sbin_ip6tables, mangle_clean_args)) + r += 8; + } cleanup_rest: /* close virtual interface */ diff --git a/src/dns/gnunet-service-dns.c b/src/dns/gnunet-service-dns.c index 50aa730e7..52f924cdf 100644 --- a/src/dns/gnunet-service-dns.c +++ b/src/dns/gnunet-service-dns.c @@ -410,7 +410,7 @@ request_done (struct RequestRecord *rr) destination_port = src->sin6_port; GNUNET_TUN_initialize_ipv6_header (&ip6, IPPROTO_UDP, - reply_len - sizeof (struct GNUNET_TUN_IPv6Header), + reply_len - off - sizeof (struct GNUNET_TUN_IPv6Header), &dst->sin6_addr, &src->sin6_addr); GNUNET_memcpy (&buf[off], &ip6, sizeof (ip6)); @@ -916,7 +916,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client, ip6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1]; if ( (msize < sizeof (struct GNUNET_TUN_IPv6Header)) || (ip6->version != 6) || - (ntohs (ip6->payload_length) != msize) || + (ntohs (ip6->payload_length) != msize - sizeof (struct GNUNET_TUN_IPv6Header)) || (ip6->next_header != IPPROTO_UDP) ) { /* non-IP/UDP packet received on TUN (or with extensions) */ @@ -924,7 +924,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client, _("Received malformed IPv6-UDP packet on TUN interface.\n")); return GNUNET_OK; } - udp = (const struct GNUNET_TUN_UdpHeader*) &ip6[1]; + udp = (const struct GNUNET_TUN_UdpHeader *) &ip6[1]; msize -= sizeof (struct GNUNET_TUN_IPv6Header); break; default: @@ -939,6 +939,8 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client, (DNS_PORT != ntohs (udp->destination_port)) ) { /* non-DNS packet received on TUN, ignore */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("DNS interceptor got non-DNS packet (dropped)\n")); GNUNET_STATISTICS_update (stats, gettext_noop ("# Non-DNS UDP packet received via TUN interface"), 1, GNUNET_NO); diff --git a/src/dns/test_gnunet_dns.sh b/src/dns/test_gnunet_dns.sh index ab8cca4b1..84a57f87e 100755 --- a/src/dns/test_gnunet_dns.sh +++ b/src/dns/test_gnunet_dns.sh @@ -6,7 +6,7 @@ then echo "This test only works if run as root. Skipping." exit 77 fi -if ! which sudo > /dev/null +if ! which sudo > /dev/null then echo "This test requires sudo. Skipping." exit 77 @@ -17,7 +17,7 @@ then exit 77 fi if ! which nslookup > /dev/null -then +then echo "This test requires nslookup. Skipping." exit 77 fi @@ -38,7 +38,7 @@ gnunet-dns-redirector -c dns.conf -4 127.0.0.1 & sleep 1 # need to run 'nslookup' as 'nobody', as gnunet-service-dns runs as root # and thus 'root' is excepted from DNS interception! -LO=`sudo -u nobody nslookup gnunet.org | grep Address | tail -n1` +LO=`sudo -u nobody nslookup -type=A gnunet.org | grep Address | tail -n1` if [ "$LO" != "Address: 127.0.0.1" ] then echo "Fail: got address $LO, wanted 127.0.0.1" diff --git a/src/include/gnunet_tun_lib.h b/src/include/gnunet_tun_lib.h index 822a661cf..7bef0a6be 100644 --- a/src/include/gnunet_tun_lib.h +++ b/src/include/gnunet_tun_lib.h @@ -917,6 +917,20 @@ GNUNET_TUN_service_name_to_hash (const char *service_name, struct GNUNET_HashCode *hc); +/** + * Check if two sockaddrs are equal. + * + * @param sa one address + * @param sb another address + * @param include_port also check ports + * @return #GNUNET_YES if they are equal + */ +int +GNUNET_TUN_sockaddr_cmp (const struct sockaddr *sa, + const struct sockaddr *sb, + int include_port); + + /** * Compute the CADET port given a service descriptor * (returned from #GNUNET_TUN_service_name_to_hash) and diff --git a/src/tun/tun.c b/src/tun/tun.c index f39b1898b..b55de1ea9 100644 --- a/src/tun/tun.c +++ b/src/tun/tun.c @@ -70,7 +70,7 @@ GNUNET_TUN_initialize_ipv4_header (struct GNUNET_TUN_IPv4Header *ip, * * @param ip header to initialize * @param protocol protocol to use (i.e. IPPROTO_UDP), technically "next_header" for IPv6 - * @param payload_length number of bytes of payload that follow (excluding IPv4 header) + * @param payload_length number of bytes of payload that follow (excluding IPv6 header) * @param src source IP address to use * @param dst destination IP address to use */ @@ -268,4 +268,44 @@ GNUNET_TUN_calculate_icmp_checksum (struct GNUNET_TUN_IcmpHeader *icmp, } +/** + * Check if two sockaddrs are equal. + * + * @param sa one address + * @param sb another address + * @param include_port also check ports + * @return #GNUNET_YES if they are equal + */ +int +GNUNET_TUN_sockaddr_cmp (const struct sockaddr *sa, + const struct sockaddr *sb, + int include_port) +{ + if (sa->sa_family != sb->sa_family) + return GNUNET_NO; + + switch (sa->sa_family) + { + case AF_INET: + { + const struct sockaddr_in *sa4 = (const struct sockaddr_in *) sa; + const struct sockaddr_in *sb4 = (const struct sockaddr_in *) sb; + return (sa4->sin_addr.s_addr == sb4->sin_addr.s_addr); + } + case AF_INET6: + { + const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *) sa; + const struct sockaddr_in6 *sb6 = (const struct sockaddr_in6 *) sb; + + return (0 == memcmp(&sa6->sin6_addr, + &sb6->sin6_addr, + sizeof (struct in6_addr))); + } + default: + GNUNET_break (0); + return GNUNET_SYSERR; + } +} + + /* end of tun.c */ -- 2.25.1