summaryrefslogtreecommitdiffstats
path: root/alass
blob: a1fe986fd2ea466a198ff1f4817a0a142844cca3 (plain)
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), $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();