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; | 
