cleaned up the suid-program
[oweals/gnunet.git] / src / vpn / gnunet-helper-vpn.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 Christian Grothoff
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file vpn/gnunet-daemon-vpn.c
23  * @brief the helper for various vpn-daemons. Opens a virtual network-interface,
24  * sends data received on the if to stdout, sends data received on stdin to the
25  * interface
26  * @author Philipp Tölke
27  */
28 #include <platform.h>
29
30 #include "gnunet-vpn-tun.h"
31 #include "gnunet_common.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet-vpn-helper-p.h"
34
35 #ifndef _LINUX_IN6_H
36
37 #define MAX_SIZE (65535 - sizeof(struct GNUNET_MessageHeader))
38
39 // This is in linux/include/net/ipv6.h.
40 struct in6_ifreq
41 {
42   struct in6_addr ifr6_addr;
43   uint32_t ifr6_prefixlen;
44   unsigned int ifr6_ifindex;
45 };
46
47 #endif
48
49 static int running = 1;
50
51 static void
52 term (int sig)
53 {
54   fprintf (stderr, "Got SIGTERM...\n");
55   if (sig == SIGTERM)
56     running = 0;
57 }
58
59 /**
60  * @brief Sets the IPv6-Address given in address on the interface dev
61  *
62  * @param dev the interface to configure
63  * @param address the IPv6-Address
64  * @param prefix_len the length of the network-prefix
65  */
66 static void
67 set_address6 (char *dev, char *address, unsigned long prefix_len)
68 {                               /* {{{ */
69   int fd = socket (AF_INET6, SOCK_DGRAM, 0);
70
71   if (fd < 0)
72     {
73       fprintf (stderr, "error creating socket: %m\n");
74       exit (1);
75     }
76
77   struct ifreq ifr;
78   struct in6_ifreq ifr6;
79
80   struct sockaddr_in6 sa6;
81   memset (&sa6, 0, sizeof (struct sockaddr_in6));
82
83   sa6.sin6_family = AF_INET6;
84
85   int r = inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr);
86   if (r < 0)
87     {
88       fprintf (stderr, "error at inet_pton: %m\n");
89       exit (1);
90     }
91
92   memcpy ((char *) &ifr6.ifr6_addr, (char *) &sa6.sin6_addr,
93           sizeof (struct in6_addr));
94
95   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
96
97   if (ioctl (fd, SIOGIFINDEX, &ifr) < 0)
98     {
99       perror ("SIOGIFINDEX");
100     }
101
102   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
103   ifr6.ifr6_prefixlen = prefix_len;
104
105   if (ioctl (fd, SIOCSIFADDR, &ifr6) < 0)
106     {
107       perror ("SIOCSIFADDR");
108     }
109
110   (void) ioctl (fd, SIOCGIFFLAGS, &ifr);
111   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
112   (void) ioctl (fd, SIOCSIFFLAGS, &ifr);
113   close (fd);
114 }                               /* }}} */
115
116 static void
117 /**
118  * @brief Sets the IPv4-Address given in address on the interface dev
119  *
120  * @param dev the interface to configure
121  * @param address the IPv4-Address
122  * @param mask the netmask
123  */
124 set_address4 (char *dev, char *address, char *mask)
125 {                               /* {{{ */
126   int fd = 0;
127   struct sockaddr_in *addr;
128   struct ifreq ifr;
129
130   memset (&ifr, 0, sizeof (struct ifreq));
131   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
132   memset (addr, 0, sizeof (struct sockaddr_in));
133   addr->sin_family = AF_INET;
134   addr->sin_addr.s_addr = inet_addr (address);
135
136   int r = inet_pton (AF_INET, address, &addr->sin_addr.s_addr);
137   if (r < 0)
138     {
139       fprintf (stderr, "error at inet_pton: %m\n");
140       exit (1);
141     }
142
143   fd = socket (PF_INET, SOCK_DGRAM, 0);
144   if (fd < 0)
145     {
146       perror ("socket()");
147       return;
148     }
149
150   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
151
152   if (ioctl (fd, SIOCSIFADDR, &ifr) != 0)
153     {
154       perror ("SIOCSIFADDR");
155       close (fd);
156       return;
157     }
158
159   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
160   r = inet_pton (AF_INET, mask, &addr->sin_addr.s_addr);
161   if (r < 0)
162     {
163       fprintf (stderr, "error at inet_pton: %m\n");
164       exit (1);
165     }
166
167   if (ioctl (fd, SIOCSIFNETMASK, &ifr) != 0)
168     {
169       perror ("SIOCSIFNETMASK");
170       close (fd);
171       return;
172     }
173
174   (void) ioctl (fd, SIOCGIFFLAGS, &ifr);
175   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
176   (void) ioctl (fd, SIOCSIFFLAGS, &ifr);
177   close (fd);
178 }                               /* }}} */
179
180 /**
181  * @brief sets the socket to nonblocking
182  *
183  * @param fd the socket
184  */
185 static void
186 setnonblocking (int fd)
187 {                               /*{{{ */
188   int opts;
189
190   opts = fcntl (fd, F_GETFL);
191   if (opts < 0)
192     {
193       perror ("fcntl(F_GETFL)");
194     }
195   opts = (opts | O_NONBLOCK);
196   if (fcntl (fd, F_SETFL, opts) < 0)
197     {
198       perror ("fcntl(F_SETFL)");
199     }
200   return;
201 }                               /*}}} */
202
203 int
204 main (int argc, char **argv)
205 {
206   unsigned char buf[MAX_SIZE];
207
208   char dev[IFNAMSIZ];
209   memset (dev, 0, IFNAMSIZ);
210
211   signal (SIGTERM, &term);
212
213   int fd_tun = init_tun (dev);
214
215   if (fd_tun < 0)
216     {
217       fprintf (stderr, "Could not initialize tun-interface: %s\n",
218                strerror (errno));
219       exit (1);
220     }
221
222   {
223     // TODO: get this out of argv
224     char address[] = "1234::1";
225     unsigned long prefix_len = 16;
226
227     set_address6 (dev, address, prefix_len);
228   }
229
230   {
231     char address[] = "10.10.10.1";
232     char mask[] = "255.255.255.252";
233
234     set_address4 (dev, address, mask);
235   }
236
237   uid_t uid = getuid ();
238   if (setresuid (uid, uid, uid) != 0)
239     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
240
241   setnonblocking (0);
242   setnonblocking (1);
243   setnonblocking (fd_tun);
244
245   fd_set fds_w;
246   fd_set fds_r;
247
248   int rea = 1;
249   int wri = 1;
250
251   int write_fd_possible = 0;
252   int write_stdout_possible = 0;
253 outer:
254   while ((rea == 1 || wri == 1) && running == 1)
255     {
256       FD_ZERO (&fds_w);
257       FD_ZERO (&fds_r);
258
259       if (rea)
260         {
261           FD_SET (fd_tun, &fds_r);
262           if (!write_stdout_possible)
263             FD_SET (1, &fds_w);
264         }
265
266       if (wri)
267         {
268           FD_SET (0, &fds_r);
269           if (!write_fd_possible)
270             FD_SET (fd_tun, &fds_w);
271         }
272
273       int r = select (fd_tun + 1, &fds_r, &fds_w, (fd_set *) 0, 0);
274
275       if (r > 0)
276         {
277           if (FD_ISSET (fd_tun, &fds_w))
278             write_fd_possible = 1;
279           if (FD_ISSET (1, &fds_w))
280             write_stdout_possible = 1;
281
282           if (FD_ISSET (0, &fds_r) && write_fd_possible)
283             {
284               write_fd_possible = 0;
285               struct suid_packet *pkt = (struct suid_packet *) buf;
286               r = read (0, buf, sizeof (struct GNUNET_MessageHeader));
287               if (r <= 0)
288                 {
289                   fprintf (stderr, "read-error: %s\n", strerror (errno));
290                   shutdown (fd_tun, SHUT_WR);
291                   shutdown (0, SHUT_RD);
292                   wri = 0;
293                   goto outer;
294                 }
295               if (pkt->hdr.type != ntohs (GNUNET_MESSAGE_TYPE_VPN_HELPER))
296                 abort ();
297               while (r < ntohs (pkt->hdr.size))
298                 {
299                   int t = read (0, buf + r, ntohs (pkt->hdr.size) - r);
300                   if (r < 0)
301                     {
302                       fprintf (stderr, "read-error: %s\n", strerror (errno));
303                       shutdown (fd_tun, SHUT_WR);
304                       shutdown (0, SHUT_RD);
305                       wri = 0;
306                       goto outer;
307                     }
308                   r += t;
309                 }
310               r = 0;
311               while (r <
312                      ntohs (pkt->hdr.size) -
313                      sizeof (struct GNUNET_MessageHeader))
314                 {
315                   int t = write (fd_tun, pkt->data,
316                                  ntohs (pkt->hdr.size) -
317                                  sizeof (struct GNUNET_MessageHeader) - r);
318                   if (t < 0)
319                     {
320                       fprintf (stderr, "write-error 3: %s\n",
321                                strerror (errno));
322                       shutdown (fd_tun, SHUT_WR);
323                       shutdown (0, SHUT_RD);
324                       wri = 0;
325                       goto outer;
326                     }
327                   r += t;
328                 }
329             }
330           else if (write_stdout_possible && FD_ISSET (fd_tun, &fds_r))
331             {
332               write_stdout_possible = 0;
333               r = read (fd_tun, buf, MAX_SIZE);
334               if (r <= 0)
335                 {
336                   fprintf (stderr, "read-error: %s\n", strerror (errno));
337                   shutdown (fd_tun, SHUT_RD);
338                   shutdown (1, SHUT_WR);
339                   rea = 0;
340                   goto outer;
341                 }
342               struct GNUNET_MessageHeader hdr = {.size =
343                   htons (r + sizeof (struct GNUNET_MessageHeader)),.type =
344                   htons (GNUNET_MESSAGE_TYPE_VPN_HELPER)
345               };
346               r = 0;
347               while (r < sizeof (struct GNUNET_MessageHeader))
348                 {
349                   int t =
350                     write (1, &hdr, sizeof (struct GNUNET_MessageHeader) - r);
351                   if (t < 0)
352                     {
353                       fprintf (stderr, "write-error 2: %s\n",
354                                strerror (errno));
355                       shutdown (fd_tun, SHUT_RD);
356                       shutdown (1, SHUT_WR);
357                       rea = 0;
358                       goto outer;
359                     }
360                   r += t;
361                 }
362               while (r < ntohs (hdr.size))
363                 {
364                   int t = write (1, buf, ntohs (hdr.size) - r);
365                   if (t < 0)
366                     {
367                       fprintf (stderr, "write-error 1: %s, written %d/%d\n",
368                                strerror (errno), r, ntohs (hdr.size));
369                       shutdown (fd_tun, SHUT_RD);
370                       shutdown (1, SHUT_WR);
371                       rea = 0;
372                       goto outer;
373                     }
374                   r += t;
375                 }
376             }
377         }
378     }
379
380   close (fd_tun);
381
382   return 0;
383 }