X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fmisc-progs%2Fopenvpnctrl.c;h=272db0faade2dc3abb79687ad1d077c7308b4175;hb=6d3c8250fe1e6ef297d01bcc7a19672adfff70e6;hp=00b35e1aba738337d1b76cb80d3d66b38b1ab5f7;hpb=1129c37a95009f8f4ee3222a09c12c3f9d374d9d;p=people%2Fteissler%2Fipfire-2.x.git diff --git a/src/misc-progs/openvpnctrl.c b/src/misc-progs/openvpnctrl.c index 00b35e1ab..272db0faa 100644 --- a/src/misc-progs/openvpnctrl.c +++ b/src/misc-progs/openvpnctrl.c @@ -4,8 +4,11 @@ #include #include #include +#include +#include #include #include "setuid.h" +#include "netutil.h" #include "libsmooth.h" #define noovpndebug @@ -22,15 +25,20 @@ char enableblue[STRING_SIZE] = "off"; char enableorange[STRING_SIZE] = "off"; // consts -char OVPNRED[STRING_SIZE] = "OVPN"; -char OVPNBLUE[STRING_SIZE] = "OVPN_BLUE_"; -char OVPNORANGE[STRING_SIZE] = "OVPN_ORANGE_"; -char WRAPPERVERSION[STRING_SIZE] = "ipfire-2.1.2"; +char OVPNINPUT[STRING_SIZE] = "OVPNINPUT"; +char OVPNBLOCK[STRING_SIZE] = "OVPNBLOCK"; +char OVPNNAT[STRING_SIZE] = "OVPNNAT"; +char WRAPPERVERSION[STRING_SIZE] = "ipfire-2.2.4"; struct connection_struct { char name[STRING_SIZE]; + char type[STRING_SIZE]; char proto[STRING_SIZE]; - int port; + char status[STRING_SIZE]; + char local_subnet[STRING_SIZE]; + char transfer_subnet[STRING_SIZE]; + char role[STRING_SIZE]; + char port[STRING_SIZE]; struct connection_struct *next; }; @@ -59,16 +67,18 @@ void usage(void) printf(" kills/stops OpenVPN\n"); printf(" -r --restart\n"); printf(" restarts OpenVPN (implicitly creates chains and firewall rules)\n"); + printf(" -sn2n --start-net-2-net\n"); + printf(" starts all net2net connections\n"); + printf(" you may pass a connection name to the switch to only start a specific one\n"); + printf(" -kn2n --kill-net-2-net\n"); + printf(" kills all net2net connections\n"); + printf(" you may pass a connection name to the switch to only start a specific one\n"); printf(" -d --display\n"); printf(" displays OpenVPN status to syslog\n"); printf(" -fwr --firewall-rules\n"); printf(" removes current OpenVPN chains and rules and resets them according to the config\n"); printf(" -sdo --start-daemon-only\n"); printf(" starts OpenVPN daemon only\n"); - printf(" -ccr --create-chains-and-rules\n"); - printf(" creates chains and rules for OpenVPN\n"); - printf(" -dcr --delete-chains-and-rules\n"); - printf(" removes all chains for OpenVPN\n"); exit(1); } @@ -81,7 +91,8 @@ connection *getConnections() { } char line[STRING_SIZE] = ""; - char *result; + char result[STRING_SIZE] = ""; + char *resultptr; int count; connection *conn_first = NULL; connection *conn_last = NULL; @@ -102,17 +113,39 @@ connection *getConnections() { conn_last = conn_curr; count = 0; - result = strtok(line, ","); - while (result) { - if (count == 2) { + char *lineptr = &line; + while (1) { + if (*lineptr == NULL) + break; + + resultptr = result; + while (*lineptr != NULL) { + if (*lineptr == ',') { + lineptr++; + break; + } + *resultptr++ = *lineptr++; + } + *resultptr = '\0'; + + if (count == 1) { + strcpy(conn_curr->status, result); + } else if (count == 2) { strcpy(conn_curr->name, result); - } else if (count == 12) { + } else if (count == 4) { + strcpy(conn_curr->type, result); + } else if (count == 7) { + strcpy(conn_curr->role, result); + } else if (count == 9) { + strcpy(conn_curr->local_subnet, result); + } else if (count == 28) { + strcpy(conn_curr->transfer_subnet, result); + } else if (count == 29) { strcpy(conn_curr->proto, result); - } else if (count == 13) { - conn_curr->port = atoi(result); + } else if (count == 30) { + strcpy(conn_curr->port, result); } - result = strtok(NULL, ","); count++; } } @@ -125,7 +158,6 @@ connection *getConnections() { int readPidFile(const char *pidfile) { FILE *fp = fopen(pidfile, "r"); if (fp == NULL) { - fprintf(stderr, "PID file not found: '%s'\n", pidfile); exit(1); } @@ -136,8 +168,30 @@ int readPidFile(const char *pidfile) { return pid; } +int readExternalAddress(char* address) { + FILE *fp = fopen("/var/ipfire/red/local-ipaddress", "r"); + if (!fp) + goto ERROR; + + int r = fscanf(fp, "%s", address); + fclose(fp); + + if (r < 0) + goto ERROR; + + /* In case the read IP address is not valid, we empty + * the content of address and return non-zero. */ + if (!VALID_IP(address)) + goto ERROR; + + return 0; + +ERROR: + address = NULL; + return 1; +} + void ovpnInit(void) { - // Read OpenVPN configuration kv = initkeyvalues(); if (!readkeyvalues(kv, CONFIG_ROOT "/ovpn/settings")) { @@ -146,17 +200,14 @@ void ovpnInit(void) { } if (!findkey(kv, "ENABLED", enablered)) { - fprintf(stderr, "Cannot read ENABLED\n"); exit(1); } if (!findkey(kv, "ENABLED_BLUE", enableblue)){ - fprintf(stderr, "Cannot read ENABLED_BLUE\n"); exit(1); } if (!findkey(kv, "ENABLED_ORANGE", enableorange)){ - fprintf(stderr, "Cannot read ENABLED_ORANGE\n"); exit(1); } freekeyvalues(kv); @@ -182,24 +233,22 @@ void ovpnInit(void) { } kv=initkeyvalues(); - if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) - { + if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) { fprintf(stderr, "Cannot read ethernet settings\n"); exit(1); } - if (strcmp(enableblue, "on")==0){ - if (!findkey(kv, "BLUE_DEV", blueif)){ - fprintf(stderr, "Cannot read BLUE_DEV\n"); + if (strcmp(enableblue, "on") == 0) { + if (!findkey(kv, "BLUE_DEV", blueif)) { exit(1); } } - if (strcmp(enableorange, "on")==0){ - if (!findkey(kv, "ORANGE_DEV", orangeif)){ - fprintf(stderr, "Cannot read ORNAGE_DEV\n"); + + if (strcmp(enableorange, "on") == 0) { + if (!findkey(kv, "ORANGE_DEV", orangeif)) { exit(1); } - } + } freekeyvalues(kv); } @@ -210,108 +259,109 @@ void executeCommand(char *command) { safe_system(strncat(command, " >/dev/null 2>&1", 17)); } -void setChainRules(char *chain, char *interface, char *protocol, char *port) -{ - char str[STRING_SIZE]; +void addRule(const char *chain, const char *interface, const char *protocol, const char *port) { + char command[STRING_SIZE]; - sprintf(str, "/sbin/iptables -A %sINPUT -i %s -p %s --dport %s -j ACCEPT", chain, interface, protocol, port); - executeCommand(str); - sprintf(str, "/sbin/iptables -A %sINPUT -i tun+ -j ACCEPT", chain); - executeCommand(str); - sprintf(str, "/sbin/iptables -A %sFORWARD -i tun+ -j ACCEPT", chain); - executeCommand(str); + snprintf(command, STRING_SIZE - 1, "/sbin/iptables -A %s -i %s -p %s --dport %s -j ACCEPT", + chain, interface, protocol, port); + executeCommand(command); } void flushChain(char *chain) { char str[STRING_SIZE]; - sprintf(str, "/sbin/iptables -F %sINPUT", chain); + snprintf(str, STRING_SIZE - 1, "/sbin/iptables -F %s", chain); executeCommand(str); - sprintf(str, "/sbin/iptables -F %sFORWARD", chain); - executeCommand(str); - safe_system(str); } -void deleteChainReference(char *chain) { +void flushChainNAT(char *chain) { char str[STRING_SIZE]; - sprintf(str, "/sbin/iptables -D INPUT -j %sINPUT", chain); - executeCommand(str); - safe_system(str); - sprintf(str, "/sbin/iptables -D FORWARD -j %sFORWARD", chain); + snprintf(str, STRING_SIZE - 1, "/sbin/iptables -t nat -F %s", chain); executeCommand(str); - safe_system(str); } -void deleteChain(char *chain) { - char str[STRING_SIZE]; +char* calcTransferNetAddress(const connection* conn) { + char *subnetmask = strdup(conn->transfer_subnet); + char *address = strsep(&subnetmask, "/"); - sprintf(str, "/sbin/iptables -X %sINPUT", chain); - executeCommand(str); - sprintf(str, "/sbin/iptables -X %sFORWARD", chain); - executeCommand(str); -} + if ((address == NULL) || (subnetmask == NULL)) { + goto ERROR; + } -void deleteAllChains(void) { - // not an elegant solution, but to avoid timing problems with undeleted chain references - deleteChainReference(OVPNRED); - deleteChainReference(OVPNBLUE); - deleteChainReference(OVPNORANGE); - flushChain(OVPNRED); - flushChain(OVPNBLUE); - flushChain(OVPNORANGE); - deleteChain(OVPNRED); - deleteChain(OVPNBLUE); - deleteChain(OVPNORANGE); -} + in_addr_t _address = inet_addr(address); + in_addr_t _subnetmask = inet_addr(subnetmask); + _address &= _subnetmask; -void createChainReference(char *chain) { - char str[STRING_SIZE]; - sprintf(str, "/sbin/iptables -I INPUT %s -j %sINPUT", "14", chain); - executeCommand(str); - sprintf(str, "/sbin/iptables -I FORWARD %s -j %sFORWARD", "12", chain); - executeCommand(str); -} + if (strcmp(conn->role, "server") == 0) { + _address += 1 << 24; + } else if (strcmp(conn->role, "client") == 0) { + _address += 2 << 24; + } else { + goto ERROR; + } -void createChain(char *chain) { - char str[STRING_SIZE]; - sprintf(str, "/sbin/iptables -N %sINPUT", chain); - executeCommand(str); - sprintf(str, "/sbin/iptables -N %sFORWARD", chain); - executeCommand(str); + struct in_addr address_info; + address_info.s_addr = _address; + + return inet_ntoa(address_info); + +ERROR: + fprintf(stderr, "Could not determine transfer net address: %s\n", conn->name); + + free(address); + return NULL; } -void createAllChains(void) { - // create chain and chain references - if (!strcmp(enableorange, "on")) { - if (strlen(orangeif)) { - createChain(OVPNORANGE); - createChainReference(OVPNORANGE); - } else { - fprintf(stderr, "OpenVPN enabled on orange but no orange interface found\n"); - //exit(1); - } +char* getLocalSubnetAddress(const connection* conn) { + kv = initkeyvalues(); + if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) { + fprintf(stderr, "Cannot read ethernet settings\n"); + exit(1); } - if (!strcmp(enableblue, "on")) { - if (strlen(blueif)) { - createChain(OVPNBLUE); - createChainReference(OVPNBLUE); - } else { - fprintf(stderr, "OpenVPN enabled on blue but no blue interface found\n"); - //exit(1); - } + const char *zones[] = {"GREEN", "BLUE", "ORANGE", NULL}; + char *zone = NULL; + + // Get net address of the local openvpn subnet. + char *subnetmask = strdup(conn->local_subnet); + char *address = strsep(&subnetmask, "/"); + + if ((address == NULL) || (subnetmask == NULL)) { + goto ERROR; } - if (!strcmp(enablered, "on")) { - if (strlen(redif)) { - createChain(OVPNRED); - createChainReference(OVPNRED); - } else { - fprintf(stderr, "OpenVPN enabled on red but no red interface found\n"); - //exit(1); + in_addr_t _address = inet_addr(address); + in_addr_t _subnetmask = inet_addr(subnetmask); + + in_addr_t _netaddr = (_address & _subnetmask); + in_addr_t _broadcast = (_address | ~_subnetmask); + + char zone_address_key[STRING_SIZE]; + char zone_address[STRING_SIZE]; + in_addr_t zone_addr; + + int i = 0; + while (zones[i]) { + zone = zones[i++]; + snprintf(zone_address_key, STRING_SIZE, "%s_ADDRESS", zone); + + if (!findkey(kv, zone_address_key, zone_address)) + continue; + + zone_addr = inet_addr(zone_address); + if ((zone_addr > _netaddr) && (zone_addr < _broadcast)) { + freekeyvalues(kv); + + return strdup(zone_address); } } + +ERROR: + fprintf(stderr, "Could not determine local subnet address: %s\n", conn->name); + + freekeyvalues(kv); + return NULL; } void setFirewallRules(void) { @@ -339,31 +389,48 @@ void setFirewallRules(void) { if (!findkey(kv, "VPN_IP", dovpnip)){ fprintf(stderr, "Cannot read VPN_IP\n"); -// exit(1); step further as we don't need an ip } freekeyvalues(kv); - // read connection configuration - connection *conn = getConnections(); - // Flush all chains. - flushChain(OVPNRED); - flushChain(OVPNBLUE); - flushChain(OVPNORANGE); + flushChain(OVPNINPUT); + flushChain(OVPNBLOCK); + flushChainNAT(OVPNNAT); // set firewall rules if (!strcmp(enablered, "on") && strlen(redif)) - setChainRules(OVPNRED, redif, protocol, dport); + addRule(OVPNINPUT, redif, protocol, dport); if (!strcmp(enableblue, "on") && strlen(blueif)) - setChainRules(OVPNBLUE, blueif, protocol, dport); + addRule(OVPNINPUT, blueif, protocol, dport); if (!strcmp(enableorange, "on") && strlen(orangeif)) - setChainRules(OVPNORANGE, orangeif, protocol, dport); + addRule(OVPNINPUT, orangeif, protocol, dport); + + // read connection configuration + connection *conn = getConnections(); // set firewall rules for n2n connections - char port[STRING_SIZE]; + char command[STRING_SIZE]; + char *local_subnet_address = NULL; + char *transfer_subnet_address = NULL; while (conn != NULL) { - sprintf(port, "%d", conn->port); - setChainRules(OVPNRED, redif, conn->proto, port); + if (strcmp(conn->type, "net") == 0) { + addRule(OVPNINPUT, redif, conn->proto, conn->port); + + /* Block all communication from the transfer nets. */ + snprintf(command, STRING_SIZE - 1, "/sbin/iptables -A %s -s %s -j DROP", + OVPNBLOCK, conn->transfer_subnet); + executeCommand(command); + + local_subnet_address = getLocalSubnetAddress(conn); + transfer_subnet_address = calcTransferNetAddress(conn); + + if ((local_subnet_address) && (transfer_subnet_address)) { + snprintf(command, STRING_SIZE - 1, "/sbin/iptables -t nat -A %s -s %s -j SNAT --to-source %s", + OVPNNAT, transfer_subnet_address, local_subnet_address); + executeCommand(command); + } + } + conn = conn->next; } } @@ -386,7 +453,7 @@ void stopDaemon(void) { void startDaemon(void) { char command[STRING_SIZE]; - if (!((strcmp(enablered, "on")==0) || (strcmp(enableblue, "on")==0) || (strcmp(enableorange, "on")==0))){ + if (!((strcmp(enablered, "on") == 0) || (strcmp(enableblue, "on") == 0) || (strcmp(enableorange, "on") == 0))) { fprintf(stderr, "OpenVPN is not enabled on any interface\n"); exit(1); } else { @@ -397,14 +464,14 @@ void startDaemon(void) { } } -void startNet2Net(char *name) { +int startNet2Net(char *name) { connection *conn = NULL; connection *conn_iter; conn_iter = getConnections(); while (conn_iter) { - if (strcmp(conn_iter->name, name) == 0) { + if ((strcmp(conn_iter->type, "net") == 0) && (strcmp(conn_iter->name, name) == 0)) { conn = conn_iter; break; } @@ -413,9 +480,16 @@ void startNet2Net(char *name) { if (conn == NULL) { fprintf(stderr, "Connection not found.\n"); - exit(1); + return 1; } + if (strcmp(conn->status, "on") != 0) { + fprintf(stderr, "Connection '%s' is not enabled.\n", conn->name); + return 1; + } + + fprintf(stderr, "Starting connection %s...\n", conn->name); + char configfile[STRING_SIZE]; snprintf(configfile, STRING_SIZE - 1, CONFIG_ROOT "/ovpn/n2nconf/%s/%s.conf", conn->name, conn->name); @@ -424,19 +498,31 @@ void startNet2Net(char *name) { if (fp == NULL) { fprintf(stderr, "Could not find configuration file for connection '%s' at '%s'.\n", conn->name, configfile); - exit(2); + return 2; } fclose(fp); // Make sure all firewall rules are up to date. setFirewallRules(); + // Get the external IP address. + char address[STRING_SIZE] = ""; + int r = readExternalAddress(address); + if (r) { + fprintf(stderr, "Could not read the external address\n"); + exit(1); + } + char command[STRING_SIZE]; - sprintf(command, "/usr/sbin/openvpn --config %s", configfile); + snprintf(command, STRING_SIZE-1, "/sbin/modprobe tun"); executeCommand(command); + snprintf(command, STRING_SIZE-1, "/usr/sbin/openvpn --local %s --config %s", address, configfile); + executeCommand(command); + + return 0; } -void killNet2Net(char *name) { +int killNet2Net(char *name) { connection *conn = NULL; connection *conn_iter; @@ -452,7 +538,7 @@ void killNet2Net(char *name) { if (conn == NULL) { fprintf(stderr, "Connection not found.\n"); - exit(1); + return 1; } char pidfile[STRING_SIZE]; @@ -460,17 +546,64 @@ void killNet2Net(char *name) { int pid = readPidFile(pidfile); if (!pid > 0) { - exit(1); + fprintf(stderr, "Could not read pid file of connection %s.", conn->name); + return 1; } - fprintf(stderr, "Killing PID %d.\n", pid); + fprintf(stderr, "Killing connection %s (PID %d)...\n", conn->name, pid); kill(pid, SIGTERM); char command[STRING_SIZE]; snprintf(command, STRING_SIZE - 1, "/bin/rm -f %s", pidfile); executeCommand(command); - exit(0); + return 0; +} + +void startAllNet2Net() { + int exitcode = 0, _exitcode = 0; + + connection *conn = getConnections(); + + while(conn) { + /* Skip all connections that are not of type "net" or disabled. */ + if ((strcmp(conn->type, "net") != 0) || (strcmp(conn->status, "on") != 0)) { + conn = conn->next; + continue; + } + + _exitcode = startNet2Net(conn->name); + conn = conn->next; + + if (_exitcode > exitcode) { + exitcode = _exitcode; + } + } + + exit(exitcode); +} + +void killAllNet2Net() { + int exitcode = 0, _exitcode = 0; + + connection *conn = getConnections(); + + while(conn) { + /* Skip all connections that are not of type "net". */ + if (strcmp(conn->type, "net") != 0) { + conn = conn->next; + continue; + } + + _exitcode = killNet2Net(conn->name); + conn = conn->next; + + if (_exitcode > exitcode) { + exitcode = _exitcode; + } + } + + exit(exitcode); } void displayopenvpn(void) { @@ -487,6 +620,8 @@ int main(int argc, char *argv[]) { usage(); if(argc == 3) { + ovpnInit(); + if( (strcmp(argv[1], "-sn2n") == 0) || (strcmp(argv[1], "--start-net-2-net") == 0) ) { startNet2Net(argv[2]); return 0; @@ -508,40 +643,33 @@ int main(int argc, char *argv[]) { displayopenvpn(); return 0; } - else if( (strcmp(argv[1], "-dcr") == 0) || (strcmp(argv[1], "--delete-chains-and-rules") == 0) ) { - deleteAllChains(); - return 0; - } else { ovpnInit(); if( (strcmp(argv[1], "-s") == 0) || (strcmp(argv[1], "--start") == 0) ) { - deleteAllChains(); - createAllChains(); setFirewallRules(); startDaemon(); return 0; } + else if( (strcmp(argv[1], "-sn2n") == 0) || (strcmp(argv[1], "--start-net-2-net") == 0) ) { + startAllNet2Net(); + return 0; + } + else if( (strcmp(argv[1], "-kn2n") == 0) || (strcmp(argv[1], "--kill-net-2-net") == 0) ) { + killAllNet2Net(); + return 0; + } else if( (strcmp(argv[1], "-sdo") == 0) || (strcmp(argv[1], "--start-daemon-only") == 0) ) { startDaemon(); return 0; } else if( (strcmp(argv[1], "-r") == 0) || (strcmp(argv[1], "--restart") == 0) ) { stopDaemon(); - deleteAllChains(); - createAllChains(); setFirewallRules(); startDaemon(); return 0; } else if( (strcmp(argv[1], "-fwr") == 0) || (strcmp(argv[1], "--firewall-rules") == 0) ) { - deleteAllChains(); - createAllChains(); - setFirewallRules(); - return 0; - } - else if( (strcmp(argv[1], "-ccr") == 0) || (strcmp(argv[1], "--create-chains-and-rules") == 0) ) { - createAllChains(); setFirewallRules(); return 0; }