aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-01-02 03:51:28 +0000
committerChristian Grothoff <christian@grothoff.org>2012-01-02 03:51:28 +0000
commit1fd2ff9b321277b55444c2a074e6476cc10099c2 (patch)
treec505d53cacd7b7c0fc27abe98e539940a0d36749 /src
parent46db89062f906a1d4f33cd24973f89e67d0b5a89 (diff)
downloadgnunet-1fd2ff9b321277b55444c2a074e6476cc10099c2.tar.gz
gnunet-1fd2ff9b321277b55444c2a074e6476cc10099c2.zip
dns hijacker code review
Diffstat (limited to 'src')
-rw-r--r--src/dns/gnunet-helper-hijack-dns.c372
1 files changed, 272 insertions, 100 deletions
diff --git a/src/dns/gnunet-helper-hijack-dns.c b/src/dns/gnunet-helper-hijack-dns.c
index 70da96477..833bda8ad 100644
--- a/src/dns/gnunet-helper-hijack-dns.c
+++ b/src/dns/gnunet-helper-hijack-dns.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 (C) 2010 Christian Grothoff 3 (C) 2010, 2011, 2012 Christian Grothoff
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 6 it under the terms of the GNU General Public License as published
@@ -19,138 +19,310 @@
19 */ 19 */
20 20
21/** 21/**
22 * @file vpn/gnunet-helper-hijack-dns.c 22 * @file dns/gnunet-helper-hijack-dns.c
23 * @brief 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.
24 * @author Philipp Tölke 26 * @author Philipp Tölke
27 * @author Christian Grothoff
28 *
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).
38 *
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
46 * to nowhere.
47 *
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.).
52 *
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):
56 *
57 * - Christian Grothoff
25 */ 58 */
26#include <platform.h> 59#include "platform.h"
27 60
28#include "gnunet_common.h" 61/**
62 * Name and full path of IPTABLES binary.
63 */
64#define SBIN_IPTABLES "/sbin/iptables"
65
66/**
67 * Name and full path of IPTABLES binary.
68 */
69#define SBIN_IP "/sbin/ip"
70
71/**
72 * Port for DNS traffic.
73 */
74#define DNS_PORT "53"
75
76/**
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...).
81 */
82#define DNS_MARK "136708149"
83
84/**
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.
90 */
91#define DNS_TABLE "53"
29 92
93
94/**
95 * Run the given command and wait for it to complete.
96 *
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
100 */
30static int 101static int
31fork_and_exec (char *file, char *cmd[]) 102fork_and_exec (const char *file,
103 char *const cmd[])
32{ 104{
33 pid_t pid = fork (); 105 int status;
106 pid_t pid;
107 pid_t ret;
34 108
35 if (pid < 0) 109 pid = fork ();
110 if (-1 == pid)
36 { 111 {
37 fprintf (stderr, "could not fork: %s\n", strerror (errno)); 112 fprintf (stderr,
38 return GNUNET_SYSERR; 113 "fork failed: %s\n",
114 strerror (errno));
115 return 1;
39 } 116 }
40 117 if (0 == pid)
41 int st = 0;
42
43 if (pid == 0)
44 { 118 {
45 execv (file, cmd); 119 /* we are the child process */
120 (void) execv (file, cmd);
121 /* can only get here on error */
122 fprintf (stderr,
123 "exec `%s' failed: %s\n",
124 file,
125 strerror (errno));
126 _exit (1);
46 } 127 }
47 else 128 /* keep running waitpid as long as the only error we get is 'EINTR' */
129 while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
130 (errno == EINTR) );
131 if (-1 == ret)
48 { 132 {
49 waitpid (pid, &st, 0); 133 fprintf (stderr,
134 "waitpid failed: %s\n",
135 strerror (errno));
136 return 1;
50 } 137 }
51 return WIFEXITED (st) && (WEXITSTATUS (st) == 0); 138 if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
139 return 1;
140 /* child process completed and returned success, we're happy */
141 return 0;
52} 142}
53 143
144
145/**
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.
151 *
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
162 */
54int 163int
55main (int argc, char **argv) 164main (int argc, char *const*argv)
56{ 165{
57 int delete = 0; 166 int delete;
58 int port = 0; 167 unsigned int port;
59 char *virt_dns; 168 char *virt_dns;
169 char localport[6];
170 int r;
60 171
172 /* check command-line arguments */
61 if (argc < 3) 173 if (argc < 3)
62 return GNUNET_SYSERR;
63
64 if (strncmp (argv[1], "-d", 2) == 0)
65 { 174 {
66 if (argc < 3) 175 fprintf (stderr,
67 return GNUNET_SYSERR; 176 "Syntax: gnunet-helper-hijack-dns [-d] PORT INTERFACENAME\n");
68 delete = 1; 177 return 1;
69 port = atoi (argv[2]);
70 virt_dns = argv[3];
71 } 178 }
179 if (0 == strcmp (argv[1], "-d"))
180 delete = 1;
72 else 181 else
182 delete = 0;
183 if (argc != 3 + delete)
73 { 184 {
74 port = atoi (argv[1]); 185 fprintf (stderr,
75 virt_dns = argv[2]; 186 "Syntax: gnunet-helper-hijack-dns [-d] PORT INTERFACENAME\n");
187 return 1;
188 }
189 port = atoi (argv[1 + delete]);
190 virt_dns = argv[2 + delete];
191 if ( (port == 0) || (port >= 65536) )
192 {
193 fprintf (stderr,
194 "Port `%u' is invalid\n",
195 port);
196 return 2;
76 } 197 }
77 198 /* verify that the binaries were care about are executable */
78 if (port == 0) 199 if (0 != access (SBIN_IPTABLES, X_OK))
79 return GNUNET_SYSERR;
80
81 struct stat s;
82
83 if (stat ("/sbin/iptables", &s) < 0)
84 { 200 {
85 fprintf (stderr, "stat on /sbin/iptables failed: %s\n", strerror (errno)); 201 fprintf (stderr,
86 return GNUNET_SYSERR; 202 "`%s' is not executable: %s\n",
203 SBIN_IPTABLES,
204 strerror (errno));
205 return 3;
87 } 206 }
88 if (stat ("/sbin/ip", &s) < 0) 207 if (0 != access (SBIN_IP, X_OK))
89 { 208 {
90 fprintf (stderr, "stat on /sbin/ip failed: %s\n", strerror (errno)); 209 fprintf (stderr,
91 return GNUNET_SYSERR; 210 "`%s' is not executable: %s\n",
211 SBIN_IP,
212 strerror (errno));
213 return 4;
92 } 214 }
93 215
94 char localport[7]; 216 /* print port number to string for command-line use*/
95 217 (void) snprintf (localport,
96 snprintf (localport, 7, "%d", port); 218 sizeof (localport),
219 "%u",
220 port);
97 221
98 int r; 222 /* update routing tables -- this is why we are SUID! */
99 223 if (! delete)
100 if (delete)
101 { 224 {
102e4: 225 /* Forward everything from the given local port (with destination
103 r = fork_and_exec ("/sbin/ip", (char *[]) 226 to port 53, and only for UDP) without hijacking */
104 { 227 {
105 "ip", "route", "del", "default", "via", virt_dns, 228 char *const mangle_args[] =
106 "table", "2", NULL}); 229 {
107e3: 230 "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
108 r = fork_and_exec ("/sbin/ip", (char *[]) 231 "udp", "--sport", localport, "--dport", DNS_PORT, "-j",
109 { 232 "ACCEPT", NULL
110 "ip", "rule", "del", "fwmark", "3", "table", "2", NULL}); 233 };
111e2: 234 if (0 != fork_and_exec (SBIN_IPTABLES, mangle_args))
112 r = fork_and_exec ("/sbin/iptables", (char *[]) 235 goto cleanup_mangle_1;
113 { 236 }
114 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp", 237 /* Mark all of the other DNS traffic using our mark DNS_MARK */
115 "--dport", "53", "-j", "MARK", "--set-mark", "3", NULL}); 238 {
116e1: 239 char *const mark_args[] =
117 r = fork_and_exec ("/sbin/iptables", (char *[]) 240 {
118 { 241 "iptables", "-t", "mangle", "-I", "OUTPUT", DNS_TABLE, "-p",
119 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp", 242 "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
120 "--sport", localport, "--dport", "53", "-j", "ACCEPT", 243 NULL
121 NULL}); 244 };
122 if (!delete) 245 if (0 != fork_and_exec (SBIN_IPTABLES, mark_args))
123 r = 0; 246 goto cleanup_mark_2;
247 }
248 /* Forward all marked DNS traffic to our DNS_TABLE */
249 {
250 char *const forward_args[] =
251 {
252 "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
253 };
254 if (0 != fork_and_exec (SBIN_IP, forward_args))
255 goto cleanup_forward_3;
256 }
257 /* Finally, add rule in our forwarding table to pass to our virtual interface */
258 {
259 char *const route_args[] =
260 {
261 "ip", "route", "add", "default", "via", virt_dns,
262 "table", DNS_TABLE, NULL
263 };
264 if (0 != fork_and_exec (SBIN_IP, route_args))
265 goto cleanup_route_4;
266 }
124 } 267 }
125 else 268 else
126 { 269 {
127 r = fork_and_exec ("/sbin/iptables", (char *[]) 270 r = 0;
128 { 271 /* delete or clean-up-on-error case */
129 "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p", 272cleanup_route_4:
130 "udp", "--sport", localport, "--dport", "53", "-j", 273 {
131 "ACCEPT", NULL}); 274 char *const route_clean_args[] =
132 if (!r) 275 {
133 goto e1; 276 "ip", "route", "del", "default", "via", virt_dns,
134 r = fork_and_exec ("/sbin/iptables", (char *[]) 277 "table", DNS_TABLE, NULL
135 { 278 };
136 "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p", 279 if (0 != fork_and_exec (SBIN_IP, route_clean_args))
137 "udp", "--dport", "53", "-j", "MARK", "--set-mark", "3", 280 r += 1;
138 NULL}); 281 }
139 if (!r) 282cleanup_forward_3:
140 goto e2; 283 {
141 r = fork_and_exec ("/sbin/ip", (char *[]) 284 char *const forward_clean_args[] =
142 { 285 {
143 "ip", "rule", "add", "fwmark", "3", "table", "2", NULL}); 286 "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
144 if (!r) 287 };
145 goto e3; 288 if (0 != fork_and_exec (SBIN_IP, forward_clean_args))
146 r = fork_and_exec ("/sbin/ip", (char *[]) 289 r += 2;
147 { 290 }
148 "ip", "route", "add", "default", "via", virt_dns, 291cleanup_mark_2:
149 "table", "2", NULL}); 292 {
150 if (!r) 293 char *const mark_clean_args[] =
151 goto e4; 294 {
152 } 295 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
153 if (r) 296 "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
154 return GNUNET_YES; 297 };
155 return GNUNET_SYSERR; 298 if (0 != fork_and_exec (SBIN_IPTABLES, mark_clean_args))
299 r += 4;
300 }
301cleanup_mangle_1:
302 {
303 char *const mangle_clean_args[] =
304 {
305 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
306 "--sport", localport, "--dport", DNS_PORT, "-j", "ACCEPT",
307 NULL
308 };
309 if (0 != fork_and_exec (SBIN_IPTABLES, mangle_clean_args))
310 r += 8;
311 }
312 if (r != 0)
313 {
314 if (delete)
315 return 16 + r; /* failed to delete */
316 return 32 + r; /* first failed to install, then also failed to clean up! */
317 }
318 if (! delete)
319 {
320 /* got here via goto to clean up handler, failed to install, succeeded with clean up */
321 return 8;
322 }
323 }
324 /* success ! */
325 return 0;
156} 326}
327
328/* end of gnunet-helper-hijack-dns.c */