2 This file is part of GNUnet.
3 (C) 2010, 2011, 2012 Christian Grothoff
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file dns/gnunet-helper-hijack-dns.c
23 * @brief helper to install firewall rules to hijack all DNS traffic
24 * and send it to our virtual interface except for DNS traffic
25 * that originates on the specified port.
26 * @author Philipp Tölke
27 * @author Christian Grothoff
29 * This program alters the Linux firewall rules so that DNS traffic
30 * that ordinarily exits the system can be intercepted and managed by
31 * a virtual interface. In order to achieve this, DNS traffic is
32 * marked with the DNS_MARK given in below and re-routed to a custom
33 * table with the DNS_TABLE ID given below. Systems and
34 * administrators must take care to not cause conflicts with these
35 * values (it was deemed safest to hardcode them as passing these
36 * values as arguments might permit messing with arbitrary firewall
37 * rules, which would be dangerous).
39 * Note that having this binary SUID is only partially safe: it will
40 * allow redirecting (and intercepting / mangling) of all DNS traffic
41 * originating from this system by any user who can create a virtual
42 * interface (and this is again enabled by other GNUnet SUID
43 * binaries). Furthermore, even without the ability to create a
44 * tunnel interface, this code will make it possible to DoS all DNS
45 * traffic originating from the current system, simply by sending it
48 * Naturally, neither of these problems can be helped as this is the
49 * fundamental purpose of the binary. Certifying that this code is
50 * "safe" thus only means that it doesn't allow anything else (such
51 * as local priv. escalation, etc.).
53 * The following list of people have reviewed this code and considered
54 * it safe (within specifications) since the last modification (if you
55 * reviewed it, please have your name added to the list):
57 * - Christian Grothoff
62 * Name and full path of IPTABLES binary.
64 #define SBIN_IPTABLES "/sbin/iptables"
67 * Name and full path of IPTABLES binary.
69 #define SBIN_IP "/sbin/ip"
72 * Port for DNS traffic.
77 * Marker we set for our hijacked DNS traffic. We use GNUnet's
78 * port (2086) plus the DNS port (53) in HEX to make a 32-bit mark
79 * (which is hopefully long enough to not collide); so
80 * 0x08260035 = 136708149 (hopefully unique enough...).
82 #define DNS_MARK "136708149"
85 * Table we use for our DNS rules. 0-255 is the range and
86 * 0, 253, 254 and 255 are already reserved. As this is about
87 * DNS and as "53" is likely (fingers crossed!) high enough to
88 * not usually conflict with a normal user's setup, we use 53
89 * to give a hint that this has something to do with DNS.
91 #define DNS_TABLE "53"
95 * Run the given command and wait for it to complete.
97 * @param file name of the binary to run
98 * @param cmd command line arguments (as given to 'execv')
99 * @return 0 on success, 1 on any error
102 fork_and_exec (const char *file,
119 /* we are the child process */
120 (void) execv (file, cmd);
121 /* can only get here on error */
123 "exec `%s' failed: %s\n",
128 /* keep running waitpid as long as the only error we get is 'EINTR' */
129 while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
134 "waitpid failed: %s\n",
138 if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
140 /* child process completed and returned success, we're happy */
146 * Main function of "gnunet-helper-hijack-dns".
147 * Use "-d" as the first argument to remove the firewall rules.
148 * The other arguments are the DNS source port to NOT affect
149 * by the rules, followed by the name of the virtual interface
150 * to redirect all of the remaining DNS traffic to.
152 * @param argc number of arguments
153 * @param argv ["-d"] PORT VTUN
154 * @return 0 on success, otherwise code indicating type of error:
155 * 1 wrong number of arguments
156 * 2 invalid port number
157 * 3 iptables not executable
158 * 4 ip not executable
159 * 8 failed to change routing table, cleanup successfull
160 * 16-31 failed to undo some changes to routing table
161 * 31-47 failed to fully change routing table and then might have failed to undo everything
164 main (int argc, char *const*argv)
172 /* check command-line arguments */
176 "Syntax: gnunet-helper-hijack-dns [-d] PORT INTERFACENAME\n");
179 if (0 == strcmp (argv[1], "-d"))
183 if (argc != 3 + delete)
186 "Syntax: gnunet-helper-hijack-dns [-d] PORT INTERFACENAME\n");
189 port = atoi (argv[1 + delete]);
190 virt_dns = argv[2 + delete];
191 if ( (port == 0) || (port >= 65536) )
194 "Port `%u' is invalid\n",
198 /* verify that the binaries were care about are executable */
199 if (0 != access (SBIN_IPTABLES, X_OK))
202 "`%s' is not executable: %s\n",
207 if (0 != access (SBIN_IP, X_OK))
210 "`%s' is not executable: %s\n",
216 /* print port number to string for command-line use*/
217 (void) snprintf (localport,
222 /* update routing tables -- this is why we are SUID! */
225 /* Forward everything from the given local port (with destination
226 to port 53, and only for UDP) without hijacking */
228 char *const mangle_args[] =
230 "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
231 "udp", "--sport", localport, "--dport", DNS_PORT, "-j",
234 if (0 != fork_and_exec (SBIN_IPTABLES, mangle_args))
235 goto cleanup_mangle_1;
237 /* Mark all of the other DNS traffic using our mark DNS_MARK */
239 char *const mark_args[] =
241 "iptables", "-t", "mangle", "-I", "OUTPUT", DNS_TABLE, "-p",
242 "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
245 if (0 != fork_and_exec (SBIN_IPTABLES, mark_args))
248 /* Forward all marked DNS traffic to our DNS_TABLE */
250 char *const forward_args[] =
252 "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
254 if (0 != fork_and_exec (SBIN_IP, forward_args))
255 goto cleanup_forward_3;
257 /* Finally, add rule in our forwarding table to pass to our virtual interface */
259 char *const route_args[] =
261 "ip", "route", "add", "default", "via", virt_dns,
262 "table", DNS_TABLE, NULL
264 if (0 != fork_and_exec (SBIN_IP, route_args))
265 goto cleanup_route_4;
271 /* delete or clean-up-on-error case */
274 char *const route_clean_args[] =
276 "ip", "route", "del", "default", "via", virt_dns,
277 "table", DNS_TABLE, NULL
279 if (0 != fork_and_exec (SBIN_IP, route_clean_args))
284 char *const forward_clean_args[] =
286 "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
288 if (0 != fork_and_exec (SBIN_IP, forward_clean_args))
293 char *const mark_clean_args[] =
295 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
296 "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
298 if (0 != fork_and_exec (SBIN_IPTABLES, mark_clean_args))
303 char *const mangle_clean_args[] =
305 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
306 "--sport", localport, "--dport", DNS_PORT, "-j", "ACCEPT",
309 if (0 != fork_and_exec (SBIN_IPTABLES, mangle_clean_args))
315 return 16 + r; /* failed to delete */
316 return 32 + r; /* first failed to install, then also failed to clean up! */
320 /* got here via goto to clean up handler, failed to install, succeeded with clean up */
328 /* end of gnunet-helper-hijack-dns.c */