2 # Common functions for managing the freeswan config file
3 # XXX check for new errors in log after applying
4 # XXX option to download connection as .conf file, and upload an existing
5 # .conf file for addition
7 BEGIN { push(@INC, ".."); };
12 # Returns an array of configured connections
15 local $file = $_[0] || $config{'file'};
18 local $fh = "CONF".$get_config_fh++;
23 if (/^\s*([^= ]+)\s*=\s*"([^"]*)"/ ||
24 /^\s*([^= ]+)\s*=\s*'([^"]*)'/ ||
25 /^\s*([^= ]+)\s*=\s*(\S+)/) {
26 # Directive within a section
28 if ($sect->{'values'}->{lc($1)}) {
29 $sect->{'values'}->{lc($1)} .= "\0".$2;
32 $sect->{'values'}->{lc($1)} = $2;
34 $sect->{'eline'} = $lnum;
37 elsif (/^\s*include\s+(\S+)/) {
38 # Including possibly multiple files
45 foreach $g (glob($inc)) {
46 local @inc = &get_config($g);
47 map { $_->{'index'} += scalar(@rv) } @inc;
51 elsif (/^\s*(\S+)\s+(\S+)/) {
53 $sect = { 'name' => $1,
58 'index' => scalar(@rv),
69 # Add a new connection to the config file
72 local $lref = &read_file_lines($_[0]->{'file'} || $config{'file'});
73 push(@$lref, "", &conn_lines($_[0]));
80 local $lref = &read_file_lines($_[0]->{'file'});
81 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1,
89 local $lref = &read_file_lines($_[0]->{'file'});
90 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1);
94 # swap_conns(&conn1, &conn2)
95 # Swaps two connections in the config file
98 local ($first, $second) = @_;
99 if ($first->{'line'} > $second->{'line'}) {
100 ($first, $second) = ($second, $first);
102 local $lref1 = &read_file_lines($first->{'file'});
103 local $lref2 = &read_file_lines($second->{'file'});
104 splice(@$lref2, $second->{'line'}, $second->{'eline'} - $second->{'line'} + 1,
105 @$lref1[$first->{'line'} .. $first->{'eline'}]);
106 splice(@$lref2, $first->{'line'}, $first->{'eline'} - $first->{'line'} + 1,
107 @$lref1[$second->{'line'} .. $second->{'eline'}]);
115 push(@rv, $_[0]->{'name'}." ".$_[0]->{'value'});
116 foreach $o (sort { $a cmp $b } keys %{$_[0]->{'values'}}) {
117 local $v = $_[0]->{'values'}->{$o};
119 foreach $vv (split(/\0/, $v)) {
121 push(@rv, "\t".$o."=\"".$vv."\"");
124 push(@rv, "\t".$o."=".$vv);
134 local $out = `$config{'ipsec'} auto --status 2>&1`;
135 return $? || $out =~ /not running/i ? 0 : 1;
139 # Returns this system's public key
142 local $out = `$config{'ipsec'} showhostkey --file '$config{'secrets'}' --left 2>&1`;
143 if ($out =~ /leftrsasigkey=(\S+)/) {
149 # get_public_key_dns()
150 # Returns the flags, protocol, algorithm and key data for the public key,
151 # suitable for creating a DNS KEY record
152 sub get_public_key_dns
154 local $out = `$config{'ipsec'} showhostkey --file '$config{'secrets'}' 2>&1`;
155 if ($out =~ /KEY\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/) {
156 return ($1, $2, $3, $4);
159 # Try with new --key argument
160 $out = `$config{'ipsec'} showhostkey --key --file '$config{'secrets'}' 2>&1`;
161 if ($out =~ /KEY\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/) {
162 return ($1, $2, $3, $4);
169 # Returns a list of all policy files
173 opendir(DIR, $config{'policies_dir'});
174 while($f = readdir(DIR)) {
175 push(@rv, $f) if ($f !~ /^\./ && $f !~ /\.rpmsave$/);
185 open(FILE, "$config{'policies_dir'}/$_[0]");
187 push(@rv, "$1/$2") if (/^\s*([0-9\.]+)\/(\d+)/);
193 # write_policy(name, &nets)
196 local $lref = &read_file_lines("$config{'policies_dir'}/$_[0]");
198 foreach $p (@{$_[1]}) {
199 while($l < @$lref && $lref->[$l] !~ /^\s*([0-9\.]+)\/(\d+)/) {
203 # Found line to replace
213 if ($lref->[$l] =~ /^\s*([0-9\.]+)\/(\d+)/) {
214 splice(@$lref, $l, 1);
221 # wrap_lines(text, width)
222 # Given a multi-line string, return an array of lines wrapped to
228 while(length($rest) > $_[1]) {
229 push(@rv, substr($rest, 0, $_[1]));
230 $rest = substr($rest, $_[1]);
232 push(@rv, $rest) if ($rest ne '');
237 # Work out which log file IPsec messages go to, and record the size
240 @ipsec_logfiles = ( $config{'logfile'} );
241 if (&foreign_check("syslog")) {
242 # Find all syslog logfiles
243 &foreign_require("syslog", "syslog-lib.pl");
244 local $conf = &syslog::get_config();
245 foreach $c (@$conf) {
246 push(@ipsec_logfiles, $c->{'file'}) if ($c->{'file'} &&
250 @ipsec_logfiles = &unique(@ipsec_logfiles);
251 @ipsec_logfile_sizes = map { local @st = stat($_); $st[7] } @ipsec_logfiles;
255 # Check any new IPsec-related messages in the log for errors
258 # Give the server a chance to start
261 # Look for new error messages
263 for($i=0; $i<@ipsec_logfiles; $i++) {
264 open(LOG, $ipsec_logfiles[$i]) || next;
265 seek(LOG, $ipsec_logfile_sizes[$i], 0);
268 if (/ipsec/i && /error/i) {
269 s/^(\S+)\s+(\d+)\s+(\d+:\d+:\d+)\s+(\S+)\s+//;
276 # Fail if there were any
278 &error(&text('start_elog', "<p><tt>".join("<br>", @errs)."</tt><br>"));
282 # get_ipsec_version(&out)
283 sub get_ipsec_version
285 local $out = `$config{'ipsec'} --version 2>&1`;
287 return $out =~ /(FreeS\/WAN|Openswan|StrongSWAN)\s+([^ \n\(]+)/i ? ($2,$1) : (undef);
291 # Returns 1 if a valid secret key file exists, 0 if not
295 open(SEC, $config{'secrets'}) || return 0;
299 if (/Modulus:\s*(\S+)/) {
307 # expand_conf(&config)
311 for my $n (0..scalar(@$conf)-1) {
313 foreach my $key (keys(%{$conn->{'values'}})) {
314 $expanded{$conn->{'value'}}->{$key} = $conn->{'values'}->{$key};
317 # now go through and expand alsos
318 foreach my $k (keys(%expanded)) {
319 $conn = \%{$expanded{$k}};
320 # XXX - only supporing a single level of redirection
321 # - this should be moved into a function that could be called
323 if ($$conn{'also'}) {
324 foreach my $also (split(/\000/, $$conn{'also'})) {
325 foreach my $i (keys(%{$expanded{$also}})) {
326 $$conn{$i} = $expanded{$also}{$i};
329 # there is only one also key
337 # Apply the current configuration, and return an error message on failure or
341 local $cmd = $config{'restart_cmd'} ||
342 "($config{'stop_cmd'} && $config{'start_cmd'})";
344 local $out = &backquote_logged("$cmd 2>&1");
346 return "<pre>$out</pre>";
353 # Returns a list of IPsec secret keys
356 if (!scalar(@list_secrets_cache)) {
359 open(SEC, $config{'secrets'});
364 push(@lines, { 'value' => $1,
368 elsif (/^\s+(.*)/ && @lines) {
369 $lines[$#rv]->{'value'} .= "\n".$1;
370 $lines[$#rv]->{'eline'} = $lnum;
376 # Turn joined lines into secrets
378 foreach $l (@lines) {
379 $l->{'value'} =~ /^([^:]*)\s*:\s+(\S+)\s+((.|\n)*)$/ || next;
380 local $sec = { 'type' => $2,
383 'line' => $l->{'line'},
384 'eline' => $l->{'eline'},
385 'idx' => scalar(@list_secrets_cache),
387 $sec->{'name'} =~ s/\n/ /g;
388 $sec->{'name'} =~ s/\s+$//;
389 push(@list_secrets_cache, $sec);
392 return @list_secrets_cache;
395 # delete_secret(&sec)
396 # Removes one secret from the file
399 local $lref = &read_file_lines($config{'secrets'});
400 local $lines = $_[0]->{'eline'} - $_[0]->{'line'} + 1;
401 splice(@$lref, $_[0]->{'line'}, $lines);
404 splice(@list_secrets_cache, $_[0]->{'idx'}, 1);
405 foreach $s (@list_secrets_cache) {
406 if ($s->{'line'} > $_[0]->{'line'}) {
407 $s->{'line'} -= $lines;
408 $s->{'eline'} -= $lines;
410 if ($s->{'idx'} > $_[0]->{'idx'}) {
416 # create_secret(&sec)
417 # Add one secret to the file
420 &list_secrets(); # force cache init
421 local $lref = &read_file_lines($config{'secrets'});
422 $_[0]->{'line'} = scalar(@$lref);
423 local @lines = &secret_lines($_[0]);
424 push(@$lref, @lines);
426 $_[0]->{'eline'} = scalar(@$lref)-1;
427 $_[0]->{'idx'} = scalar(@list_secrets_cache);
428 push(@list_secrets_cache, $_[0]);
431 # modify_secret(&sec)
432 # Update one secret in the file
435 local $lref = &read_file_lines($config{'secrets'});
436 local @newlines = &secret_lines($_[0]);
437 local $oldlines = $_[0]->{'eline'} - $_[0]->{'line'} + 1;
438 splice(@$lref, $_[0]->{'line'}, $oldlines, @newlines);
441 foreach $s (@list_secrets_cache) {
442 if ($s ne $_[0] && $s->{'line'} > $_[0]->{'line'}) {
443 $s->{'line'} += @newlines - $oldlines;
444 $s->{'eline'} += @newlines - $oldlines;
447 $_[0]->{'eline'} += @newlines - $oldlines;
452 local $str = $_[0]->{'name'} ? $_[0]->{'name'}." : " : ": ";
453 $str .= uc($_[0]->{'type'});
454 $str .= " ".$_[0]->{'value'};
455 return split(/\n/, $str);
458 @rsa_attribs = ( "Modulus", "PublicExponent", "PrivateExponent",
459 "Prime1", "Prime2", "Exponent1", "Exponent2", "Coefficient" );