ipsecctrl: Use --wait switch for all iptables commands
[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>
52e54c1c 16
05207d69 17#include "setuid.h"
52e54c1c 18#include "netutil.h"
05207d69 19
5fd30232
MT
20/*
21 This module is responsible for start stop of the vpn system.
22
23 1) it allows AH & ESP to get in from interface where a vpn is mounted
69dcc425 24 The NAT traversal is used on the udp 4500 port.
5fd30232
MT
25
26 2) it starts the ipsec daemon
69dcc425
CS
27 The RED interface is a problem because it can be up or down a startup.
28 Then, the state change and it must not affect other VPN mounted on
29 other interface.
30 Unfortunatly, openswan 1 cannot do that correctly. It cannot use an
31 interface without restarting everything.
5fd30232
MT
32
33*/
34
05207d69 35void usage() {
69dcc425
CS
36 fprintf (stderr, "Usage:\n");
37 fprintf (stderr, "\tipsecctrl S [connectionkey]\n");
38 fprintf (stderr, "\tipsecctrl D [connectionkey]\n");
39 fprintf (stderr, "\tipsecctrl R\n");
db073a10 40 fprintf (stderr, "\tipsecctrl I\n");
69dcc425
CS
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");
db073a10 44 fprintf (stderr, "\t\tI : Print Statusinfo\n");
05207d69
MT
45}
46
8fcb9253
MT
47static void ipsec_reload() {
48 /* Re-read all configuration files and secrets and
49 * reload the daemon (#10339).
50 */
51 safe_system("/usr/sbin/ipsec rereadall >/dev/null 2>&1");
52 safe_system("/usr/sbin/ipsec reload >/dev/null 2>&1");
53}
54
5fd30232 55/*
69dcc425 56 ACCEPT the ipsec protocol ah, esp & udp (for nat traversal) on the specified interface
5fd30232
MT
57*/
58void open_physical (char *interface, int nat_traversal_port) {
69dcc425
CS
59 char str[STRING_SIZE];
60
69dcc425 61 // IKE
a9600358 62 sprintf(str, "/sbin/iptables --wait -D IPSECINPUT -p udp -i %s --dport 500 -j ACCEPT >/dev/null 2>&1", interface);
1f324fd7 63 safe_system(str);
a9600358 64 sprintf(str, "/sbin/iptables --wait -A IPSECINPUT -p udp -i %s --dport 500 -j ACCEPT", interface);
db073a10 65 safe_system(str);
69dcc425
CS
66
67 if (! nat_traversal_port)
68 return;
69
a9600358 70 sprintf(str, "/sbin/iptables --wait -D IPSECINPUT -p udp -i %s --dport %i -j ACCEPT >/dev/null 2>&1", interface, nat_traversal_port);
1f324fd7 71 safe_system(str);
a9600358 72 sprintf(str, "/sbin/iptables --wait -A IPSECINPUT -p udp -i %s --dport %i -j ACCEPT", interface, nat_traversal_port);
db073a10 73 safe_system(str);
5fd30232
MT
74}
75
76void ipsec_norules() {
69dcc425 77 /* clear input rules */
a9600358
MT
78 safe_system("/sbin/iptables --wait -F IPSECINPUT");
79 safe_system("/sbin/iptables --wait -F IPSECFORWARD");
80 safe_system("/sbin/iptables --wait -F IPSECOUTPUT");
05207d69
MT
81}
82
5fd30232
MT
83/*
84 return values from the vpn config file or false if not 'on'
85*/
86int decode_line (char *s,
69dcc425
CS
87 char **key,
88 char **name,
9f0b5c9f 89 char **type
69dcc425
CS
90 ) {
91 int count = 0;
92 *key = NULL;
93 *name = NULL;
94 *type = NULL;
95
96 if (s[strlen(s) - 1] == '\n')
97 s[strlen(s) - 1] = '\0';
98
99 char *result = strsep(&s, ",");
100 while (result) {
101 if (count == 0)
102 *key = result;
103 if ((count == 1) && strcmp(result, "on") != 0)
104 return 0; // a disabled line
105 if (count == 2)
106 *name = result;
107 if (count == 4)
108 *type = result;
69dcc425
CS
109 count++;
110 result = strsep(&s, ",");
111 }
112
113 // check other syntax
114 if (! *name)
115 return 0;
116
117 if (strspn(*name, LETTERS_NUMBERS) != strlen(*name)) {
118 fprintf(stderr, "Bad connection name: %s\n", *name);
119 return 0;
120 }
121
122 if (! (strcmp(*type, "host") == 0 || strcmp(*type, "net") == 0)) {
123 fprintf(stderr, "Bad connection type: %s\n", *type);
124 return 0;
125 }
126
69dcc425
CS
127 //it's a valid & active line
128 return 1;
5fd30232
MT
129}
130
131/*
132 issue ipsec commmands to turn on connection 'name'
133*/
9f0b5c9f
MT
134void turn_connection_on(char *name, char *type) {
135 /*
136 * To bring up a connection, we need to reload the configuration
137 * and issue ipsec up afterwards. To make sure the connection
138 * is not established from the start, we bring it down in advance.
139 */
6e2ba31b 140 char command[STRING_SIZE];
6e2ba31b 141
9f0b5c9f 142 // Bring down the connection (if established).
6e2ba31b 143 snprintf(command, STRING_SIZE - 1,
9f0b5c9f 144 "/usr/sbin/ipsec down %s >/dev/null", name);
6e2ba31b 145 safe_system(command);
6e2ba31b 146
8e2683f7
MT
147 // Reload the configuration into the daemon (#10339).
148 ipsec_reload();
6e2ba31b 149
9f0b5c9f
MT
150 // Bring the connection up again.
151 snprintf(command, STRING_SIZE - 1,
152 "/usr/sbin/ipsec up %s >/dev/null", name);
153 safe_system(command);
5fd30232 154}
9f0b5c9f 155
5fd30232
MT
156/*
157 issue ipsec commmands to turn off connection 'name'
158*/
159void turn_connection_off (char *name) {
9f0b5c9f
MT
160 /*
161 * To turn off a connection, all SAs must be turned down.
162 * After that, the configuration must be reloaded.
163 */
69dcc425 164 char command[STRING_SIZE];
9f0b5c9f
MT
165
166 // Bring down the connection.
3e077ef3 167 snprintf(command, STRING_SIZE - 1,
9f0b5c9f 168 "/usr/sbin/ipsec down %s >/dev/null", name);
3e077ef3 169 safe_system(command);
6e2ba31b 170
9f0b5c9f 171 // Reload, so the connection is dropped.
8e2683f7
MT
172 ipsec_reload();
173}
174
05207d69 175int main(int argc, char *argv[]) {
69dcc425
CS
176 char configtype[STRING_SIZE];
177 char redtype[STRING_SIZE] = "";
178 struct keyvalue *kv = NULL;
179
180 if (argc < 2) {
181 usage();
182 exit(1);
183 }
184 if (!(initsetuid()))
185 exit(1);
186
fe6cda92 187 FILE *file = NULL;
69dcc425 188
ba149d47
AF
189
190 if (strcmp(argv[1], "I") == 0) {
9f0b5c9f 191 safe_system("/usr/sbin/ipsec status");
ba149d47
AF
192 exit(0);
193 }
194
195 if (strcmp(argv[1], "R") == 0) {
8e2683f7 196 ipsec_reload();
ba149d47
AF
197 exit(0);
198 }
199
69dcc425
CS
200 /* FIXME: workaround for pclose() issue - still no real idea why
201 * this is happening */
202 signal(SIGCHLD, SIG_DFL);
203
204 /* handle operations that doesn't need start the ipsec system */
205 if (argc == 2) {
206 if (strcmp(argv[1], "D") == 0) {
9f0b5c9f 207 safe_system("/usr/sbin/ipsec stop >/dev/null 2>&1");
1f324fd7 208 ipsec_norules();
69dcc425
CS
209 exit(0);
210 }
69dcc425
CS
211 }
212
69dcc425
CS
213 /* read vpn config */
214 kv=initkeyvalues();
215 if (!readkeyvalues(kv, CONFIG_ROOT "/vpn/settings"))
216 {
217 fprintf(stderr, "Cannot read vpn settings\n");
218 exit(1);
219 }
220
221 /* check is the vpn system is enabled */
222 {
223 char s[STRING_SIZE];
224 findkey(kv, "ENABLED", s);
225 freekeyvalues(kv);
226 if (strcmp (s, "on") != 0)
227 exit(0);
228 }
229
230 /* read interface settings */
231 kv=initkeyvalues();
232 if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings"))
233 {
234 fprintf(stderr, "Cannot read ethernet settings\n");
235 exit(1);
236 }
237 if (!findkey(kv, "CONFIG_TYPE", configtype))
238 {
239 fprintf(stderr, "Cannot read CONFIG_TYPE\n");
240 exit(1);
241 }
242 findkey(kv, "RED_TYPE", redtype);
243
244
245 /* Loop through the config file to find physical interface that will accept IPSEC */
246 int enable_red=0; // states 0: not used
247 int enable_green=0; // 1: error condition
248 int enable_orange=0; // 2: good
249 int enable_blue=0;
250 char if_red[STRING_SIZE] = "";
251 char if_green[STRING_SIZE] = "";
252 char if_orange[STRING_SIZE] = "";
253 char if_blue[STRING_SIZE] = "";
254 char s[STRING_SIZE];
255
9f0b5c9f
MT
256 // when RED is up, find interface name in special file
257 FILE *ifacefile = NULL;
258 if ((ifacefile = fopen(CONFIG_ROOT "/red/iface", "r"))) {
259 if (fgets(if_red, STRING_SIZE, ifacefile)) {
260 if (if_red[strlen(if_red) - 1] == '\n')
261 if_red[strlen(if_red) - 1] = '\0';
69dcc425 262 }
9f0b5c9f 263 fclose (ifacefile);
69dcc425 264
9f0b5c9f
MT
265 if (VALID_DEVICE(if_red))
266 enable_red++;
267 }
69dcc425 268
9f0b5c9f
MT
269 // Check if GREEN is enabled.
270 findkey(kv, "GREEN_DEV", if_green);
271 if (VALID_DEVICE(if_green))
272 enable_green++;
9f0b5c9f
MT
273
274 // Check if ORANGE is enabled.
275 findkey(kv, "ORANGE_DEV", if_orange);
276 if (VALID_DEVICE(if_orange))
277 enable_orange++;
9f0b5c9f
MT
278
279 // Check if BLUE is enabled.
280 findkey(kv, "BLUE_DEV", if_blue);
281 if (VALID_DEVICE(if_blue))
282 enable_blue++;
69dcc425 283
69dcc425
CS
284 freekeyvalues(kv);
285
69dcc425 286 // exit if nothing to do
9f0b5c9f 287 if ((enable_red+enable_green+enable_orange+enable_blue) == 0)
69dcc425
CS
288 exit(0);
289
290 // open needed ports
9f0b5c9f 291 if (enable_red > 0)
69dcc425
CS
292 open_physical(if_red, 4500);
293
9f0b5c9f 294 if (enable_green > 0)
69dcc425
CS
295 open_physical(if_green, 4500);
296
9f0b5c9f 297 if (enable_orange > 0)
69dcc425
CS
298 open_physical(if_orange, 4500);
299
9f0b5c9f 300 if (enable_blue > 0)
69dcc425
CS
301 open_physical(if_blue, 4500);
302
69dcc425
CS
303 // start the system
304 if ((argc == 2) && strcmp(argv[1], "S") == 0) {
9f0b5c9f 305 safe_system("/usr/sbin/ipsec restart >/dev/null");
69dcc425
CS
306 exit(0);
307 }
308
309 // it is a selective start or stop
310 // second param is only a number 'key'
311 if ((argc == 2) || strspn(argv[2], NUMBERS) != strlen(argv[2])) {
9f0b5c9f 312 fprintf(stderr, "Bad arg: %s\n", argv[2]);
69dcc425
CS
313 usage();
314 exit(1);
315 }
316
317 // search the vpn pointed by 'key'
318 if (!(file = fopen(CONFIG_ROOT "/vpn/config", "r"))) {
69dcc425
CS
319 fprintf(stderr, "Couldn't open vpn settings file");
320 exit(1);
321 }
322 while (fgets(s, STRING_SIZE, file) != NULL) {
323 char *key;
324 char *name;
325 char *type;
9f0b5c9f 326 if (!decode_line(s,&key,&name,&type))
69dcc425
CS
327 continue;
328
69dcc425
CS
329 // is it the 'key' requested ?
330 if (strcmp(argv[2], key) != 0)
331 continue;
9f0b5c9f 332
69dcc425
CS
333 // Start or Delete this Connection
334 if (strcmp(argv[1], "S") == 0)
335 turn_connection_on (name, type);
9f0b5c9f 336 else if (strcmp(argv[1], "D") == 0)
69dcc425
CS
337 turn_connection_off (name);
338 else {
69dcc425
CS
339 fprintf(stderr, "Bad command\n");
340 exit(1);
341 }
342 }
343 fclose(file);
9f0b5c9f 344
69dcc425 345 return 0;
05207d69 346}