commit und geh weg
[people/pmueller/ipfire-2.x.git] / src / misc-progs / ipsecctrl.c
CommitLineData
05207d69
MT
1/*
2 *
3 * File originally from the Smoothwall project
4 * (c) 2001 Smoothwall Team
5 *
05207d69
MT
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
5fd30232
MT
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
69dcc425 22 The NAT traversal is used on the udp 4500 port.
5fd30232
MT
23
24 2) it starts the ipsec daemon
69dcc425
CS
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.
5fd30232
MT
30
31*/
32
69dcc425
CS
33#define phystable "IPSECPHYSICAL"
34#define virtualtable "IPSECVIRTUAL"
5fd30232 35
05207d69 36void usage() {
69dcc425
CS
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");
05207d69
MT
44}
45
5fd30232 46void load_modules() {
69dcc425 47 safe_system("/sbin/modprobe ipsec");
05207d69
MT
48}
49
5fd30232 50/*
69dcc425 51 ACCEPT the ipsec protocol ah, esp & udp (for nat traversal) on the specified interface
5fd30232
MT
52*/
53void open_physical (char *interface, int nat_traversal_port) {
69dcc425
CS
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);
05207d69
MT
74}
75
5fd30232
MT
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*/
82void open_virtual (void) {
69dcc425
CS
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 ?
5fd30232
MT
86}
87
88void ipsec_norules() {
69dcc425
CS
89 /* clear input rules */
90 safe_system("/sbin/iptables -F " phystable);
91 safe_system("/sbin/iptables -F " virtualtable);
5fd30232 92
69dcc425 93 // unmap red alias ????
5fd30232
MT
94}
95
96
97void add_alias_interfaces(char *configtype,
69dcc425
CS
98 char *redtype,
99 char *redif,
100 int offset) //reserve room for ipsec0=red, ipsec1=green, ipsec2=orange,ipsec3=blue
05207d69 101{
69dcc425
CS
102 FILE *file = NULL;
103 char s[STRING_SIZE];
104 int alias=0;
05207d69 105
69dcc425
CS
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;
05207d69
MT
110
111 /* Now check the RED_TYPE - aliases only work with STATIC. */
69dcc425
CS
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 }
05207d69
MT
159}
160
5fd30232
MT
161/*
162 return values from the vpn config file or false if not 'on'
163*/
164int decode_line (char *s,
69dcc425
CS
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;
5fd30232
MT
215}
216
217/*
218 issue ipsec commmands to turn on connection 'name'
219*/
220void turn_connection_on (char *name, char *type) {
69dcc425
CS
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 }
5fd30232
MT
234}
235/*
236 issue ipsec commmands to turn off connection 'name'
237*/
238void turn_connection_off (char *name) {
69dcc425
CS
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");
5fd30232
MT
250}
251
252
05207d69 253int main(int argc, char *argv[]) {
5fd30232 254
69dcc425
CS
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
fe6cda92 266 FILE *file = NULL;
69dcc425 267
dced81b2 268 /* Get vpnwatch pid */
fe6cda92
CS
269
270 if (file = fopen("/var/run/vpn-watch.pid", "r")) {
7dbf47dc 271 safe_system("kill -9 $(cat /var/run/vpn-watch.pid)");
dced81b2
CS
272 safe_system("unlink /var/run/vpn-watch.pid)");
273 }
7dbf47dc 274 close(file);
fe6cda92 275
69dcc425
CS
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 exit(0);
435 }
436
437 // it is a selective start or stop
438 // second param is only a number 'key'
439 if ((argc == 2) || strspn(argv[2], NUMBERS) != strlen(argv[2])) {
440 ipsec_norules();
441 fprintf(stderr, "Bad arg\n");
442 usage();
443 exit(1);
444 }
445
446 // search the vpn pointed by 'key'
447 if (!(file = fopen(CONFIG_ROOT "/vpn/config", "r"))) {
448 ipsec_norules();
449 fprintf(stderr, "Couldn't open vpn settings file");
450 exit(1);
451 }
452 while (fgets(s, STRING_SIZE, file) != NULL) {
453 char *key;
454 char *name;
455 char *type;
456 char *interface;
457 if (!decode_line(s,&key,&name,&type,&interface))
458 continue;
459
460 // start/stop a vpn if belonging to specified interface
461 if (strcmp(argv[1], interface) == 0 ) {
462 if (strcmp(argv[2], "0")==0)
463 turn_connection_off (name);
464 else
465 turn_connection_on (name, type);
466 continue;
467 }
468 // is it the 'key' requested ?
469 if (strcmp(argv[2], key) != 0)
470 continue;
471 // Start or Delete this Connection
472 if (strcmp(argv[1], "S") == 0)
473 turn_connection_on (name, type);
474 else
475 if (strcmp(argv[1], "D") == 0)
476 turn_connection_off (name);
477 else {
478 ipsec_norules();
479 fprintf(stderr, "Bad command\n");
480 exit(1);
481 }
482 }
483 fclose(file);
484 safe_system("/usr/local/bin/vpn-watch &");
485 return 0;
05207d69 486}