openvpnctrl: Allow ICMP error messages to pass the transfer net.
[people/teissler/ipfire-2.x.git] / src / misc-progs / openvpnctrl.c
1 #include <signal.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <arpa/inet.h>
8 #include <netinet/in.h>
9 #include <fcntl.h>
10 #include "setuid.h"
11 #include "netutil.h"
12 #include "libsmooth.h"
13
14 #define noovpndebug
15
16 // global vars
17 struct keyvalue *kv = NULL;
18 FILE *ifacefile = NULL;
19
20 char redif[STRING_SIZE];
21 char blueif[STRING_SIZE];
22 char orangeif[STRING_SIZE];
23 char enablered[STRING_SIZE] = "off";
24 char enableblue[STRING_SIZE] = "off";
25 char enableorange[STRING_SIZE] = "off";
26
27 // consts
28 char OVPNINPUT[STRING_SIZE] = "OVPNINPUT";
29 char OVPNBLOCK[STRING_SIZE] = "OVPNBLOCK";
30 char OVPNNAT[STRING_SIZE] = "OVPNNAT";
31 char WRAPPERVERSION[STRING_SIZE] = "ipfire-2.2.4";
32
33 struct connection_struct {
34 char name[STRING_SIZE];
35 char type[STRING_SIZE];
36 char proto[STRING_SIZE];
37 char status[STRING_SIZE];
38 char local_subnet[STRING_SIZE];
39 char transfer_subnet[STRING_SIZE];
40 char role[STRING_SIZE];
41 char port[STRING_SIZE];
42 struct connection_struct *next;
43 };
44
45 typedef struct connection_struct connection;
46
47 void exithandler(void)
48 {
49 if(kv)
50 freekeyvalues(kv);
51 if (ifacefile)
52 fclose(ifacefile);
53 }
54
55 void usage(void)
56 {
57 #ifdef ovpndebug
58 printf("Wrapper for OpenVPN %s-debug\n", WRAPPERVERSION);
59 #else
60 printf("Wrapper for OpenVPN %s\n", WRAPPERVERSION);
61 #endif
62 printf("openvpnctrl <option>\n");
63 printf(" Valid options are:\n");
64 printf(" -s --start\n");
65 printf(" starts OpenVPN (implicitly creates chains and firewall rules)\n");
66 printf(" -k --kill\n");
67 printf(" kills/stops OpenVPN\n");
68 printf(" -r --restart\n");
69 printf(" restarts OpenVPN (implicitly creates chains and firewall rules)\n");
70 printf(" -sn2n --start-net-2-net\n");
71 printf(" starts all net2net connections\n");
72 printf(" you may pass a connection name to the switch to only start a specific one\n");
73 printf(" -kn2n --kill-net-2-net\n");
74 printf(" kills all net2net connections\n");
75 printf(" you may pass a connection name to the switch to only start a specific one\n");
76 printf(" -d --display\n");
77 printf(" displays OpenVPN status to syslog\n");
78 printf(" -fwr --firewall-rules\n");
79 printf(" removes current OpenVPN chains and rules and resets them according to the config\n");
80 printf(" -sdo --start-daemon-only\n");
81 printf(" starts OpenVPN daemon only\n");
82 exit(1);
83 }
84
85 connection *getConnections() {
86 FILE *fp = NULL;
87
88 if (!(fp = fopen(CONFIG_ROOT "/ovpn/ovpnconfig", "r"))) {
89 fprintf(stderr, "Could not open openvpn n2n configuration file.\n");
90 exit(1);
91 }
92
93 char line[STRING_SIZE] = "";
94 char result[STRING_SIZE] = "";
95 char *resultptr;
96 int count;
97 connection *conn_first = NULL;
98 connection *conn_last = NULL;
99 connection *conn_curr;
100
101 while ((fgets(line, STRING_SIZE, fp) != NULL)) {
102 if (line[strlen(line) - 1] == '\n')
103 line[strlen(line) - 1] = '\0';
104
105 conn_curr = (connection *)malloc(sizeof(connection));
106 memset(conn_curr, 0, sizeof(connection));
107
108 if (conn_first == NULL) {
109 conn_first = conn_curr;
110 } else {
111 conn_last->next = conn_curr;
112 }
113 conn_last = conn_curr;
114
115 count = 0;
116 char *lineptr = &line;
117 while (1) {
118 if (*lineptr == NULL)
119 break;
120
121 resultptr = result;
122 while (*lineptr != NULL) {
123 if (*lineptr == ',') {
124 lineptr++;
125 break;
126 }
127 *resultptr++ = *lineptr++;
128 }
129 *resultptr = '\0';
130
131 if (count == 1) {
132 strcpy(conn_curr->status, result);
133 } else if (count == 2) {
134 strcpy(conn_curr->name, result);
135 } else if (count == 4) {
136 strcpy(conn_curr->type, result);
137 } else if (count == 7) {
138 strcpy(conn_curr->role, result);
139 } else if (count == 9) {
140 strcpy(conn_curr->local_subnet, result);
141 } else if (count == 28) {
142 strcpy(conn_curr->transfer_subnet, result);
143 } else if (count == 29) {
144 strcpy(conn_curr->proto, result);
145 } else if (count == 30) {
146 strcpy(conn_curr->port, result);
147 }
148
149 count++;
150 }
151 }
152
153 fclose(fp);
154
155 return conn_first;
156 }
157
158 int readPidFile(const char *pidfile) {
159 FILE *fp = fopen(pidfile, "r");
160 if (fp == NULL) {
161 exit(1);
162 }
163
164 int pid = 0;
165 fscanf(fp, "%d", &pid);
166 fclose(fp);
167
168 return pid;
169 }
170
171 int readExternalAddress(char* address) {
172 FILE *fp = fopen("/var/ipfire/red/local-ipaddress", "r");
173 if (!fp)
174 goto ERROR;
175
176 int r = fscanf(fp, "%s", address);
177 fclose(fp);
178
179 if (r < 0)
180 goto ERROR;
181
182 /* In case the read IP address is not valid, we empty
183 * the content of address and return non-zero. */
184 if (!VALID_IP(address))
185 goto ERROR;
186
187 return 0;
188
189 ERROR:
190 address = NULL;
191 return 1;
192 }
193
194 void ovpnInit(void) {
195 // Read OpenVPN configuration
196 kv = initkeyvalues();
197 if (!readkeyvalues(kv, CONFIG_ROOT "/ovpn/settings")) {
198 fprintf(stderr, "Cannot read ovpn settings\n");
199 exit(1);
200 }
201
202 if (!findkey(kv, "ENABLED", enablered)) {
203 exit(1);
204 }
205
206 if (!findkey(kv, "ENABLED_BLUE", enableblue)){
207 exit(1);
208 }
209
210 if (!findkey(kv, "ENABLED_ORANGE", enableorange)){
211 exit(1);
212 }
213 freekeyvalues(kv);
214
215 // read interface settings
216
217 // details for the red int
218 memset(redif, 0, STRING_SIZE);
219 if ((ifacefile = fopen(CONFIG_ROOT "/red/iface", "r")))
220 {
221 if (fgets(redif, STRING_SIZE, ifacefile))
222 {
223 if (redif[strlen(redif) - 1] == '\n')
224 redif[strlen(redif) - 1] = '\0';
225 }
226 fclose (ifacefile);
227 ifacefile = NULL;
228
229 if (!VALID_DEVICE(redif))
230 {
231 memset(redif, 0, STRING_SIZE);
232 }
233 }
234
235 kv=initkeyvalues();
236 if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) {
237 fprintf(stderr, "Cannot read ethernet settings\n");
238 exit(1);
239 }
240
241 if (strcmp(enableblue, "on") == 0) {
242 if (!findkey(kv, "BLUE_DEV", blueif)) {
243 exit(1);
244 }
245 }
246
247 if (strcmp(enableorange, "on") == 0) {
248 if (!findkey(kv, "ORANGE_DEV", orangeif)) {
249 exit(1);
250 }
251 }
252 freekeyvalues(kv);
253 }
254
255 void executeCommand(char *command) {
256 #ifdef ovpndebug
257 printf(strncat(command, "\n", 2));
258 #endif
259 safe_system(strncat(command, " >/dev/null 2>&1", 17));
260 }
261
262 void addRule(const char *chain, const char *interface, const char *protocol, const char *port) {
263 char command[STRING_SIZE];
264
265 snprintf(command, STRING_SIZE - 1, "/sbin/iptables -A %s -i %s -p %s --dport %s -j ACCEPT",
266 chain, interface, protocol, port);
267 executeCommand(command);
268 }
269
270 void flushChain(char *chain) {
271 char str[STRING_SIZE];
272
273 snprintf(str, STRING_SIZE - 1, "/sbin/iptables -F %s", chain);
274 executeCommand(str);
275 }
276
277 void flushChainNAT(char *chain) {
278 char str[STRING_SIZE];
279
280 snprintf(str, STRING_SIZE - 1, "/sbin/iptables -t nat -F %s", chain);
281 executeCommand(str);
282 }
283
284 char* calcTransferNetAddress(const connection* conn) {
285 char *subnetmask = strdup(conn->transfer_subnet);
286 char *address = strsep(&subnetmask, "/");
287
288 if ((address == NULL) || (subnetmask == NULL)) {
289 goto ERROR;
290 }
291
292 in_addr_t _address = inet_addr(address);
293 in_addr_t _subnetmask = inet_addr(subnetmask);
294 _address &= _subnetmask;
295
296 if (strcmp(conn->role, "server") == 0) {
297 _address += 1 << 24;
298 } else if (strcmp(conn->role, "client") == 0) {
299 _address += 2 << 24;
300 } else {
301 goto ERROR;
302 }
303
304 struct in_addr address_info;
305 address_info.s_addr = _address;
306
307 return inet_ntoa(address_info);
308
309 ERROR:
310 fprintf(stderr, "Could not determine transfer net address: %s\n", conn->name);
311
312 free(address);
313 return NULL;
314 }
315
316 char* getLocalSubnetAddress(const connection* conn) {
317 kv = initkeyvalues();
318 if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) {
319 fprintf(stderr, "Cannot read ethernet settings\n");
320 exit(1);
321 }
322
323 const char *zones[] = {"GREEN", "BLUE", "ORANGE", NULL};
324 char *zone = NULL;
325
326 // Get net address of the local openvpn subnet.
327 char *subnetmask = strdup(conn->local_subnet);
328 char *address = strsep(&subnetmask, "/");
329
330 if ((address == NULL) || (subnetmask == NULL)) {
331 goto ERROR;
332 }
333
334 in_addr_t _address = inet_addr(address);
335 in_addr_t _subnetmask = inet_addr(subnetmask);
336
337 in_addr_t _netaddr = (_address & _subnetmask);
338 in_addr_t _broadcast = (_address | ~_subnetmask);
339
340 char zone_address_key[STRING_SIZE];
341 char zone_address[STRING_SIZE];
342 in_addr_t zone_addr;
343
344 int i = 0;
345 while (zones[i]) {
346 zone = zones[i++];
347 snprintf(zone_address_key, STRING_SIZE, "%s_ADDRESS", zone);
348
349 if (!findkey(kv, zone_address_key, zone_address))
350 continue;
351
352 zone_addr = inet_addr(zone_address);
353 if ((zone_addr > _netaddr) && (zone_addr < _broadcast)) {
354 freekeyvalues(kv);
355
356 return strdup(zone_address);
357 }
358 }
359
360 ERROR:
361 fprintf(stderr, "Could not determine local subnet address: %s\n", conn->name);
362
363 freekeyvalues(kv);
364 return NULL;
365 }
366
367 void setFirewallRules(void) {
368 char command[STRING_SIZE];
369 char protocol[STRING_SIZE] = "";
370 char dport[STRING_SIZE] = "";
371 char dovpnip[STRING_SIZE] = "";
372
373 kv = initkeyvalues();
374 if (!readkeyvalues(kv, CONFIG_ROOT "/ovpn/settings"))
375 {
376 fprintf(stderr, "Cannot read ovpn settings\n");
377 exit(1);
378 }
379
380 /* we got one device, so lets proceed further */
381 if (!findkey(kv, "DDEST_PORT", dport)){
382 fprintf(stderr, "Cannot read DDEST_PORT\n");
383 exit(1);
384 }
385
386 if (!findkey(kv, "DPROTOCOL", protocol)){
387 fprintf(stderr, "Cannot read DPROTOCOL\n");
388 exit(1);
389 }
390
391 if (!findkey(kv, "VPN_IP", dovpnip)){
392 fprintf(stderr, "Cannot read VPN_IP\n");
393 }
394 freekeyvalues(kv);
395
396 // Flush all chains.
397 flushChain(OVPNINPUT);
398 flushChain(OVPNBLOCK);
399 flushChainNAT(OVPNNAT);
400
401 // set firewall rules
402 if (!strcmp(enablered, "on") && strlen(redif))
403 addRule(OVPNINPUT, redif, protocol, dport);
404 if (!strcmp(enableblue, "on") && strlen(blueif))
405 addRule(OVPNINPUT, blueif, protocol, dport);
406 if (!strcmp(enableorange, "on") && strlen(orangeif))
407 addRule(OVPNINPUT, orangeif, protocol, dport);
408
409 /* Allow ICMP error messages to pass. */
410 snprintf(command, STRING_SIZE - 1, "/sbin/iptables -A %s -p icmp"
411 " -m conntrack --ctstate RELATED -j RETURN", OVPNBLOCK);
412 executeCommand(command);
413
414 // read connection configuration
415 connection *conn = getConnections();
416
417 // set firewall rules for n2n connections
418 char *local_subnet_address = NULL;
419 char *transfer_subnet_address = NULL;
420 while (conn != NULL) {
421 if (strcmp(conn->type, "net") == 0) {
422 addRule(OVPNINPUT, redif, conn->proto, conn->port);
423
424 /* Block all communication from the transfer nets. */
425 snprintf(command, STRING_SIZE - 1, "/sbin/iptables -A %s -s %s -j DROP",
426 OVPNBLOCK, conn->transfer_subnet);
427 executeCommand(command);
428
429 local_subnet_address = getLocalSubnetAddress(conn);
430 transfer_subnet_address = calcTransferNetAddress(conn);
431
432 if ((local_subnet_address) && (transfer_subnet_address)) {
433 snprintf(command, STRING_SIZE - 1, "/sbin/iptables -t nat -A %s -s %s -j SNAT --to-source %s",
434 OVPNNAT, transfer_subnet_address, local_subnet_address);
435 executeCommand(command);
436 }
437 }
438
439 conn = conn->next;
440 }
441 }
442
443 void stopDaemon(void) {
444 char command[STRING_SIZE];
445
446 int pid = readPidFile("/var/run/openvpn.pid");
447 if (!pid > 0) {
448 exit(1);
449 }
450
451 fprintf(stderr, "Killing PID %d.\n", pid);
452 kill(pid, SIGTERM);
453
454 snprintf(command, STRING_SIZE - 1, "/bin/rm -f /var/run/openvpn.pid");
455 executeCommand(command);
456 }
457
458 void startDaemon(void) {
459 char command[STRING_SIZE];
460
461 if (!((strcmp(enablered, "on") == 0) || (strcmp(enableblue, "on") == 0) || (strcmp(enableorange, "on") == 0))) {
462 fprintf(stderr, "OpenVPN is not enabled on any interface\n");
463 exit(1);
464 } else {
465 snprintf(command, STRING_SIZE-1, "/sbin/modprobe tun");
466 executeCommand(command);
467 snprintf(command, STRING_SIZE-1, "/usr/sbin/openvpn --config /var/ipfire/ovpn/server.conf");
468 executeCommand(command);
469 }
470 }
471
472 int startNet2Net(char *name) {
473 connection *conn = NULL;
474 connection *conn_iter;
475
476 conn_iter = getConnections();
477
478 while (conn_iter) {
479 if ((strcmp(conn_iter->type, "net") == 0) && (strcmp(conn_iter->name, name) == 0)) {
480 conn = conn_iter;
481 break;
482 }
483 conn_iter = conn_iter->next;
484 }
485
486 if (conn == NULL) {
487 fprintf(stderr, "Connection not found.\n");
488 return 1;
489 }
490
491 if (strcmp(conn->status, "on") != 0) {
492 fprintf(stderr, "Connection '%s' is not enabled.\n", conn->name);
493 return 1;
494 }
495
496 fprintf(stderr, "Starting connection %s...\n", conn->name);
497
498 char configfile[STRING_SIZE];
499 snprintf(configfile, STRING_SIZE - 1, CONFIG_ROOT "/ovpn/n2nconf/%s/%s.conf",
500 conn->name, conn->name);
501
502 FILE *fp = fopen(configfile, "r");
503 if (fp == NULL) {
504 fprintf(stderr, "Could not find configuration file for connection '%s' at '%s'.\n",
505 conn->name, configfile);
506 return 2;
507 }
508 fclose(fp);
509
510 // Make sure all firewall rules are up to date.
511 setFirewallRules();
512
513 // Get the external IP address.
514 char address[STRING_SIZE] = "";
515 int r = readExternalAddress(address);
516 if (r) {
517 fprintf(stderr, "Could not read the external address\n");
518 exit(1);
519 }
520
521 char command[STRING_SIZE];
522 snprintf(command, STRING_SIZE-1, "/sbin/modprobe tun");
523 executeCommand(command);
524 snprintf(command, STRING_SIZE-1, "/usr/sbin/openvpn --local %s --config %s", address, configfile);
525 executeCommand(command);
526
527 return 0;
528 }
529
530 int killNet2Net(char *name) {
531 connection *conn = NULL;
532 connection *conn_iter;
533
534 conn_iter = getConnections();
535
536 while (conn_iter) {
537 if (strcmp(conn_iter->name, name) == 0) {
538 conn = conn_iter;
539 break;
540 }
541 conn_iter = conn_iter->next;
542 }
543
544 if (conn == NULL) {
545 fprintf(stderr, "Connection not found.\n");
546 return 1;
547 }
548
549 char pidfile[STRING_SIZE];
550 snprintf(pidfile, STRING_SIZE - 1, "/var/run/%sn2n.pid", conn->name);
551
552 int pid = readPidFile(pidfile);
553 if (!pid > 0) {
554 fprintf(stderr, "Could not read pid file of connection %s.", conn->name);
555 return 1;
556 }
557
558 fprintf(stderr, "Killing connection %s (PID %d)...\n", conn->name, pid);
559 kill(pid, SIGTERM);
560
561 char command[STRING_SIZE];
562 snprintf(command, STRING_SIZE - 1, "/bin/rm -f %s", pidfile);
563 executeCommand(command);
564
565 return 0;
566 }
567
568 void startAllNet2Net() {
569 int exitcode = 0, _exitcode = 0;
570
571 connection *conn = getConnections();
572
573 while(conn) {
574 /* Skip all connections that are not of type "net" or disabled. */
575 if ((strcmp(conn->type, "net") != 0) || (strcmp(conn->status, "on") != 0)) {
576 conn = conn->next;
577 continue;
578 }
579
580 _exitcode = startNet2Net(conn->name);
581 conn = conn->next;
582
583 if (_exitcode > exitcode) {
584 exitcode = _exitcode;
585 }
586 }
587
588 exit(exitcode);
589 }
590
591 void killAllNet2Net() {
592 int exitcode = 0, _exitcode = 0;
593
594 connection *conn = getConnections();
595
596 while(conn) {
597 /* Skip all connections that are not of type "net". */
598 if (strcmp(conn->type, "net") != 0) {
599 conn = conn->next;
600 continue;
601 }
602
603 _exitcode = killNet2Net(conn->name);
604 conn = conn->next;
605
606 if (_exitcode > exitcode) {
607 exitcode = _exitcode;
608 }
609 }
610
611 exit(exitcode);
612 }
613
614 void displayopenvpn(void) {
615 char command[STRING_SIZE];
616
617 snprintf(command, STRING_SIZE - 1, "/bin/killall -sSIGUSR2 openvpn");
618 executeCommand(command);
619 }
620
621 int main(int argc, char *argv[]) {
622 if (!(initsetuid()))
623 exit(1);
624 if(argc < 2)
625 usage();
626
627 if(argc == 3) {
628 ovpnInit();
629
630 if( (strcmp(argv[1], "-sn2n") == 0) || (strcmp(argv[1], "--start-net-2-net") == 0) ) {
631 startNet2Net(argv[2]);
632 return 0;
633 }
634 else if( (strcmp(argv[1], "-kn2n") == 0) || (strcmp(argv[1], "--kill-net-2-net") == 0) ) {
635 killNet2Net(argv[2]);
636 return 0;
637 } else {
638 usage();
639 return 1;
640 }
641 }
642 else if(argc == 2) {
643 if( (strcmp(argv[1], "-k") == 0) || (strcmp(argv[1], "--kill") == 0) ) {
644 stopDaemon();
645 return 0;
646 }
647 else if( (strcmp(argv[1], "-d") == 0) || (strcmp(argv[1], "--display") == 0) ) {
648 displayopenvpn();
649 return 0;
650 }
651 else {
652 ovpnInit();
653
654 if( (strcmp(argv[1], "-s") == 0) || (strcmp(argv[1], "--start") == 0) ) {
655 setFirewallRules();
656 startDaemon();
657 return 0;
658 }
659 else if( (strcmp(argv[1], "-sn2n") == 0) || (strcmp(argv[1], "--start-net-2-net") == 0) ) {
660 startAllNet2Net();
661 return 0;
662 }
663 else if( (strcmp(argv[1], "-kn2n") == 0) || (strcmp(argv[1], "--kill-net-2-net") == 0) ) {
664 killAllNet2Net();
665 return 0;
666 }
667 else if( (strcmp(argv[1], "-sdo") == 0) || (strcmp(argv[1], "--start-daemon-only") == 0) ) {
668 startDaemon();
669 return 0;
670 }
671 else if( (strcmp(argv[1], "-r") == 0) || (strcmp(argv[1], "--restart") == 0) ) {
672 stopDaemon();
673 setFirewallRules();
674 startDaemon();
675 return 0;
676 }
677 else if( (strcmp(argv[1], "-fwr") == 0) || (strcmp(argv[1], "--firewall-rules") == 0) ) {
678 setFirewallRules();
679 return 0;
680 }
681 else {
682 usage();
683 return 0;
684 }
685 }
686 }
687 else {
688 usage();
689 return 0;
690 }
691 return 0;
692 }
693