1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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 --personal-digest-preference SHA512), $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();
|