gnunet-helper-dns.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2010, 2011, 2012 Christian Grothoff
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file dns/gnunet-helper-dns.c
  18. * @brief helper to install firewall rules to hijack all DNS traffic
  19. * and send it to our virtual interface (except for DNS traffic
  20. * that originates on the specified port). We then
  21. * allow interacting with our virtual interface via stdin/stdout.
  22. * @author Philipp Tölke
  23. * @author Christian Grothoff
  24. *
  25. * This program alters the Linux firewall rules so that DNS traffic
  26. * that ordinarily exits the system can be intercepted and managed by
  27. * a virtual interface. In order to achieve this, DNS traffic is
  28. * marked with the DNS_MARK given in below and re-routed to a custom
  29. * table with the DNS_TABLE ID given below. Systems and
  30. * administrators must take care to not cause conflicts with these
  31. * values (it was deemed safest to hardcode them as passing these
  32. * values as arguments might permit messing with arbitrary firewall
  33. * rules, which would be dangerous). Traffic coming from the same
  34. * group ID as the effective group ID that this process is running
  35. * as is not intercepted.
  36. *
  37. * The code first sets up the virtual interface, then begins to
  38. * redirect the DNS traffic to it, and then on errors or SIGTERM shuts
  39. * down the virtual interface and removes the rules for the traffic
  40. * redirection.
  41. *
  42. *
  43. * Note that having this binary SUID is only partially safe: it will
  44. * allow redirecting (and intercepting / mangling) of all DNS traffic
  45. * originating from this system by any user who is able to run it.
  46. * Furthermore, this code will make it trivial to DoS all DNS traffic
  47. * originating from the current system, simply by sending it to
  48. * nowhere (redirect stdout to /dev/null).
  49. *
  50. * Naturally, neither of these problems can be helped as this is the
  51. * fundamental purpose of the binary. Certifying that this code is
  52. * "safe" thus only means that it doesn't allow anything else (such
  53. * as local priv. escalation, etc.).
  54. *
  55. * The following list of people have reviewed this code and considered
  56. * it safe (within specifications) since the last modification (if you
  57. * reviewed it, please have your name added to the list):
  58. *
  59. * - Christian Grothoff
  60. */
  61. #include "platform.h"
  62. #include <linux/if_tun.h>
  63. /**
  64. * Need 'struct GNUNET_MessageHeader'.
  65. */
  66. #include "gnunet_crypto_lib.h"
  67. #include "gnunet_common.h"
  68. /**
  69. * Need DNS message types.
  70. */
  71. #include "gnunet_protocols.h"
  72. /**
  73. * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE)
  74. */
  75. #define MAX_SIZE 65536
  76. #if !HAVE_DECL_STRUCT_IN6_IFREQ
  77. /**
  78. * This is in linux/include/net/ipv6.h, but not always exported...
  79. */
  80. struct in6_ifreq
  81. {
  82. struct in6_addr ifr6_addr;
  83. uint32_t ifr6_prefixlen;
  84. unsigned int ifr6_ifindex;
  85. };
  86. #endif
  87. /**
  88. * Name and full path of IPTABLES binary.
  89. */
  90. static const char *sbin_iptables;
  91. /**
  92. * Name and full path of IPTABLES binary.
  93. */
  94. static const char *sbin_ip6tables;
  95. /**
  96. * Name and full path of sysctl binary
  97. */
  98. static const char *sbin_sysctl;
  99. /**
  100. * Name and full path of IPTABLES binary.
  101. */
  102. static const char *sbin_ip;
  103. /**
  104. * Port for DNS traffic.
  105. */
  106. #define DNS_PORT "53"
  107. /**
  108. * Marker we set for our hijacked DNS traffic. We use GNUnet's
  109. * port (2086) plus the DNS port (53) in HEX to make a 32-bit mark
  110. * (which is hopefully long enough to not collide); so
  111. * 0x08260035 = 136708149 (hopefully unique enough...).
  112. */
  113. #define DNS_MARK "136708149"
  114. /**
  115. * Table we use for our DNS rules. 0-255 is the range and
  116. * 0, 253, 254 and 255 are already reserved. As this is about
  117. * DNS and as "53" is likely (fingers crossed!) high enough to
  118. * not usually conflict with a normal user's setup, we use 53
  119. * to give a hint that this has something to do with DNS.
  120. */
  121. #define DNS_TABLE "53"
  122. /**
  123. * Control pipe for shutdown via signal. [0] is the read end,
  124. * [1] is the write end.
  125. */
  126. static int cpipe[2];
  127. /**
  128. * Signal handler called to initiate "nice" shutdown. Signals select
  129. * loop via non-bocking pipe 'cpipe'.
  130. *
  131. * @param signal signal number of the signal (not used)
  132. */
  133. static void
  134. signal_handler (int signal)
  135. {
  136. /* ignore return value, as the signal handler could theoretically
  137. be called many times before the shutdown can actually happen */
  138. (void) write (cpipe[1], "K", 1);
  139. }
  140. /**
  141. * Open '/dev/null' and make the result the given
  142. * file descriptor.
  143. *
  144. * @param target_fd desired FD to point to /dev/null
  145. * @param flags open flags (O_RDONLY, O_WRONLY)
  146. */
  147. static void
  148. open_dev_null (int target_fd,
  149. int flags)
  150. {
  151. int fd;
  152. fd = open ("/dev/null", flags);
  153. if (-1 == fd)
  154. abort ();
  155. if (fd == target_fd)
  156. return;
  157. if (-1 == dup2 (fd, target_fd))
  158. {
  159. (void) close (fd);
  160. abort ();
  161. }
  162. (void) close (fd);
  163. }
  164. /**
  165. * Run the given command and wait for it to complete.
  166. *
  167. * @param file name of the binary to run
  168. * @param cmd command line arguments (as given to 'execv')
  169. * @return 0 on success, 1 on any error
  170. */
  171. static int
  172. fork_and_exec (const char *file,
  173. char *const cmd[])
  174. {
  175. int status;
  176. pid_t pid;
  177. pid_t ret;
  178. pid = fork ();
  179. if (-1 == pid)
  180. {
  181. fprintf (stderr,
  182. "fork failed: %s\n",
  183. strerror (errno));
  184. return 1;
  185. }
  186. if (0 == pid)
  187. {
  188. /* we are the child process */
  189. /* close stdin/stdout to not cause interference
  190. with the helper's main protocol! */
  191. (void) close (0);
  192. open_dev_null (0, O_RDONLY);
  193. (void) close (1);
  194. open_dev_null (1, O_WRONLY);
  195. (void) execv (file, cmd);
  196. /* can only get here on error */
  197. fprintf (stderr,
  198. "exec `%s' failed: %s\n",
  199. file,
  200. strerror (errno));
  201. _exit (1);
  202. }
  203. /* keep running waitpid as long as the only error we get is 'EINTR' */
  204. while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
  205. (errno == EINTR) );
  206. if (-1 == ret)
  207. {
  208. fprintf (stderr,
  209. "waitpid failed: %s\n",
  210. strerror (errno));
  211. return 1;
  212. }
  213. if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
  214. return 1;
  215. /* child process completed and returned success, we're happy */
  216. return 0;
  217. }
  218. /**
  219. * Creates a tun-interface called @a dev;
  220. *
  221. * @param dev is asumed to point to a char[IFNAMSIZ]
  222. * if *dev == '\\0', uses the name supplied by the kernel;
  223. * @return the fd to the tun or -1 on error
  224. */
  225. static int
  226. init_tun (char *dev)
  227. {
  228. struct ifreq ifr;
  229. int fd;
  230. if (NULL == dev)
  231. {
  232. errno = EINVAL;
  233. return -1;
  234. }
  235. if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
  236. {
  237. fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
  238. strerror (errno));
  239. return -1;
  240. }
  241. if (fd >= FD_SETSIZE)
  242. {
  243. fprintf (stderr, "File descriptor to large: %d", fd);
  244. (void) close (fd);
  245. return -1;
  246. }
  247. memset (&ifr, 0, sizeof (ifr));
  248. ifr.ifr_flags = IFF_TUN;
  249. if ('\0' != *dev)
  250. strncpy (ifr.ifr_name, dev, IFNAMSIZ);
  251. if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
  252. {
  253. fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
  254. strerror (errno));
  255. (void) close (fd);
  256. return -1;
  257. }
  258. strcpy (dev, ifr.ifr_name);
  259. return fd;
  260. }
  261. /**
  262. * @brief Sets the IPv6-Address given in @a address on the interface @a dev
  263. *
  264. * @param dev the interface to configure
  265. * @param address the IPv6-Address
  266. * @param prefix_len the length of the network-prefix
  267. */
  268. static void
  269. set_address6 (const char *dev, const char *address, unsigned long prefix_len)
  270. {
  271. struct ifreq ifr;
  272. struct in6_ifreq ifr6;
  273. struct sockaddr_in6 sa6;
  274. int fd;
  275. /*
  276. * parse the new address
  277. */
  278. memset (&sa6, 0, sizeof (struct sockaddr_in6));
  279. sa6.sin6_family = AF_INET6;
  280. if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
  281. {
  282. fprintf (stderr,
  283. "Failed to parse IPv6 address `%s': %s\n",
  284. address,
  285. strerror (errno));
  286. exit (1);
  287. }
  288. if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
  289. {
  290. fprintf (stderr,
  291. "Error creating IPv6 socket: %s (ignored)\n",
  292. strerror (errno));
  293. /* ignore error, maybe only IPv4 works on this system! */
  294. return;
  295. }
  296. memset (&ifr, 0, sizeof (struct ifreq));
  297. /*
  298. * Get the index of the if
  299. */
  300. strncpy (ifr.ifr_name, dev, IFNAMSIZ);
  301. if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
  302. {
  303. fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
  304. (void) close (fd);
  305. exit (1);
  306. }
  307. memset (&ifr6, 0, sizeof (struct in6_ifreq));
  308. ifr6.ifr6_addr = sa6.sin6_addr;
  309. ifr6.ifr6_ifindex = ifr.ifr_ifindex;
  310. ifr6.ifr6_prefixlen = prefix_len;
  311. /*
  312. * Set the address
  313. */
  314. if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
  315. {
  316. fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
  317. strerror (errno));
  318. (void) close (fd);
  319. exit (1);
  320. }
  321. /*
  322. * Get the flags
  323. */
  324. if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
  325. {
  326. fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
  327. strerror (errno));
  328. (void) close (fd);
  329. exit (1);
  330. }
  331. /*
  332. * Add the UP and RUNNING flags
  333. */
  334. ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
  335. if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
  336. {
  337. fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
  338. strerror (errno));
  339. (void) close (fd);
  340. exit (1);
  341. }
  342. if (0 != close (fd))
  343. {
  344. fprintf (stderr, "close failed: %s\n", strerror (errno));
  345. exit (1);
  346. }
  347. }
  348. /**
  349. * @brief Sets the IPv4-Address given in @a address on the interface @a dev
  350. *
  351. * @param dev the interface to configure
  352. * @param address the IPv4-Address
  353. * @param mask the netmask
  354. */
  355. static void
  356. set_address4 (const char *dev, const char *address, const char *mask)
  357. {
  358. int fd;
  359. struct sockaddr_in *addr;
  360. struct ifreq ifr;
  361. memset (&ifr, 0, sizeof (struct ifreq));
  362. addr = (struct sockaddr_in *) &(ifr.ifr_addr);
  363. addr->sin_family = AF_INET;
  364. /*
  365. * Parse the address
  366. */
  367. if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
  368. {
  369. fprintf (stderr,
  370. "Failed to parse IPv4 address `%s': %s\n",
  371. address,
  372. strerror (errno));
  373. exit (1);
  374. }
  375. if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
  376. {
  377. fprintf (stderr,
  378. "Error creating IPv4 socket: %s\n",
  379. strerror (errno));
  380. exit (1);
  381. }
  382. strncpy (ifr.ifr_name, dev, IFNAMSIZ);
  383. /*
  384. * Set the address
  385. */
  386. if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
  387. {
  388. fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
  389. (void) close (fd);
  390. exit (1);
  391. }
  392. /*
  393. * Parse the netmask
  394. */
  395. addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
  396. if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
  397. {
  398. fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
  399. strerror (errno));
  400. (void) close (fd);
  401. exit (1);
  402. }
  403. /*
  404. * Set the netmask
  405. */
  406. if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
  407. {
  408. fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
  409. strerror (errno));
  410. (void) close (fd);
  411. exit (1);
  412. }
  413. /*
  414. * Get the flags
  415. */
  416. if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
  417. {
  418. fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
  419. strerror (errno));
  420. (void) close (fd);
  421. exit (1);
  422. }
  423. /*
  424. * Add the UP and RUNNING flags
  425. */
  426. ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
  427. if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
  428. {
  429. fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
  430. strerror (errno));
  431. (void) close (fd);
  432. exit (1);
  433. }
  434. if (0 != close (fd))
  435. {
  436. fprintf (stderr, "close failed: %s\n", strerror (errno));
  437. (void) close (fd);
  438. exit (1);
  439. }
  440. }
  441. /**
  442. * Start forwarding to and from the tunnel. This function runs with
  443. * "reduced" priviledges (saved UID is still 0, but effective UID is
  444. * the real user ID).
  445. *
  446. * @param fd_tun tunnel FD
  447. */
  448. static void
  449. run (int fd_tun)
  450. {
  451. /*
  452. * The buffer filled by reading from fd_tun
  453. */
  454. unsigned char buftun[MAX_SIZE];
  455. ssize_t buftun_size = 0;
  456. unsigned char *buftun_read = NULL;
  457. /*
  458. * The buffer filled by reading from stdin
  459. */
  460. unsigned char bufin[MAX_SIZE];
  461. ssize_t bufin_size = 0;
  462. size_t bufin_rpos = 0;
  463. unsigned char *bufin_read = NULL;
  464. fd_set fds_w;
  465. fd_set fds_r;
  466. int max;
  467. while (1)
  468. {
  469. FD_ZERO (&fds_w);
  470. FD_ZERO (&fds_r);
  471. /*
  472. * We are supposed to read and the buffer is empty
  473. * -> select on read from tun
  474. */
  475. if (0 == buftun_size)
  476. FD_SET (fd_tun, &fds_r);
  477. /*
  478. * We are supposed to read and the buffer is not empty
  479. * -> select on write to stdout
  480. */
  481. if (0 < buftun_size)
  482. FD_SET (1, &fds_w);
  483. /*
  484. * We are supposed to write and the buffer is empty
  485. * -> select on read from stdin
  486. */
  487. if (NULL == bufin_read)
  488. FD_SET (0, &fds_r);
  489. /*
  490. * We are supposed to write and the buffer is not empty
  491. * -> select on write to tun
  492. */
  493. if (NULL != bufin_read)
  494. FD_SET (fd_tun, &fds_w);
  495. FD_SET (cpipe[0], &fds_r);
  496. max = (fd_tun > cpipe[0]) ? fd_tun : cpipe[0];
  497. int r = select (max + 1, &fds_r, &fds_w, NULL, NULL);
  498. if (-1 == r)
  499. {
  500. if (EINTR == errno)
  501. continue;
  502. fprintf (stderr, "select failed: %s\n", strerror (errno));
  503. return;
  504. }
  505. if (r > 0)
  506. {
  507. if (FD_ISSET (cpipe[0], &fds_r))
  508. return; /* aborted by signal */
  509. if (FD_ISSET (fd_tun, &fds_r))
  510. {
  511. buftun_size =
  512. read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
  513. MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
  514. if (-1 == buftun_size)
  515. {
  516. if ( (errno == EINTR) ||
  517. (errno == EAGAIN) )
  518. {
  519. buftun_size = 0;
  520. continue;
  521. }
  522. fprintf (stderr, "read-error: %s\n", strerror (errno));
  523. return;
  524. }
  525. if (0 == buftun_size)
  526. {
  527. fprintf (stderr, "EOF on tun\n");
  528. return;
  529. }
  530. buftun_read = buftun;
  531. {
  532. struct GNUNET_MessageHeader *hdr =
  533. (struct GNUNET_MessageHeader *) buftun;
  534. buftun_size += sizeof (struct GNUNET_MessageHeader);
  535. hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
  536. hdr->size = htons (buftun_size);
  537. }
  538. }
  539. else if (FD_ISSET (1, &fds_w))
  540. {
  541. ssize_t written = write (1, buftun_read, buftun_size);
  542. if (-1 == written)
  543. {
  544. if ( (errno == EINTR) ||
  545. (errno == EAGAIN) )
  546. continue;
  547. fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
  548. return;
  549. }
  550. if (0 == written)
  551. {
  552. fprintf (stderr, "write returned 0\n");
  553. return;
  554. }
  555. buftun_size -= written;
  556. buftun_read += written;
  557. }
  558. if (FD_ISSET (0, &fds_r))
  559. {
  560. bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
  561. if (-1 == bufin_size)
  562. {
  563. bufin_read = NULL;
  564. if ( (errno == EINTR) ||
  565. (errno == EAGAIN) )
  566. continue;
  567. fprintf (stderr, "read-error: %s\n", strerror (errno));
  568. return;
  569. }
  570. if (0 == bufin_size)
  571. {
  572. bufin_read = NULL;
  573. fprintf (stderr, "EOF on stdin\n");
  574. return;
  575. }
  576. {
  577. struct GNUNET_MessageHeader *hdr;
  578. PROCESS_BUFFER:
  579. bufin_rpos += bufin_size;
  580. if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
  581. continue;
  582. hdr = (struct GNUNET_MessageHeader *) bufin;
  583. if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
  584. {
  585. fprintf (stderr, "protocol violation!\n");
  586. return;
  587. }
  588. if (ntohs (hdr->size) > bufin_rpos)
  589. continue;
  590. bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
  591. bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
  592. bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
  593. }
  594. }
  595. else if (FD_ISSET (fd_tun, &fds_w))
  596. {
  597. ssize_t written = write (fd_tun, bufin_read, bufin_size);
  598. if (-1 == written)
  599. {
  600. if ( (errno == EINTR) ||
  601. (errno == EAGAIN) )
  602. continue;
  603. fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
  604. return;
  605. }
  606. if (0 == written)
  607. {
  608. fprintf (stderr, "write returned 0\n");
  609. return;
  610. }
  611. {
  612. bufin_size -= written;
  613. bufin_read += written;
  614. if (0 == bufin_size)
  615. {
  616. memmove (bufin, bufin_read, bufin_rpos);
  617. bufin_read = NULL; /* start reading again */
  618. bufin_size = 0;
  619. goto PROCESS_BUFFER;
  620. }
  621. }
  622. }
  623. }
  624. }
  625. }
  626. /**
  627. * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface,
  628. * redirects all outgoing DNS traffic (except from the specified port) to that
  629. * interface and then passes traffic from and to the interface via stdin/stdout.
  630. *
  631. * Once stdin/stdout close or have other errors, the tunnel is closed and the
  632. * DNS traffic redirection is stopped.
  633. *
  634. * @param argc number of arguments
  635. * @param argv 0: binary name (should be "gnunet-helper-vpn")
  636. * 1: tunnel interface name (typically "gnunet-dns")
  637. * 2: IPv6 address for the tunnel ("FE80::1")
  638. * 3: IPv6 netmask length in bits ("64")
  639. * 4: IPv4 address for the tunnel ("1.2.3.4")
  640. * 5: IPv4 netmask ("255.255.0.0")
  641. * 6: skip sysctl, routing and iptables setup ("0")
  642. * @return 0 on success, otherwise code indicating type of error:
  643. * 1 wrong number of arguments
  644. * 2 invalid arguments (i.e. port number / prefix length wrong)
  645. * 3 iptables not executable
  646. * 4 ip not executable
  647. * 5 failed to initialize tunnel interface
  648. * 6 failed to initialize control pipe
  649. * 8 failed to change routing table, cleanup successful
  650. * 9-23 failed to change routing table and failed to undo some changes to routing table
  651. * 24 failed to drop privs
  652. * 25-39 failed to drop privs and then failed to undo some changes to routing table
  653. * 40 failed to regain privs
  654. * 41-55 failed to regain prisv and then failed to undo some changes to routing table
  655. * 254 insufficient priviledges
  656. * 255 failed to handle kill signal properly
  657. */
  658. int
  659. main (int argc, char *const*argv)
  660. {
  661. int r;
  662. char dev[IFNAMSIZ];
  663. char mygid[32];
  664. int fd_tun;
  665. uid_t uid;
  666. int nortsetup = 0;
  667. if (7 != argc)
  668. {
  669. fprintf (stderr, "Fatal: must supply 6 arguments!\n");
  670. return 1;
  671. }
  672. /* assert privs so we can modify the firewall rules! */
  673. uid = getuid ();
  674. #ifdef HAVE_SETRESUID
  675. if (0 != setresuid (uid, 0, 0))
  676. {
  677. fprintf (stderr, "Failed to setresuid to root: %s\n", strerror (errno));
  678. return 254;
  679. }
  680. #else
  681. if (0 != seteuid (0))
  682. {
  683. fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
  684. return 254;
  685. }
  686. #endif
  687. if (0 == strncmp (argv[6], "1", 2))
  688. nortsetup = 1;
  689. if (0 == nortsetup)
  690. {
  691. /* verify that the binaries we care about are executable */
  692. #ifdef IPTABLES
  693. if (0 == access (IPTABLES, X_OK))
  694. sbin_iptables = IPTABLES;
  695. else
  696. #endif
  697. if (0 == access ("/sbin/iptables", X_OK))
  698. sbin_iptables = "/sbin/iptables";
  699. else if (0 == access ("/usr/sbin/iptables", X_OK))
  700. sbin_iptables = "/usr/sbin/iptables";
  701. else
  702. {
  703. fprintf (stderr,
  704. "Fatal: executable iptables not found in approved directories: %s\n",
  705. strerror (errno));
  706. return 3;
  707. }
  708. #ifdef IP6TABLES
  709. if (0 == access (IP6TABLES, X_OK))
  710. sbin_ip6tables = IP6TABLES;
  711. else
  712. #endif
  713. if (0 == access ("/sbin/ip6tables", X_OK))
  714. sbin_ip6tables = "/sbin/ip6tables";
  715. else if (0 == access ("/usr/sbin/ip6tables", X_OK))
  716. sbin_ip6tables = "/usr/sbin/ip6tables";
  717. else
  718. {
  719. fprintf (stderr,
  720. "Fatal: executable ip6tables not found in approved directories: %s\n",
  721. strerror (errno));
  722. return 3;
  723. }
  724. #ifdef PATH_TO_IP
  725. if (0 == access (PATH_TO_IP, X_OK))
  726. sbin_ip = PATH_TO_IP;
  727. else
  728. #endif
  729. if (0 == access ("/sbin/ip", X_OK))
  730. sbin_ip = "/sbin/ip";
  731. else if (0 == access ("/usr/sbin/ip", X_OK))
  732. sbin_ip = "/usr/sbin/ip";
  733. else if (0 == access ("/bin/ip", X_OK)) /* gentoo has it there */
  734. sbin_ip = "/bin/ip";
  735. else
  736. {
  737. fprintf (stderr,
  738. "Fatal: executable ip not found in approved directories: %s\n",
  739. strerror (errno));
  740. return 4;
  741. }
  742. #ifdef SYSCTL
  743. if (0 == access (SYSCTL, X_OK))
  744. sbin_sysctl = SYSCTL;
  745. else
  746. #endif
  747. if (0 == access ("/sbin/sysctl", X_OK))
  748. sbin_sysctl = "/sbin/sysctl";
  749. else if (0 == access ("/usr/sbin/sysctl", X_OK))
  750. sbin_sysctl = "/usr/sbin/sysctl";
  751. else
  752. {
  753. fprintf (stderr,
  754. "Fatal: executable sysctl not found in approved directories: %s\n",
  755. strerror (errno));
  756. return 5;
  757. }
  758. }
  759. /* setup 'mygid' string */
  760. snprintf (mygid, sizeof (mygid), "%d", (int) getegid());
  761. /* do not die on SIGPIPE */
  762. if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
  763. {
  764. fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
  765. strerror (errno));
  766. return 7;
  767. }
  768. /* setup pipe to shutdown nicely on SIGINT */
  769. if (0 != pipe (cpipe))
  770. {
  771. fprintf (stderr,
  772. "Fatal: could not setup control pipe: %s\n",
  773. strerror (errno));
  774. return 6;
  775. }
  776. if (cpipe[0] >= FD_SETSIZE)
  777. {
  778. fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
  779. (void) close (cpipe[0]);
  780. (void) close (cpipe[1]);
  781. return 6;
  782. }
  783. {
  784. /* make pipe non-blocking, as we theoretically could otherwise block
  785. in the signal handler */
  786. int flags = fcntl (cpipe[1], F_GETFL);
  787. if (-1 == flags)
  788. {
  789. fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
  790. (void) close (cpipe[0]);
  791. (void) close (cpipe[1]);
  792. return 6;
  793. }
  794. flags |= O_NONBLOCK;
  795. if (0 != fcntl (cpipe[1], F_SETFL, flags))
  796. {
  797. fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (errno));
  798. (void) close (cpipe[0]);
  799. (void) close (cpipe[1]);
  800. return 6;
  801. }
  802. }
  803. if ( (SIG_ERR == signal (SIGTERM, &signal_handler)) ||
  804. #if (SIGTERM != GNUNET_TERM_SIG)
  805. (SIG_ERR == signal (GNUNET_TERM_SIG, &signal_handler)) ||
  806. #endif
  807. (SIG_ERR == signal (SIGINT, &signal_handler)) ||
  808. (SIG_ERR == signal (SIGHUP, &signal_handler)) )
  809. {
  810. fprintf (stderr,
  811. "Fatal: could not initialize signal handler: %s\n",
  812. strerror (errno));
  813. (void) close (cpipe[0]);
  814. (void) close (cpipe[1]);
  815. return 7;
  816. }
  817. /* get interface name */
  818. strncpy (dev, argv[1], IFNAMSIZ);
  819. dev[IFNAMSIZ - 1] = '\0';
  820. /* Disable rp filtering */
  821. if (0 == nortsetup)
  822. {
  823. char *const sysctl_args[] = {"sysctl", "-w",
  824. "net.ipv4.conf.all.rp_filter=0", NULL};
  825. char *const sysctl_args2[] = {"sysctl", "-w",
  826. "net.ipv4.conf.default.rp_filter=0", NULL};
  827. if ((0 != fork_and_exec (sbin_sysctl, sysctl_args)) ||
  828. (0 != fork_and_exec (sbin_sysctl, sysctl_args2)))
  829. {
  830. fprintf (stderr,
  831. "Failed to disable rp filtering.\n");
  832. return 5;
  833. }
  834. }
  835. /* now open virtual interface (first part that requires root) */
  836. if (-1 == (fd_tun = init_tun (dev)))
  837. {
  838. fprintf (stderr, "Fatal: could not initialize tun-interface\n");
  839. (void) signal (SIGTERM, SIG_IGN);
  840. #if (SIGTERM != GNUNET_TERM_SIG)
  841. (void) signal (GNUNET_TERM_SIG, SIG_IGN);
  842. #endif
  843. (void) signal (SIGINT, SIG_IGN);
  844. (void) signal (SIGHUP, SIG_IGN);
  845. (void) close (cpipe[0]);
  846. (void) close (cpipe[1]);
  847. return 5;
  848. }
  849. /* now set interface addresses */
  850. {
  851. const char *address = argv[2];
  852. long prefix_len = atol (argv[3]);
  853. if ((prefix_len < 1) || (prefix_len > 127))
  854. {
  855. fprintf (stderr, "Fatal: prefix_len out of range\n");
  856. (void) signal (SIGTERM, SIG_IGN);
  857. #if (SIGTERM != GNUNET_TERM_SIG)
  858. (void) signal (GNUNET_TERM_SIG, SIG_IGN);
  859. #endif
  860. (void) signal (SIGINT, SIG_IGN);
  861. (void) signal (SIGHUP, SIG_IGN);
  862. (void) close (cpipe[0]);
  863. (void) close (cpipe[1]);
  864. return 2;
  865. }
  866. set_address6 (dev, address, prefix_len);
  867. }
  868. {
  869. const char *address = argv[4];
  870. const char *mask = argv[5];
  871. set_address4 (dev, address, mask);
  872. }
  873. /* update routing tables -- next part why we need SUID! */
  874. /* Forward everything from our EGID (which should only be held
  875. by the 'gnunet-service-dns') and with destination
  876. to port 53 on UDP, without hijacking */
  877. if (0 == nortsetup)
  878. {
  879. r = 8; /* failed to fully setup routing table */
  880. {
  881. char *const mangle_args[] =
  882. {
  883. "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
  884. "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
  885. "ACCEPT", NULL
  886. };
  887. if (0 != fork_and_exec (sbin_iptables, mangle_args))
  888. goto cleanup_rest;
  889. }
  890. {
  891. char *const mangle_args[] =
  892. {
  893. "ip6tables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
  894. "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
  895. "ACCEPT", NULL
  896. };
  897. if (0 != fork_and_exec (sbin_ip6tables, mangle_args))
  898. goto cleanup_mangle_1b;
  899. }
  900. /* Mark all of the other DNS traffic using our mark DNS_MARK,
  901. unless it is on a link-local IPv6 address, which we cannot support. */
  902. {
  903. char *const mark_args[] =
  904. {
  905. "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
  906. "udp", "--dport", DNS_PORT,
  907. "-j", "MARK", "--set-mark", DNS_MARK,
  908. NULL
  909. };
  910. if (0 != fork_and_exec (sbin_iptables, mark_args))
  911. goto cleanup_mangle_1;
  912. }
  913. {
  914. char *const mark_args[] =
  915. {
  916. "ip6tables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
  917. "udp", "--dport", DNS_PORT,
  918. "!", "-s", "fe80::/10", /* this line excludes link-local traffic */
  919. "-j", "MARK", "--set-mark", DNS_MARK,
  920. NULL
  921. };
  922. if (0 != fork_and_exec (sbin_ip6tables, mark_args))
  923. goto cleanup_mark_2b;
  924. }
  925. /* Forward all marked DNS traffic to our DNS_TABLE */
  926. {
  927. char *const forward_args[] =
  928. {
  929. "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
  930. };
  931. if (0 != fork_and_exec (sbin_ip, forward_args))
  932. goto cleanup_mark_2;
  933. }
  934. {
  935. char *const forward_args[] =
  936. {
  937. "ip", "-6", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
  938. };
  939. if (0 != fork_and_exec (sbin_ip, forward_args))
  940. goto cleanup_forward_3b;
  941. }
  942. /* Finally, add rule in our forwarding table to pass to our virtual interface */
  943. {
  944. char *const route_args[] =
  945. {
  946. "ip", "route", "add", "default", "dev", dev,
  947. "table", DNS_TABLE, NULL
  948. };
  949. if (0 != fork_and_exec (sbin_ip, route_args))
  950. goto cleanup_forward_3;
  951. }
  952. {
  953. char *const route_args[] =
  954. {
  955. "ip", "-6", "route", "add", "default", "dev", dev,
  956. "table", DNS_TABLE, NULL
  957. };
  958. if (0 != fork_and_exec (sbin_ip, route_args))
  959. goto cleanup_route_4b;
  960. }
  961. }
  962. /* drop privs *except* for the saved UID; this is not perfect, but better
  963. than doing nothing */
  964. #ifdef HAVE_SETRESUID
  965. if (0 != setresuid (uid, uid, 0))
  966. {
  967. fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
  968. r = 24;
  969. goto cleanup_route_4;
  970. }
  971. #else
  972. /* Note: no 'setuid' here as we must keep our saved UID as root */
  973. if (0 != seteuid (uid))
  974. {
  975. fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
  976. r = 24;
  977. goto cleanup_route_4;
  978. }
  979. #endif
  980. r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
  981. /* now forward until we hit a problem */
  982. run (fd_tun);
  983. /* now need to regain privs so we can remove the firewall rules we added! */
  984. #ifdef HAVE_SETRESUID
  985. if (0 != setresuid (uid, 0, 0))
  986. {
  987. fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (errno));
  988. r = 40;
  989. goto cleanup_route_4;
  990. }
  991. #else
  992. if (0 != seteuid (0))
  993. {
  994. fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
  995. r = 40;
  996. goto cleanup_route_4;
  997. }
  998. #endif
  999. /* update routing tables again -- this is why we could not fully drop privs */
  1000. /* now undo updating of routing tables; normal exit or clean-up-on-error case */
  1001. cleanup_route_4:
  1002. if (0 == nortsetup)
  1003. {
  1004. char *const route_clean_args[] =
  1005. {
  1006. "ip", "-6", "route", "del", "default", "dev", dev,
  1007. "table", DNS_TABLE, NULL
  1008. };
  1009. if (0 != fork_and_exec (sbin_ip, route_clean_args))
  1010. r += 1;
  1011. }
  1012. cleanup_route_4b:
  1013. if (0 == nortsetup)
  1014. {
  1015. char *const route_clean_args[] =
  1016. {
  1017. "ip", "route", "del", "default", "dev", dev,
  1018. "table", DNS_TABLE, NULL
  1019. };
  1020. if (0 != fork_and_exec (sbin_ip, route_clean_args))
  1021. r += 1;
  1022. }
  1023. cleanup_forward_3:
  1024. if (0 == nortsetup)
  1025. {
  1026. char *const forward_clean_args[] =
  1027. {
  1028. "ip", "-6", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
  1029. };
  1030. if (0 != fork_and_exec (sbin_ip, forward_clean_args))
  1031. r += 2;
  1032. }
  1033. cleanup_forward_3b:
  1034. if (0 == nortsetup)
  1035. {
  1036. char *const forward_clean_args[] =
  1037. {
  1038. "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
  1039. };
  1040. if (0 != fork_and_exec (sbin_ip, forward_clean_args))
  1041. r += 2;
  1042. }
  1043. cleanup_mark_2:
  1044. if (0 == nortsetup)
  1045. {
  1046. char *const mark_clean_args[] =
  1047. {
  1048. "ip6tables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
  1049. "--dport", DNS_PORT,
  1050. "!", "-s", "fe80::/10", /* this line excludes link-local traffic */
  1051. "-j", "MARK", "--set-mark", DNS_MARK, NULL
  1052. };
  1053. if (0 != fork_and_exec (sbin_ip6tables, mark_clean_args))
  1054. r += 4;
  1055. }
  1056. cleanup_mark_2b:
  1057. if (0 == nortsetup)
  1058. {
  1059. char *const mark_clean_args[] =
  1060. {
  1061. "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
  1062. "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
  1063. };
  1064. if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
  1065. r += 4;
  1066. }
  1067. cleanup_mangle_1:
  1068. if (0 == nortsetup)
  1069. {
  1070. char *const mangle_clean_args[] =
  1071. {
  1072. "ip6tables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
  1073. "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
  1074. NULL
  1075. };
  1076. if (0 != fork_and_exec (sbin_ip6tables, mangle_clean_args))
  1077. r += 8;
  1078. }
  1079. cleanup_mangle_1b:
  1080. if (0 == nortsetup)
  1081. {
  1082. char *const mangle_clean_args[] =
  1083. {
  1084. "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
  1085. "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
  1086. NULL
  1087. };
  1088. if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
  1089. r += 8;
  1090. }
  1091. cleanup_rest:
  1092. /* close virtual interface */
  1093. (void) close (fd_tun);
  1094. /* remove signal handler so we can close the pipes */
  1095. (void) signal (SIGTERM, SIG_IGN);
  1096. #if (SIGTERM != GNUNET_TERM_SIG)
  1097. (void) signal (GNUNET_TERM_SIG, SIG_IGN);
  1098. #endif
  1099. (void) signal (SIGINT, SIG_IGN);
  1100. (void) signal (SIGHUP, SIG_IGN);
  1101. (void) close (cpipe[0]);
  1102. (void) close (cpipe[1]);
  1103. return r;
  1104. }
  1105. /* end of gnunet-helper-dns.c */