Add strongswan (4.3.6) for testing.
[people/pmueller/ipfire-2.x.git] / src / misc-progs / ipsecctrl.c
1 /*
2 *
3 * File originally from the Smoothwall project
4 * (c) 2001 Smoothwall Team
5 *
6 */
7
8 #include "libsmooth.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <signal.h>
16 #include "setuid.h"
17
18 /*
19 This module is responsible for start stop of the vpn system.
20
21 1) it allows AH & ESP to get in from interface where a vpn is mounted
22 The NAT traversal is used on the udp 4500 port.
23
24 2) it starts the ipsec daemon
25 The RED interface is a problem because it can be up or down a startup.
26 Then, the state change and it must not affect other VPN mounted on
27 other interface.
28 Unfortunatly, openswan 1 cannot do that correctly. It cannot use an
29 interface without restarting everything.
30
31 */
32
33 #define phystable "IPSECPHYSICAL"
34 #define virtualtable "IPSECVIRTUAL"
35
36 void usage() {
37 fprintf (stderr, "Usage:\n");
38 fprintf (stderr, "\tipsecctrl S [connectionkey]\n");
39 fprintf (stderr, "\tipsecctrl D [connectionkey]\n");
40 fprintf (stderr, "\tipsecctrl R\n");
41 fprintf (stderr, "\t\tS : Start/Restart Connection\n");
42 fprintf (stderr, "\t\tD : Stop Connection\n");
43 fprintf (stderr, "\t\tR : Reload Certificates and Secrets\n");
44 }
45
46 void load_modules() {
47 // safe_system("/sbin/modprobe ipsec");
48 }
49
50 /*
51 ACCEPT the ipsec protocol ah, esp & udp (for nat traversal) on the specified interface
52 */
53 void open_physical (char *interface, int nat_traversal_port) {
54 char str[STRING_SIZE];
55
56 // GRE ???
57 sprintf(str, "/sbin/iptables -A " phystable " -p 47 -i %s -j ACCEPT", interface);
58 // safe_system(str);
59 // ESP
60 sprintf(str, "/sbin/iptables -A " phystable " -p 50 -i %s -j ACCEPT", interface);
61 // safe_system(str);
62 // AH
63 sprintf(str, "/sbin/iptables -A " phystable " -p 51 -i %s -j ACCEPT", interface);
64 // safe_system(str);
65 // IKE
66 sprintf(str, "/sbin/iptables -A " phystable " -p udp -i %s --sport 500 --dport 500 -j ACCEPT", interface);
67 // safe_system(str);
68
69 if (! nat_traversal_port)
70 return;
71
72 sprintf(str, "/sbin/iptables -A " phystable " -p udp -i %s --dport %i -j ACCEPT", interface, nat_traversal_port);
73 // safe_system(str);
74 }
75
76 /*
77 Basic control for what can flow from/to ipsecX interfaces.
78
79 rc.firewall call this chain just before ACCEPTing everything
80 from green (-i DEV_GREEN -j ACCEPT).
81 */
82 void open_virtual (void) {
83 // allow anything from any ipsec to go on all interface, including other ipsec
84 // safe_system("/sbin/iptables -A " virtualtable " -i ipsec+ -j ACCEPT");
85 //todo: BOT extension?; allowing ipsec0<<==port-list-filter==>>GREEN ?
86 }
87
88 void ipsec_norules() {
89 /* clear input rules */
90 // safe_system("/sbin/iptables -F " phystable);
91 // safe_system("/sbin/iptables -F " virtualtable);
92
93 // unmap red alias ????
94 }
95
96
97 void add_alias_interfaces(char *configtype,
98 char *redtype,
99 char *redif,
100 int offset) //reserve room for ipsec0=red, ipsec1=green, ipsec2=orange,ipsec3=blue
101 {
102 FILE *file = NULL;
103 char s[STRING_SIZE];
104 int alias=0;
105
106 /* Check for CONFIG_TYPE=2 or 3 i.e. RED ethernet present. If not,
107 * exit gracefully. This is not an error... */
108 if (!((strcmp(configtype, "1")==0) || (strcmp(configtype, "2")==0) || (strcmp(configtype, "3")==0) || (strcmp(configtype, "4")==0)))
109 return;
110
111 /* Now check the RED_TYPE - aliases only work with STATIC. */
112 if (!(strcmp(redtype, "STATIC")==0))
113 return;
114
115 /* Now set up the new aliases from the config file */
116 if (!(file = fopen(CONFIG_ROOT "/ethernet/aliases", "r")))
117 {
118 fprintf(stderr, "Unable to open aliases configuration file\n");
119 return;
120 }
121 while (fgets(s, STRING_SIZE, file) != NULL && (offset+alias) < 16 )
122 {
123 if (s[strlen(s) - 1] == '\n')
124 s[strlen(s) - 1] = '\0';
125 int count = 0;
126 char *aliasip=NULL;
127 char *enabled=NULL;
128 char *comment=NULL;
129 char *sptr = strtok(s, ",");
130 while (sptr)
131 {
132 if (count == 0)
133 aliasip = sptr;
134 if (count == 1)
135 enabled = sptr;
136 else
137 comment = sptr;
138 count++;
139 sptr = strtok(NULL, ",");
140 }
141
142 if (!(aliasip && enabled))
143 continue;
144
145 if (!VALID_IP(aliasip))
146 {
147 fprintf(stderr, "Bad alias : %s\n", aliasip);
148 return;
149 }
150
151 if (strcmp(enabled, "on") == 0)
152 {
153 memset(s, 0, STRING_SIZE);
154 snprintf(s, STRING_SIZE-1, "/usr/sbin/ipsec tncfg --attach --virtual ipsec%d --physical %s:%d >/dev/null", offset+alias, redif, alias);
155 // safe_system(s);
156 alias++;
157 }
158 }
159 }
160
161 /*
162 return values from the vpn config file or false if not 'on'
163 */
164 int decode_line (char *s,
165 char **key,
166 char **name,
167 char **type,
168 char **interface
169 ) {
170 int count = 0;
171 *key = NULL;
172 *name = NULL;
173 *type = NULL;
174
175 if (s[strlen(s) - 1] == '\n')
176 s[strlen(s) - 1] = '\0';
177
178 char *result = strsep(&s, ",");
179 while (result) {
180 if (count == 0)
181 *key = result;
182 if ((count == 1) && strcmp(result, "on") != 0)
183 return 0; // a disabled line
184 if (count == 2)
185 *name = result;
186 if (count == 4)
187 *type = result;
188 if (count == 27)
189 *interface = result;
190 count++;
191 result = strsep(&s, ",");
192 }
193
194 // check other syntax
195 if (! *name)
196 return 0;
197
198 if (strspn(*name, LETTERS_NUMBERS) != strlen(*name)) {
199 fprintf(stderr, "Bad connection name: %s\n", *name);
200 return 0;
201 }
202
203 if (! (strcmp(*type, "host") == 0 || strcmp(*type, "net") == 0)) {
204 fprintf(stderr, "Bad connection type: %s\n", *type);
205 return 0;
206 }
207
208 if (! (strcmp(*interface, "RED") == 0 || strcmp(*interface, "GREEN") == 0 ||
209 strcmp(*interface, "ORANGE") == 0 || strcmp(*interface, "BLUE") == 0)) {
210 fprintf(stderr, "Bad interface name: %s\n", *interface);
211 return 0;
212 }
213 //it's a valid & active line
214 return 1;
215 }
216
217 /*
218 issue ipsec commmands to turn on connection 'name'
219 */
220 void turn_connection_on (char *name, char *type) {
221 char command[STRING_SIZE];
222
223 safe_system("/usr/sbin/ipsec auto --rereadsecrets >/dev/null");
224 memset(command, 0, STRING_SIZE);
225 snprintf(command, STRING_SIZE - 1,
226 "/usr/sbin/ipsec auto --replace %s >/dev/null", name);
227 safe_system(command);
228 if (strcmp(type, "net") == 0) {
229 memset(command, 0, STRING_SIZE);
230 snprintf(command, STRING_SIZE - 1,
231 "/usr/sbin/ipsec auto --asynchronous --up %s >/dev/null", name);
232 safe_system(command);
233 }
234 }
235 /*
236 issue ipsec commmands to turn off connection 'name'
237 */
238 void turn_connection_off (char *name) {
239 char command[STRING_SIZE];
240
241 memset(command, 0, STRING_SIZE);
242 snprintf(command, STRING_SIZE - 1,
243 "/usr/sbin/ipsec auto --down %s >/dev/null", name);
244 safe_system(command);
245 memset(command, 0, STRING_SIZE);
246 snprintf(command, STRING_SIZE - 1,
247 "/usr/sbin/ipsec auto --delete %s >/dev/null", name);
248 safe_system(command);
249 safe_system("/usr/sbin/ipsec auto --rereadsecrets >/dev/null");
250 }
251
252
253 int main(int argc, char *argv[]) {
254
255 char configtype[STRING_SIZE];
256 char redtype[STRING_SIZE] = "";
257 struct keyvalue *kv = NULL;
258
259 if (argc < 2) {
260 usage();
261 exit(1);
262 }
263 if (!(initsetuid()))
264 exit(1);
265
266 FILE *file = NULL;
267
268 /* Get vpnwatch pid */
269
270 if ( (argc == 2) && (file = fopen("/var/run/vpn-watch.pid", "r"))) {
271 safe_system("kill -9 $(cat /var/run/vpn-watch.pid)");
272 safe_system("unlink /var/run/vpn-watch.pid");
273 close(file);
274 }
275
276 /* FIXME: workaround for pclose() issue - still no real idea why
277 * this is happening */
278 signal(SIGCHLD, SIG_DFL);
279
280 /* handle operations that doesn't need start the ipsec system */
281 if (argc == 2) {
282 if (strcmp(argv[1], "D") == 0) {
283 ipsec_norules();
284 /* Only shutdown pluto if it really is running */
285 /* Get pluto pid */
286 if (file = fopen("/var/run/pluto.pid", "r")) {
287 safe_system("/etc/rc.d/init.d/ipsec stop 2> /dev/null >/dev/null");
288 close(file);
289 }
290 exit(0);
291 }
292
293 if (strcmp(argv[1], "R") == 0) {
294 safe_system("/usr/sbin/ipsec auto --rereadall");
295 exit(0);
296 }
297 }
298
299 /* clear iptables vpn rules */
300 ipsec_norules();
301
302 /* read vpn config */
303 kv=initkeyvalues();
304 if (!readkeyvalues(kv, CONFIG_ROOT "/vpn/settings"))
305 {
306 fprintf(stderr, "Cannot read vpn settings\n");
307 exit(1);
308 }
309
310 /* check is the vpn system is enabled */
311 {
312 char s[STRING_SIZE];
313 findkey(kv, "ENABLED", s);
314 freekeyvalues(kv);
315 if (strcmp (s, "on") != 0)
316 exit(0);
317 }
318
319 /* read interface settings */
320 kv=initkeyvalues();
321 if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings"))
322 {
323 fprintf(stderr, "Cannot read ethernet settings\n");
324 exit(1);
325 }
326 if (!findkey(kv, "CONFIG_TYPE", configtype))
327 {
328 fprintf(stderr, "Cannot read CONFIG_TYPE\n");
329 exit(1);
330 }
331 findkey(kv, "RED_TYPE", redtype);
332
333
334 /* Loop through the config file to find physical interface that will accept IPSEC */
335 int enable_red=0; // states 0: not used
336 int enable_green=0; // 1: error condition
337 int enable_orange=0; // 2: good
338 int enable_blue=0;
339 char if_red[STRING_SIZE] = "";
340 char if_green[STRING_SIZE] = "";
341 char if_orange[STRING_SIZE] = "";
342 char if_blue[STRING_SIZE] = "";
343 char s[STRING_SIZE];
344
345 if (!(file = fopen(CONFIG_ROOT "/vpn/config", "r"))) {
346 fprintf(stderr, "Couldn't open vpn settings file");
347 exit(1);
348 }
349 while (fgets(s, STRING_SIZE, file) != NULL) {
350 char *key;
351 char *name;
352 char *type;
353 char *interface;
354 if (!decode_line(s,&key,&name,&type,&interface))
355 continue;
356 /* search interface */
357 if (!enable_red && strcmp (interface, "RED") == 0) {
358 // when RED is up, find interface name in special file
359 FILE *ifacefile = NULL;
360 if ((ifacefile = fopen(CONFIG_ROOT "/red/iface", "r"))) {
361 if (fgets(if_red, STRING_SIZE, ifacefile)) {
362 if (if_red[strlen(if_red) - 1] == '\n')
363 if_red[strlen(if_red) - 1] = '\0';
364 }
365 fclose (ifacefile);
366
367 if (VALID_DEVICE(if_red))
368 enable_red+=2; // present and running
369 }
370 }
371
372 if (!enable_green && strcmp (interface, "GREEN") == 0) {
373 enable_green = 1;
374 findkey(kv, "GREEN_DEV", if_green);
375 if (VALID_DEVICE(if_green))
376 enable_green++;
377 else
378 fprintf(stderr, "IPSec enabled on green but green interface is invalid or not found\n");
379 }
380
381 if (!enable_orange && strcmp (interface, "ORANGE") == 0) {
382 enable_orange = 1;
383 findkey(kv, "ORANGE_DEV", if_orange);
384 if (VALID_DEVICE(if_orange))
385 enable_orange++;
386 else
387 fprintf(stderr, "IPSec enabled on orange but orange interface is invalid or not found\n");
388 }
389
390 if (!enable_blue && strcmp (interface, "BLUE") == 0) {
391 enable_blue++;
392 findkey(kv, "BLUE_DEV", if_blue);
393 if (VALID_DEVICE(if_blue))
394 enable_blue++;
395 else
396 fprintf(stderr, "IPSec enabled on blue but blue interface is invalid or not found\n");
397
398 }
399 }
400 fclose(file);
401 freekeyvalues(kv);
402
403 // do nothing if something is in error condition
404 if ((enable_red==1) || (enable_green==1) || (enable_orange==1) || (enable_blue==1) )
405 exit(1);
406
407 // exit if nothing to do
408 if ( (enable_red+enable_green+enable_orange+enable_blue) == 0 )
409 exit(0);
410
411 // open needed ports
412 // todo: read a nat_t indicator to allow or not openning UDP/4500
413 if (enable_red==2)
414 open_physical(if_red, 4500);
415
416 if (enable_green==2)
417 open_physical(if_green, 4500);
418
419 if (enable_orange==2)
420 open_physical(if_orange, 4500);
421
422 if (enable_blue==2)
423 open_physical(if_blue, 4500);
424
425 // then open the ipsecX
426 open_virtual();
427
428 // start the system
429 if ((argc == 2) && strcmp(argv[1], "S") == 0) {
430 load_modules();
431 safe_system("/usr/sbin/ipsec tncfg --clear >/dev/null");
432 safe_system("/etc/rc.d/init.d/ipsec restart >/dev/null");
433 add_alias_interfaces(configtype, redtype, if_red, (enable_red+enable_green+enable_orange+enable_blue) >>1 );
434 safe_system("/usr/local/bin/vpn-watch &");
435 exit(0);
436 }
437
438 // it is a selective start or stop
439 // second param is only a number 'key'
440 if ((argc == 2) || strspn(argv[2], NUMBERS) != strlen(argv[2])) {
441 ipsec_norules();
442 fprintf(stderr, "Bad arg\n");
443 usage();
444 exit(1);
445 }
446
447 // search the vpn pointed by 'key'
448 if (!(file = fopen(CONFIG_ROOT "/vpn/config", "r"))) {
449 ipsec_norules();
450 fprintf(stderr, "Couldn't open vpn settings file");
451 exit(1);
452 }
453 while (fgets(s, STRING_SIZE, file) != NULL) {
454 char *key;
455 char *name;
456 char *type;
457 char *interface;
458 if (!decode_line(s,&key,&name,&type,&interface))
459 continue;
460
461 // start/stop a vpn if belonging to specified interface
462 if (strcmp(argv[1], interface) == 0 ) {
463 if (strcmp(argv[2], "0")==0)
464 turn_connection_off (name);
465 else
466 turn_connection_on (name, type);
467 continue;
468 }
469 // is it the 'key' requested ?
470 if (strcmp(argv[2], key) != 0)
471 continue;
472 // Start or Delete this Connection
473 if (strcmp(argv[1], "S") == 0)
474 turn_connection_on (name, type);
475 else
476 if (strcmp(argv[1], "D") == 0)
477 turn_connection_off (name);
478 else {
479 ipsec_norules();
480 fprintf(stderr, "Bad command\n");
481 exit(1);
482 }
483 }
484 fclose(file);
485 return 0;
486 }