summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--alass86
1 files changed, 86 insertions, 0 deletions
diff --git a/alass b/alass
new file mode 100644
index 0000000..a1fe986
--- /dev/null
+++ b/alass
@@ -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();
+