diff options
-rw-r--r-- | alass | 86 |
1 files changed, 86 insertions, 0 deletions
@@ -0,0 +1,86 @@ +#!/usr/bin/perl -T + +use strictures; + +use autodie; +use Function::Parameters; +use Path::Tiny; +use Sys::Syslog qw(:standard :macros); + +=head1 SYNOPSIS + +[cat \$file | ] SSH_ORIGINAL_COMMAND="<command>" alass + +commands: + sign <filename> Request a signature for the file data + +=head1 DESCRIPTION + +The Arch Linux Automated Signing Server (ALASS) is supposed to be called via an +SSH forced command. It parses SSH_ORIGINAL_COMMAND, recieves file data via +STDIN and returns a signature for the file via STDOUT if the command and file +data pass various security checks. + +=cut + +fun main() { + my $prog_name = 'alass'; + + my $command = $ENV{SSH_ORIGINAL_COMMAND}; + + # reset PATH since taint mode requires a secure path + $ENV{PATH} = "/usr/local/sbin:/usr/local/bin:/usr/bin"; + + openlog($prog_name, "ndelay,pid", "local0"); + syslog(LOG_NOTICE, "Signing script called with arguments: %s", $command); + + # when this variable goes out of scope the temp dir is removed! + my $tmpdir = Path::Tiny->tempdir(); + my $input_file = $tmpdir->child("input"); + + copy_stdin_to_file($input_file); + + if (allowed_to_sign($command, $input_file)) { + syslog(LOG_NOTICE, "Signing request granted by security checks"); + my $signature = get_signature($input_file); + print $signature; + } else { + syslog(LOG_ERR, "Sign requst failed security checks"); + exit 1; + } +} + +fun copy_stdin_to_file($destination) { + my $data; + my $bufsize = 4096; + my $total_size = 0; + while (my $read_size = sysread(STDIN, $data, $bufsize)) { + $destination->append_raw($data); + $total_size += $read_size; + } + syslog(LOG_NOTICE, "Input file has %d bytes", $total_size); +} + +fun allowed_to_sign($command, $input_file) { + if ($command =~ m/^sign (?<filename>[^ ]+)/) { + my $filename = $+{filename}; + if ($filename =~ m/\.pkg\.tar\.xz$/) { + return 1; + } + } + return 0; +} + +fun get_signature($input_file) { + execute_command([qw(gpg --detach-sign --use-agent --no-armor), $input_file->stringify]); + return $input_file->sibling("input.sig")->slurp_raw(); +} + +fun execute_command($command) { + syslog(LOG_NOTICE, "Executing command: %s", join(" ", @$command)); + system(@$command); +} + + +main(); + |