diff options
-rwxr-xr-x | emergencyd.pl | 182 |
1 files changed, 113 insertions, 69 deletions
diff --git a/emergencyd.pl b/emergencyd.pl index 0668110..1cb7536 100755 --- a/emergencyd.pl +++ b/emergencyd.pl @@ -134,84 +134,128 @@ sub curtime { $year+1900,$mon+1,$mday,$hour,$min,$sec); } +sub check_mac { + my $cmdline = shift; + my $timestamp = shift; + my $mac_check = shift; + my $validate = "$cmdline|$timestamp"; + my $mac_checked = 0; + foreach my $key ( @authorized_keys ) { + if ( $mac_check eq hmac_sha256_hex($validate, $key) ) { + return 1; + } + } + return 0; +} + my $mintime = "0"; +my $running = 1; + +sub check_and_update_timestamp { + my $timestamp = shift; + if ( ($timestamp ge curtime(-30)) && ($timestamp le curtime(30)) + && ($timestamp gt $mintime) ) { + $mintime = $timestamp; + return 1; + } + return 0; +} + +sub cmd_ping { + return "PONG\n"; +} + +sub cmd_date { + return [ "DATE", curtime, $mintime ]; +} + +sub cmd_noop { + return "NOOP\n"; +} + +sub cmd_dpid { + return [ "DPID" , $$ ]; +} + +sub cmd_die { + $running = 0; + return "BYE!\n"; +} + +sub cmd_rkey { + read_keys; + return "DONE\n"; +} + +sub cmd_logm { + my $s = shift or die "Missing argument to LOGM\n"; + my $level = LOG_NOTICE; + my %levels = ("EMERG"=>LOG_EMERG, "ALERT"=>LOG_ALERT, + "CRIT"=>LOG_CRIT, "ERR"=>LOG_ERR, "WARNING"=>LOG_WARNING, + "NOTICE"=>LOG_NOTICE, "INFO"=>LOG_INFO, "DEBUG"=>LOG_DEBUG); + if ( $s =~ /^([A-Z0-9]*)\s+(.*)/ && exists($levels{$1}) ) { + $level = $levels{$1}; + $s = $2; + } + syslog $level, $s; + return "DONE\n"; +} + +sub cmd_syrq { + my $s = shift or die "Missing argument to SYRQ\n"; + open my $sysrq_trigger, ">", "/proc/sysrq-trigger" + or die "Couldn't open /proc/sysrq-trigger for writing: $!"; + print $sysrq_trigger $s + or die "Couldn't write to /proc/sysrq-trigger: $!"; + close $sysrq_trigger; + return "DONE\n"; +} + +my %dispatch = ( + "PING" => \&cmd_ping, + "DATE" => \&cmd_date, + "NOOP" => \&cmd_noop, + "DPID" => \&cmd_dpid, + "DIE!" => \&cmd_die, + "RKEY" => \&cmd_rkey, + "LOGM" => \&cmd_logm, + "SYRQ" => \&cmd_syrq, + ); + PACKET: -while (1) { +while ( $running ) { my $buf; my $sender = recv($socket, $buf, 16384, 0); die "Failed to receive packet: $!" unless defined($sender); my @lines = split /\015*\012|\|/s, $buf; - my $command = $lines[0] // ""; + my $cmdline = $lines[0] // ""; my $timestamp = $lines[1] // ""; - my $maccheck = $lines[2] // ""; - next PACKET if $command eq ""; - if ( $command eq "PING" ) { - send $socket, "PONG\n", 0, $sender; - } elsif ( $command eq "DATE" ) { - send $socket, ("DATE\n".curtime."\n".$mintime."\n"), 0, $sender; - } else { - my $validate = "$command|$timestamp"; - my $mac_checked = 0; - foreach my $key ( @authorized_keys ) { - if ( $maccheck eq hmac_sha256_hex($validate, $key) ) { - $mac_checked = 1; - } - } - unless ( $mac_checked ) { - send $socket, "!MAC\n", 0, $sender; - next PACKET; + my $mac_check = $lines[2] // ""; + next PACKET if $cmdline eq ""; + my $resp = undef; + eval { + my ($command, $arg) = $cmdline =~ /^([A-Z0-9\!]{4})(?:\s+(.*))?$/ + or die "!BAD\n"; + unless ( $command eq "PING" || $command eq "DATE" ) { + check_mac $cmdline, $timestamp, $mac_check + or die "!MAC\n"; + check_and_update_timestamp $timestamp + or die "!DAT\n"; } - my $datechecked = ($timestamp ge curtime(-30)) - && ($timestamp le curtime(30)) - && ($timestamp gt $mintime); - unless ( $datechecked ) { - send $socket, "!DAT\n", 0, $sender; - next PACKET; - } - $mintime = $timestamp; - if ( $command eq "NOOP" ) { - send $socket, "NOOP\n", 0, $sender; - } elsif ( $command eq "DPID" ) { - send $socket, "DPID\n$$\n", 0, $sender; - } elsif ( $command eq "DIE!" ) { - send $socket, "BYE!\n", 0, $sender; - exit 0; - } elsif ( $command eq "RKEY" ) { - my $resp = "DONE\n"; - eval { read_keys }; - if ( $@ ) { - $resp = "!ERR\n$@"; - } - send $socket, $resp, 0, $sender; - } elsif ( $command =~ /^LOGM\s+(.*)$/ ) { - my $s = $1; - my $level = LOG_NOTICE; - my %levels = ("EMERG"=>LOG_EMERG, "ALERT"=>LOG_ALERT, - "CRIT"=>LOG_CRIT, "ERR"=>LOG_ERR, "WARNING"=>LOG_WARNING, - "NOTICE"=>LOG_NOTICE, "INFO"=>LOG_INFO, "DEBUG"=>LOG_DEBUG); - if ( $s =~ /^([A-Z0-9]*)\s+(.*)/ && exists($levels{$1}) ) { - $level = $levels{$1}; - $s = $2; - } - syslog $level, $s; - send $socket, "DONE\n", 0, $sender; - } elsif ( $command =~ /^SYRQ\s+(.*)$/ ) { - my $s = $1; - my $resp = "DONE\n"; - eval { - open my $sysrq_trigger, ">", "/proc/sysrq-trigger" - or die "Couldn't open /proc/sysrq-trigger for writing: $!"; - print $sysrq_trigger $s - or die "Couldn't write to /proc/sysrq-trigger: $!"; - close $sysrq_trigger; - }; - if ( $@ ) { - $resp = "!ERR\n$@"; - } - send $socket, $resp, 0, $sender; - } else { - send $socket, "!UNK\n", 0, $sender; + my $sub = $dispatch{$command}; + die "!UNK\n" unless defined($sub); + $resp = &{$sub}($arg); + }; + if ( $@ ) { + $resp = $@; + if ( ref($resp) eq "" && $resp !~ /^\!/ ) { + $resp = "!ERR\n" . $resp; } } + if ( defined($resp) ) { + $resp = join("\n", @{$resp}) . "\n" if ref($resp) eq "ARRAY"; + send $socket, $resp, 0, $sender; + } } +exit 0; |