aboutsummaryrefslogtreecommitdiffstats
path: root/lacme
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2016-12-01 00:16:18 +0100
committerGuilhem Moulin <guilhem@fripost.org>2016-12-01 00:16:18 +0100
commit27788fd4a399642eddbdb1934ccaa13f7fd00124 (patch)
tree80ac319fdaec806a82dfc04e1453054ca199683a /lacme
parentdd1da2ac44a7eab89e9a17135367aa0915efad0b (diff)
Make lacme able to spawn lacme-accountd.
Diffstat (limited to 'lacme')
-rwxr-xr-xlacme77
1 files changed, 57 insertions, 20 deletions
diff --git a/lacme b/lacme
index 13bff78..05b4b18 100755
--- a/lacme
+++ b/lacme
@@ -30,7 +30,7 @@ use File::Temp ();
use Getopt::Long qw/:config posix_default no_ignore_case gnu_getopt auto_version/;
use List::Util 'first';
use POSIX ();
-use Socket qw/PF_INET PF_INET6 PF_UNIX INADDR_ANY IN6ADDR_ANY
+use Socket qw/AF_UNIX PF_INET PF_INET6 PF_UNIX PF_UNSPEC INADDR_ANY IN6ADDR_ANY
SOCK_STREAM SOL_SOCKET SO_REUSEADDR SHUT_RDWR/;
use Config::Tiny ();
@@ -82,6 +82,7 @@ do {
my $h = Config::Tiny::->read_string($conf) or die Config::Tiny::->errstr()."\n";
my $defaults = delete $h->{_} // {};
+ my $accountd = exists $h->{accountd} ? 1 : 0;
my %valid = (
client => {
socket => (defined $ENV{XDG_RUNTIME_DIR} ? "$ENV{XDG_RUNTIME_DIR}/S.lacme" : undef),
@@ -99,6 +100,14 @@ do {
command => '/usr/lib/lacme/webserver',
iptables => 'Yes'
+ },
+ accountd => {
+ user => '',
+ group => '',
+ command => '/usr/bin/lacme-accountd',
+ config => '/etc/lacme/lacme-accountd.conf',
+ privkey => undef,
+ quiet => 'Yes',
}
);
foreach my $s (keys %valid) {
@@ -110,6 +119,7 @@ do {
}
die "Invalid section(s): ".join(', ', keys %$h)."\n" if %$h;
$CONFIG->{_} = $defaults;
+ delete $CONFIG->{accountd} unless $accountd;
};
# Regular expressions for domain validation
@@ -388,31 +398,58 @@ sub acme_client($@) {
my $args = shift;
my @args = @_;
- my @stat;
+ my $client;
my $conf = $CONFIG->{client};
- my $sockname = $OPTS{socket} // $conf->{socket} // die "Missing socket option\n";
- $sockname = $sockname =~ /\A(\p{Print}+)\z/ ? $1 : die "Invalid socket name\n"; # untaint $sockname
-
- # ensure we're the only user with write access to the parent dir
- my $dirname = $sockname =~ s/[^\/]+$//r;
- @stat = stat($dirname) or die "Can't stat $dirname: $!";
- die "Error: insecure permissions on $dirname\n" if ($stat[2] & 0022) != 0;
-
- # ensure we're the only user with read/write access to the socket
- @stat = stat($sockname) or die "Can't stat $sockname: $! (Is lacme-accountd running?)\n";
- die "Error: insecure permissions on $sockname\n" if ($stat[2] & 0066) != 0;
-
- # connect(2) to the socket
- socket(my $client, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
- my $sockaddr = Socket::sockaddr_un($sockname) // die "Invalid address $sockname\n";
- until (connect($client, $sockaddr)) {
- next if $! == EINTR; # try again if connect(2) was interrupted by a signal
- die "connect: $!";
+ if (defined (my $accountd = $CONFIG->{accountd})) {
+ socketpair($client, my $s, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!";
+ my $pid = fork() // "fork: $!";
+ unless ($pid) {
+ drop_privileges($accountd->{user}, $accountd->{group}, '/');
+ set_FD_CLOEXEC($s, 0);
+ $client->close() or die "Can't close: $!";
+ my @cmd = ($accountd->{command}, '--fdopen='.fileno($s));
+ push @cmd, '--config='.$accountd->{config} if defined $accountd->{config};
+ push @cmd, '--privkey='.$accountd->{privkey} if defined $accountd->{privkey};
+ push @cmd, '--quiet' unless lc $accountd->{quiet} eq 'no';
+ push @cmd, '--debug' if $OPTS{debug};
+ exec { $cmd[0] } @cmd or die;
+ }
+ print STDERR "[$$] Forking lacme-accountd, child PID $pid\n" if $OPTS{debug};
+ $s->close() or die "Can't close: $!";
+ push @CLEANUP, sub() {
+ print STDERR "[$$] Shutting down lacme-accountd\n" if $OPTS{debug};
+ shutdown($client, SHUT_RDWR) or warn "shutdown: $!";
+ kill 15 => $pid;
+ waitpid $pid => 0;
+ };
+ }
+ else {
+ my @stat;
+ my $sockname = $OPTS{socket} // $conf->{socket} // die "Missing socket option\n";
+ $sockname = $sockname =~ /\A(\p{Print}+)\z/ ? $1 : die "Invalid socket name\n"; # untaint $sockname
+
+ # ensure we're the only user with write access to the parent dir
+ my $dirname = $sockname =~ s/[^\/]+$//r;
+ @stat = stat($dirname) or die "Can't stat $dirname: $!";
+ die "Error: insecure permissions on $dirname\n" if ($stat[2] & 0022) != 0;
+
+ # ensure we're the only user with read/write access to the socket
+ @stat = stat($sockname) or die "Can't stat $sockname: $! (Is lacme-accountd running?)\n";
+ die "Error: insecure permissions on $sockname\n" if ($stat[2] & 0066) != 0;
+
+ # connect(2) to the socket
+ socket($client, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
+ my $sockaddr = Socket::sockaddr_un($sockname) // die "Invalid address $sockname\n";
+ until (connect($client, $sockaddr)) {
+ next if $! == EINTR; # try again if connect(2) was interrupted by a signal
+ die "connect: $!";
+ }
}
# use execve(2) rather than a Perl pseudo-process to ensure that the
# child doesn't have access to the parent's memory
my @fileno = map { fileno($_) =~ /^(\d+)$/ ? $1 : die } ($CONFFILE, $client); # untaint fileno
+ set_FD_CLOEXEC($client, 1);
spawn({%$args{qw/in out/}, child => sub() {
drop_privileges($conf->{user}, $conf->{group}, $args->{chdir} // '/');
set_FD_CLOEXEC($_, 0) foreach ($CONFFILE, $client);