X-Git-Url: http://git.ipfire.org/?p=people%2Fpmueller%2Fipfire-2.x.git;a=blobdiff_plain;f=src%2Fmisc-progs%2Fipsecctrl.c;h=aa0f41c4d756edb56232c7ccaf99d66c7f7fc317;hp=408dfad9076be5814a00f96eb507fb9272764fab;hb=64dc6c92f1b61b54744812c7de7a324ff648cdf9;hpb=05207d6927b62a9e4f63b3560719a5bd4cca0e81 diff --git a/src/misc-progs/ipsecctrl.c b/src/misc-progs/ipsecctrl.c index 408dfad907..aa0f41c4d7 100644 --- a/src/misc-progs/ipsecctrl.c +++ b/src/misc-progs/ipsecctrl.c @@ -3,8 +3,6 @@ * File originally from the Smoothwall project * (c) 2001 Smoothwall Team * - * $Id: ipsecctrl.c,v 1.5.2.14 2005/05/15 12:58:28 rkerr Exp $ - * */ #include "libsmooth.h" @@ -17,309 +15,379 @@ #include #include "setuid.h" +/* + This module is responsible for start stop of the vpn system. + + 1) it allows AH & ESP to get in from interface where a vpn is mounted + The NAT traversal is used on the udp 4500 port. + + 2) it starts the ipsec daemon + The RED interface is a problem because it can be up or down a startup. + Then, the state change and it must not affect other VPN mounted on + other interface. + Unfortunatly, openswan 1 cannot do that correctly. It cannot use an + interface without restarting everything. + +*/ + void usage() { - fprintf (stderr, "Usage:\n"); - fprintf (stderr, "\tipsecctrl S [connectionkey]\n"); - fprintf (stderr, "\tipsecctrl D [connectionkey]\n"); - fprintf (stderr, "\tipsecctrl R\n"); - fprintf (stderr, "\t\tS : Start/Restart Connection\n"); - fprintf (stderr, "\t\tD : Stop Connection\n"); - fprintf (stderr, "\t\tR : Reload Certificates and Secrets\n"); + fprintf (stderr, "Usage:\n"); + fprintf (stderr, "\tipsecctrl S [connectionkey]\n"); + fprintf (stderr, "\tipsecctrl D [connectionkey]\n"); + fprintf (stderr, "\tipsecctrl R\n"); + fprintf (stderr, "\tipsecctrl I\n"); + fprintf (stderr, "\t\tS : Start/Restart Connection\n"); + fprintf (stderr, "\t\tD : Stop Connection\n"); + fprintf (stderr, "\t\tR : Reload Certificates and Secrets\n"); + fprintf (stderr, "\t\tI : Print Statusinfo\n"); } -void loadalgmodules() { - safe_system("/sbin/modprobe ipsec"); +/* + ACCEPT the ipsec protocol ah, esp & udp (for nat traversal) on the specified interface +*/ +void open_physical (char *interface, int nat_traversal_port) { + char str[STRING_SIZE]; + + // GRE ??? +// sprintf(str, "/sbin/iptables -A " phystable " -p 47 -i %s -j ACCEPT", interface); +// safe_system(str); + // ESP +// sprintf(str, "/sbin/iptables -A " phystable " -p 50 -i %s -j ACCEPT", interface); +// safe_system(str); + // AH +// sprintf(str, "/sbin/iptables -A " phystable " -p 51 -i %s -j ACCEPT", interface); +// safe_system(str); + // IKE + sprintf(str, "/sbin/iptables -A IPSECINPUT -p udp -i %s --sport 500 --dport 500 -j ACCEPT", interface); + safe_system(str); + + if (! nat_traversal_port) + return; + + sprintf(str, "/sbin/iptables -A IPSECINPUT -p udp -i %s --dport %i -j ACCEPT", interface, nat_traversal_port); + safe_system(str); +} + +void ipsec_norules() { + /* clear input rules */ + safe_system("/sbin/iptables -F IPSECINPUT"); + safe_system("/sbin/iptables -F IPSECFORWARD"); + safe_system("/sbin/iptables -F IPSECOUTPUT"); + } -void ipsecrules(char *chain, char *interface) -{ - char str[STRING_SIZE]; - - sprintf(str, "/sbin/iptables -A %s -p 47 -i %s -j ACCEPT", chain, interface); - safe_system(str); - sprintf(str, "/sbin/iptables -A %s -p 50 -i %s -j ACCEPT", chain, interface); - safe_system(str); - sprintf(str, "/sbin/iptables -A %s -p 51 -i %s -j ACCEPT", chain, interface); - safe_system(str); - sprintf(str, "/sbin/iptables -A %s -p udp -i %s --sport 500 --dport 500 -j ACCEPT", chain, interface); - safe_system(str); - sprintf(str, "/sbin/iptables -A %s -p udp -i %s --dport 4500 -j ACCEPT", chain, interface); - safe_system(str); +/* + return values from the vpn config file or false if not 'on' +*/ +int decode_line (char *s, + char **key, + char **name, + char **type, + char **interface + ) { + int count = 0; + *key = NULL; + *name = NULL; + *type = NULL; + + if (s[strlen(s) - 1] == '\n') + s[strlen(s) - 1] = '\0'; + + char *result = strsep(&s, ","); + while (result) { + if (count == 0) + *key = result; + if ((count == 1) && strcmp(result, "on") != 0) + return 0; // a disabled line + if (count == 2) + *name = result; + if (count == 4) + *type = result; + if (count == 27) + *interface = result; + count++; + result = strsep(&s, ","); + } + + // check other syntax + if (! *name) + return 0; + + if (strspn(*name, LETTERS_NUMBERS) != strlen(*name)) { + fprintf(stderr, "Bad connection name: %s\n", *name); + return 0; + } + + if (! (strcmp(*type, "host") == 0 || strcmp(*type, "net") == 0)) { + fprintf(stderr, "Bad connection type: %s\n", *type); + return 0; + } + + if (! (strcmp(*interface, "RED") == 0 || strcmp(*interface, "GREEN") == 0 || + strcmp(*interface, "ORANGE") == 0 || strcmp(*interface, "BLUE") == 0)) { + fprintf(stderr, "Bad interface name: %s\n", *interface); + return 0; + } + //it's a valid & active line + return 1; } -void addaliasinterfaces(char *configtype, char *redtype, char *redif, char *enablered, char*enableblue) -{ - FILE *file = NULL; - char s[STRING_SIZE]; - char *sptr; - char *aliasip=NULL; - char *enabled=NULL; - char *comment=NULL; - int count=0; - int alias=0; - int add=0; - - if ( strcmp(enablered, "on") == 0 ) - add += 1; - if ( strcmp(enableblue, "on") == 0 ) - add += 1; - - /* Check for CONFIG_TYPE=2 or 3 i.e. RED ethernet present. If not, - * exit gracefully. This is not an error... */ - if (!((strcmp(configtype, "2")==0) || (strcmp(configtype, "3")==0) || (strcmp(configtype, "6")==0) || (strcmp(configtype, "7")==0))) - return; - - /* Now check the RED_TYPE - aliases only work with STATIC. */ - if (!(strcmp(redtype, "STATIC")==0)) - return; - - /* Now set up the new aliases from the config file */ - if (!(file = fopen(CONFIG_ROOT "/ethernet/aliases", "r"))) - { - fprintf(stderr, "Unable to open aliases configuration file\n"); - return; - } - - while (fgets(s, STRING_SIZE, file) != NULL && (add+alias) < 16) - { - if (s[strlen(s) - 1] == '\n') - s[strlen(s) - 1] = '\0'; - sptr = strtok(s, ","); - count = 0; - aliasip = NULL; - enabled = NULL; - comment = NULL; - while (sptr) - { - if (count == 0) - aliasip = sptr; - if (count == 1) - enabled = sptr; - else - comment = sptr; - count++; - sptr = strtok(NULL, ","); - } - - if (!(aliasip && enabled)) - continue; - - if (!VALID_IP(aliasip)) - { - fprintf(stderr, "Bad alias : %s\n", aliasip); - return; - } - - if (strcmp(enabled, "on") == 0) - { - memset(s, 0, STRING_SIZE); - snprintf(s, STRING_SIZE-1, "/usr/sbin/ipsec tncfg --attach --virtual ipsec%d --physical %s:%d >/dev/null", alias+add, redif, alias); - safe_system(s); - alias++; - } - } +/* + issue ipsec commmands to turn on connection 'name' +*/ +void turn_connection_on (char *name, char *type) { + char command[STRING_SIZE]; + + safe_system("/usr/sbin/ipsec reload >/dev/null"); + memset(command, 0, STRING_SIZE); + snprintf(command, STRING_SIZE - 1, + "/usr/sbin/ipsec up %s >/dev/null", name); + safe_system(command); } +/* + issue ipsec commmands to turn off connection 'name' +*/ +void turn_connection_off (char *name) { + char command[STRING_SIZE]; + memset(command, 0, STRING_SIZE); + snprintf(command, STRING_SIZE - 1, + "/usr/sbin/ipsec whack --delete --name %s >/dev/null", name); + safe_system(command); + safe_system("/usr/sbin/ipsec whack --rereadall >/dev/null"); +} + int main(int argc, char *argv[]) { - int count; - char s[STRING_SIZE]; - char configtype[STRING_SIZE]; - char redtype[STRING_SIZE] = ""; - char command[STRING_SIZE]; - char *result; - char *key; - char *enabled; - char *name; - char *type; - char *running; - FILE *file = NULL; - struct keyvalue *kv = NULL; - char enablered[STRING_SIZE] = "off"; - char enableblue[STRING_SIZE] = "off"; - char redif[STRING_SIZE] = "";; - char blueif[STRING_SIZE] = ""; - FILE *ifacefile = NULL; - - if (!(initsetuid())) - exit(1); - - if (argc < 2) { - usage(); - exit(1); - } - - /* FIXME: workaround for pclose() issue - still no real idea why - * this is happening */ - signal(SIGCHLD, SIG_DFL); - - /* Init the keyvalue structure */ - kv=initkeyvalues(); - - /* Read in the current values */ - if (!readkeyvalues(kv, CONFIG_ROOT "/vpn/settings")) - { - fprintf(stderr, "Cannot read vpn settings\n"); - exit(1); - } - - findkey(kv, "ENABLED", enablered); - findkey(kv, "ENABLED_BLUE", enableblue); - - freekeyvalues(kv); - kv=initkeyvalues(); - - if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) - { - fprintf(stderr, "Cannot read ethernet settings\n"); - exit(1); - } - - if (!findkey(kv, "CONFIG_TYPE", configtype)) - { - fprintf(stderr, "Cannot read CONFIG_TYPE\n"); - exit(1); - } - - findkey(kv, "RED_TYPE", redtype); - findkey(kv, "BLUE_DEV", blueif); - freekeyvalues(kv); - memset(redif, 0, STRING_SIZE); - - if ((ifacefile = fopen(CONFIG_ROOT "/red/iface", "r"))) - { - if (fgets(redif, STRING_SIZE, ifacefile)) - { - if (redif[strlen(redif) - 1] == '\n') - redif[strlen(redif) - 1] = '\0'; - } - fclose (ifacefile); - ifacefile = NULL; - - if (!VALID_DEVICE(redif)) - { - memset(redif, 0, STRING_SIZE); - } - } - - safe_system("/sbin/iptables -F IPSECRED"); - if (!strcmp(enablered, "on") && strlen(redif)) { - ipsecrules("IPSECRED", redif); - } - - safe_system("/sbin/iptables -F IPSECBLUE"); - if (!strcmp(enableblue, "on")) { - if (VALID_DEVICE(blueif)) - ipsecrules("IPSECBLUE", blueif); - else - { - fprintf(stderr, "IPSec enabled on blue but blue interface is invalid or not found\n"); - exit(1); - } - } - - /* Only shutdown pluto if it really is running */ - if (argc == 2) { - if (strcmp(argv[1], "D") == 0) { - int fd; - /* Get pluto pid */ - if ((fd = open("/var/run/pluto.pid", O_RDONLY)) != -1) { - safe_system("/etc/rc.d/init.d/ipsec stop 2> /dev/null >/dev/null"); - close(fd); - } - } - } - - if ((strcmp(enablered, "on") || !strlen(redif)) && strcmp(enableblue, "on")) - exit(0); - - if (argc == 2) { - if (strcmp(argv[1], "S") == 0) { - loadalgmodules(); - safe_system("/usr/sbin/ipsec tncfg --clear >/dev/null"); - safe_system("/etc/rc.d/init.d/ipsec restart >/dev/null"); - addaliasinterfaces(configtype, redtype, redif, enablered, enableblue); - } else if (strcmp(argv[1], "R") == 0) { - safe_system("/usr/sbin/ipsec auto --rereadall"); - } else { - fprintf(stderr, "Bad arg\n"); - usage(); - exit(1); - } - } else if (strspn(argv[2], NUMBERS) == strlen(argv[2])) { - if (!(file = fopen(CONFIG_ROOT "/vpn/config", "r"))) { - fprintf(stderr, "Couldn't open vpn settings file"); - exit(1); - } - while (fgets(s, STRING_SIZE, file) != NULL) { - if (s[strlen(s) - 1] == '\n') - s[strlen(s) - 1] = '\0'; - running = strdup (s); - result = strsep(&running, ","); - count = 0; - key = NULL; - name = NULL; - enabled = NULL; - type = NULL; - while (result) { - if (count == 0) - key = result; - if (count == 1) - enabled = result; - if (count == 2) - name = result; - if (count == 4) - type = result; - count++; - result = strsep(&running, ","); - } - if (strcmp(key, argv[2]) != 0) - continue; - - if (!(name && enabled)) - continue; - - if (strspn(name, LETTERS_NUMBERS) != strlen(name)) { - fprintf(stderr, "Bad connection name: %s\n", name); - goto EXIT; - } - - if (! (strcmp(type, "host") == 0 || strcmp(type, "net") == 0)) { - fprintf(stderr, "Bad connection type: %s\n", type); - goto EXIT; - } - - if (strcmp(argv[1], "S") == 0 && strcmp(enabled, "on") == 0) { - safe_system("/usr/sbin/ipsec auto --rereadsecrets >/dev/null"); - memset(command, 0, STRING_SIZE); - snprintf(command, STRING_SIZE - 1, - "/usr/sbin/ipsec auto --replace %s >/dev/null", name); - safe_system(command); - if (strcmp(type, "net") == 0) { - memset(command, 0, STRING_SIZE); - snprintf(command, STRING_SIZE - 1, - "/usr/sbin/ipsec auto --asynchronous --up %s >/dev/null", name); - safe_system(command); - } - } else if (strcmp(argv[1], "D") == 0) { - safe_system("/usr/sbin/ipsec auto --rereadsecrets >/dev/null"); - memset(command, 0, STRING_SIZE); - snprintf(command, STRING_SIZE - 1, - "/usr/sbin/ipsec auto --down %s >/dev/null", name); - safe_system(command); - memset(command, 0, STRING_SIZE); - snprintf(command, STRING_SIZE - 1, - "/usr/sbin/ipsec auto --delete %s >/dev/null", name); - safe_system(command); - } - } - } else { - fprintf(stderr, "Bad arg\n"); - usage(); - exit(1); - } - -EXIT: - if (file) - fclose(file); - return 0; + + char configtype[STRING_SIZE]; + char redtype[STRING_SIZE] = ""; + struct keyvalue *kv = NULL; + + if (argc < 2) { + usage(); + exit(1); + } + if (!(initsetuid())) + exit(1); + + FILE *file = NULL; + + /* Get vpnwatch pid */ + + if ( (argc == 2) && (file = fopen("/var/run/vpn-watch.pid", "r"))) { + safe_system("kill -9 $(cat /var/run/vpn-watch.pid)"); + safe_system("unlink /var/run/vpn-watch.pid"); + close(file); + } + + /* FIXME: workaround for pclose() issue - still no real idea why + * this is happening */ + signal(SIGCHLD, SIG_DFL); + + /* handle operations that doesn't need start the ipsec system */ + if (argc == 2) { + if (strcmp(argv[1], "D") == 0) { + ipsec_norules(); + /* Only shutdown pluto if it really is running */ + /* Get pluto pid */ + if (file = fopen("/var/run/pluto.pid", "r")) { + safe_system("/etc/rc.d/init.d/ipsec stop 2> /dev/null >/dev/null"); + close(file); + } + exit(0); + } + + if (strcmp(argv[1], "R") == 0) { + safe_system("/usr/sbin/ipsec whack --rereadall >/dev/null"); + exit(0); + } + + if (strcmp(argv[1], "I") == 0) { + safe_system("/usr/sbin/ipsec whack --status"); + exit(0); + } + + } + + /* clear iptables vpn rules */ + ipsec_norules(); + + /* read vpn config */ + kv=initkeyvalues(); + if (!readkeyvalues(kv, CONFIG_ROOT "/vpn/settings")) + { + fprintf(stderr, "Cannot read vpn settings\n"); + exit(1); + } + + /* check is the vpn system is enabled */ + { + char s[STRING_SIZE]; + findkey(kv, "ENABLED", s); + freekeyvalues(kv); + if (strcmp (s, "on") != 0) + exit(0); + } + + /* read interface settings */ + kv=initkeyvalues(); + if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) + { + fprintf(stderr, "Cannot read ethernet settings\n"); + exit(1); + } + if (!findkey(kv, "CONFIG_TYPE", configtype)) + { + fprintf(stderr, "Cannot read CONFIG_TYPE\n"); + exit(1); + } + findkey(kv, "RED_TYPE", redtype); + + + /* Loop through the config file to find physical interface that will accept IPSEC */ + int enable_red=0; // states 0: not used + int enable_green=0; // 1: error condition + int enable_orange=0; // 2: good + int enable_blue=0; + char if_red[STRING_SIZE] = ""; + char if_green[STRING_SIZE] = ""; + char if_orange[STRING_SIZE] = ""; + char if_blue[STRING_SIZE] = ""; + char s[STRING_SIZE]; + + if (!(file = fopen(CONFIG_ROOT "/vpn/config", "r"))) { + fprintf(stderr, "Couldn't open vpn settings file"); + exit(1); + } + while (fgets(s, STRING_SIZE, file) != NULL) { + char *key; + char *name; + char *type; + char *interface; + if (!decode_line(s,&key,&name,&type,&interface)) + continue; + /* search interface */ + if (!enable_red && strcmp (interface, "RED") == 0) { + // when RED is up, find interface name in special file + FILE *ifacefile = NULL; + if ((ifacefile = fopen(CONFIG_ROOT "/red/iface", "r"))) { + if (fgets(if_red, STRING_SIZE, ifacefile)) { + if (if_red[strlen(if_red) - 1] == '\n') + if_red[strlen(if_red) - 1] = '\0'; + } + fclose (ifacefile); + + if (VALID_DEVICE(if_red)) + enable_red+=2; // present and running + } + } + + if (!enable_green && strcmp (interface, "GREEN") == 0) { + enable_green = 1; + findkey(kv, "GREEN_DEV", if_green); + if (VALID_DEVICE(if_green)) + enable_green++; + else + fprintf(stderr, "IPSec enabled on green but green interface is invalid or not found\n"); + } + + if (!enable_orange && strcmp (interface, "ORANGE") == 0) { + enable_orange = 1; + findkey(kv, "ORANGE_DEV", if_orange); + if (VALID_DEVICE(if_orange)) + enable_orange++; + else + fprintf(stderr, "IPSec enabled on orange but orange interface is invalid or not found\n"); + } + + if (!enable_blue && strcmp (interface, "BLUE") == 0) { + enable_blue++; + findkey(kv, "BLUE_DEV", if_blue); + if (VALID_DEVICE(if_blue)) + enable_blue++; + else + fprintf(stderr, "IPSec enabled on blue but blue interface is invalid or not found\n"); + + } + } + fclose(file); + freekeyvalues(kv); + + // do nothing if something is in error condition + if ((enable_red==1) || (enable_green==1) || (enable_orange==1) || (enable_blue==1) ) + exit(1); + + // exit if nothing to do + if ( (enable_red+enable_green+enable_orange+enable_blue) == 0 ) + exit(0); + + // open needed ports + // todo: read a nat_t indicator to allow or not openning UDP/4500 + if (enable_red==2) + open_physical(if_red, 4500); + + if (enable_green==2) + open_physical(if_green, 4500); + + if (enable_orange==2) + open_physical(if_orange, 4500); + + if (enable_blue==2) + open_physical(if_blue, 4500); + + // start the system + if ((argc == 2) && strcmp(argv[1], "S") == 0) { + safe_system("/etc/rc.d/init.d/ipsec restart >/dev/null"); + safe_system("/usr/local/bin/vpn-watch &"); + exit(0); + } + + // it is a selective start or stop + // second param is only a number 'key' + if ((argc == 2) || strspn(argv[2], NUMBERS) != strlen(argv[2])) { + ipsec_norules(); + fprintf(stderr, "Bad arg\n"); + usage(); + exit(1); + } + + // search the vpn pointed by 'key' + if (!(file = fopen(CONFIG_ROOT "/vpn/config", "r"))) { + ipsec_norules(); + fprintf(stderr, "Couldn't open vpn settings file"); + exit(1); + } + while (fgets(s, STRING_SIZE, file) != NULL) { + char *key; + char *name; + char *type; + char *interface; + if (!decode_line(s,&key,&name,&type,&interface)) + continue; + + // start/stop a vpn if belonging to specified interface + if (strcmp(argv[1], interface) == 0 ) { + if (strcmp(argv[2], "0")==0) + turn_connection_off (name); + else + turn_connection_on (name, type); + continue; + } + // is it the 'key' requested ? + if (strcmp(argv[2], key) != 0) + continue; + // Start or Delete this Connection + if (strcmp(argv[1], "S") == 0) + turn_connection_on (name, type); + else + if (strcmp(argv[1], "D") == 0) + turn_connection_off (name); + else { + ipsec_norules(); + fprintf(stderr, "Bad command\n"); + exit(1); + } + } + fclose(file); + return 0; }