From fbcd17c52091cb51a86f0ab2acb5348a12b613e0 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Mon, 22 Feb 2021 12:06:09 +0100 Subject: In lacme's the [accountd] config, let lacme-accountd(1) do the %-expansion for 'config'. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This matches the arguably expected behavior that ‘config = %h/foo’ is passed as ‘--config=%h/foo’ and resolved by lacme-accountd(1) (possibly remote and with another passwd database). --- lacme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lacme') diff --git a/lacme b/lacme index 731535f..9691888 100755 --- a/lacme +++ b/lacme @@ -536,7 +536,7 @@ sub acme_client($@) { my ($cmd, @args) = split(/\s+/, $accountd->{command}) or die "Empty accountd command\n"; $_ = spec_expand($_) foreach ($cmd, @args); # expand %-specifiers after privilege drop and whitespace split push @args, '--stdio'; - push @args, '--config='.spec_expand($accountd->{config}) if $accountd->{config} ne ''; + push @args, '--config='.$accountd->{config} if $accountd->{config} ne ''; push @args, '--privkey='.$accountd->{privkey} if $accountd->{privkey} ne ''; # XXX deprecated in 0.8.0 push @args, '--quiet' unless lc $accountd->{quiet} eq 'no'; push @args, '--debug' if $OPTS{debug}; -- cgit v1.2.3 From 016c9611970c0667ad02cb1cf31834f2325b1575 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 24 Feb 2021 12:56:28 +0100 Subject: lacme: When getpwnam()/getgrnam()'s errno is 0, exclude it from error messages. --- lacme | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 9691888..a1e6b10 100755 --- a/lacme +++ b/lacme @@ -240,7 +240,7 @@ sub drop_privileges($$$) { # set effective and real gid; also set the list of supplementary gids to that single gid if ($group ne '') { - my $gid = getgrnam($group) // die "getgrnam($group): $!"; + my $gid = getgrnam($group) // die "getgrnam($group)", ($! ? ": $!" : "\n"); $) = "$gid $gid"; die "setgroups: $!" if $@; POSIX::setgid($gid) or die "setgid: $!"; @@ -249,7 +249,7 @@ sub drop_privileges($$$) { # set effective and real uid if ($user ne '') { - my $uid = getpwnam($user) // die "getpwnam($user): $!"; + my $uid = getpwnam($user) // die "getpwnam($user)", ($! ? ": $!" : "\n"); POSIX::setuid($uid) or die "setuid: $!"; die "Couldn't setuid/seteuid" unless $< == $uid and $> == $uid; # safety check } @@ -351,7 +351,7 @@ sub spawn_webserver() { my $tmpdir = File::Temp::->newdir(CLEANUP => 1, TMPDIR => 1, TEMPLATE => "acme-challenge.XXXXXXXXXX") // die; chmod 0755, $tmpdir or die "chmod: $!"; if ((my $username = $CONFIG->{client}->{user}) ne '') { - my $uid = getpwnam($username) // die "getpwnam($username): $!"; + my $uid = getpwnam($username) // die "getpwnam($username)", ($! ? ": $!" : "\n"); chown($uid, -1, $tmpdir) or die "chown: $!"; } @@ -849,10 +849,10 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { if (defined $conf->{chown}) { my ($user, $group) = split /:/, $conf->{chown}, 2; - my $uid = getpwnam($user) // die "getpwnam($user): $!"; - my $gid = defined $group ? (getgrnam($group) // die "getgrnam($group): $!") : -1; + my $uid = getpwnam($user) // die "getpwnam($user)", ($! ? ": $!" : "\n"); + my $gid = getgrnam($group) // die "getgrnam($group)", ($! ? ": $!" : "\n") if defined $group; foreach (grep defined, @$conf{qw/certificate certificate-chain/}) { - chown($uid, $gid, $_) or die "chown: $!"; + chown($uid, $gid // -1, $_) or die "chown: $!"; } } if (defined $conf->{chmod}) { -- cgit v1.2.3 From d1a862d9cb98a54e12c9fdbc405b896f3f0efcfe Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 24 Feb 2021 13:25:38 +0100 Subject: lacme: Ignore empty values in 'chown'/'chmod'/'certificate'/'certificate-chain'. --- lacme | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index a1e6b10..66dd6f6 100755 --- a/lacme +++ b/lacme @@ -766,15 +766,15 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { print STDERR " $_ = $conf->{$_}\n" foreach grep { defined $conf->{$_} } (sort keys %$conf); } - my $cert = $conf->{'certificate-chain'} // $conf->{'certificate'}; - unless (defined $cert) { + my @certs = grep {defined $_ and $_ ne ""} @$conf{qw/certificate-chain certificate/}; + unless (@certs) { print STDERR "[$s] Warning: Missing 'certificate' and 'certificate-chain', skipping\n"; $rv = 1; next; } # skip certificates that expire at least $conf->{'min-days'} days in the future - if (-f $cert and defined (my $t = x509_enddate($cert))) { + if (-f $certs[0] and defined (my $t = x509_enddate($certs[0]))) { my $d = $OPTS{'min-days'} // $conf->{'min-days'} // 21; if ($d >= 0 and $t - time > $d*86400) { my $d = POSIX::strftime('%Y-%m-%d %H:%M:%S UTC', gmtime($t)); @@ -838,26 +838,26 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { } # install certificate - if (defined $conf->{'certificate'}) { - print STDERR "Installing X.509 certificate $conf->{'certificate'}\n"; - install_cert($conf->{'certificate'}, $x509, 1); + if ((my $path = $conf->{'certificate'} // "") ne "") { + print STDERR "Installing X.509 certificate $path\n"; + install_cert($path, $x509, 1); } - if (defined $conf->{'certificate-chain'}) { - print STDERR "Installing X.509 certificate chain $conf->{'certificate-chain'}\n"; - install_cert($conf->{'certificate-chain'}, $x509); + if ((my $path = $conf->{'certificate-chain'} // "") ne "") { + print STDERR "Installing X.509 certificate chain $path\n"; + install_cert($path, $x509); } - if (defined $conf->{chown}) { - my ($user, $group) = split /:/, $conf->{chown}, 2; + if ((my $own = $conf->{chown} // "") ne "") { + my ($user, $group) = split /:/, $own, 2; my $uid = getpwnam($user) // die "getpwnam($user)", ($! ? ": $!" : "\n"); my $gid = getgrnam($group) // die "getgrnam($group)", ($! ? ": $!" : "\n") if defined $group; - foreach (grep defined, @$conf{qw/certificate certificate-chain/}) { + foreach (@certs) { chown($uid, $gid // -1, $_) or die "chown: $!"; } } - if (defined $conf->{chmod}) { - my $mode = oct($conf->{chmod}) // die; - foreach (grep defined, @$conf{qw/certificate certificate-chain/}) { + if ((my $mode = $conf->{chmod} // "") ne "") { + my $mode = oct($mode) // die; + foreach (@certs) { chmod($mode, $_) or die "chown: $!"; } } -- cgit v1.2.3 From faab30461b0f2b920e3dd19489ce458c0b38e6d9 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 24 Feb 2021 21:06:48 +0100 Subject: If restricting access via umask() fails, don't include errno in the error message. errno is not set on umask failure, see https://perldoc.perl.org/functions/umask. --- lacme | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 66dd6f6..fb19646 100755 --- a/lacme +++ b/lacme @@ -376,14 +376,14 @@ sub spawn_webserver() { if ($domain == AF_UNIX) { # bind(2) with a loose umask(2) to allow anyone to connect - my $umask = umask(0111) // die "umask: $!"; + my $umask = umask(0111) // die; my $path = Socket::unpack_sockaddr_un($sockaddr); bind($sock, $sockaddr) or die "Couldn't bind to $p: $!"; push @CLEANUP, sub() { print STDERR "Unlinking $path\n" if $OPTS{debug}; unlink $path or warn "Warning: Couldn't unlink $path: $!"; }; - umask($umask) // die "umask: $!"; + umask($umask) // die; } else { bind($sock, $sockaddr) or die "Couldn't bind to $p: $!"; -- cgit v1.2.3 From cdd025133a306cd8d3e81aa832ac056119d65f3a Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 24 Feb 2021 20:03:44 +0100 Subject: lacme: Don't write certificate(-chain) file on chown/chmod failure. Otherwise we end up with files with mode 0644 owned by root:root, and subsequent lacme(8) invocations will likely not renew them for a while. This change also saves a chown(2) call. And the new logic (chown resp. chmod from root:root resp. 0600) is safe if we ever include private key material in there too. --- lacme | 54 +++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 25 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index fb19646..2366830 100755 --- a/lacme +++ b/lacme @@ -657,8 +657,9 @@ sub spawn($@) { ############################################################################# # Install the certificate (optionally excluding the chain of trust) # -sub install_cert($$;$) { - my ($filename, $chain, $leafonly) = @_; +sub install_cert(%) { + my %args = @_; + my $filename = $args{path} // die; my ($dirname, $basename) = $filename =~ /\A(.*)\/([^\/]+)\z/ ? ($1, $2) : ('.', $filename); @@ -666,10 +667,8 @@ sub install_cert($$;$) { TEMPLATE => "$basename.XXXXXX") // die; eval { - my $umask = umask() // die "umask: $!"; - chmod(0644 &~ $umask, $fh) or die "chmod: $!"; - if ($leafonly) { - # keep only the leaf certificate + if ($args{nochain}) { + # keep only the first certificate in the file pipe my $rd, my $wd or die "pipe: $!"; my $pid = fork // die "fork: $!"; unless ($pid) { @@ -678,16 +677,34 @@ sub install_cert($$;$) { exec qw/openssl x509 -outform PEM/ or die; } $rd->close() or die "close: $!"; - $wd->print($chain); + $wd->print($args{content}); $wd->close() or die "close: $!"; waitpid $pid => 0; die $? if $? > 0; } else { - $fh->print($chain) or die "print: $!"; + $fh->print($args{content}) or die "print: $!"; } + + my $mode; + if ((my $m = $args{mode}) ne "") { + $mode = oct($m) // die; + } else { + my $umask = umask() // die; + $mode = 0644 &~ $umask; + } + chmod($mode, $fh) or die "chown: $!"; + + if ((my $owner = $args{owner}) ne "") { + my ($user, $group) = split /:/, $owner, 2; + my $uid = getpwnam($user) // die "getpwnam($user)", ($! ? ": $!" : "\n"); + my $gid = getgrnam($group) // die "getgrnam($group)", ($! ? ": $!" : "\n") if defined $group; + chown($uid, $gid // -1, $fh) or die "chown: $!"; + } + $fh->close() or die "close: $!"; }; + my $path = $fh->filename(); if ($@) { print STDERR "Unlinking $path\n" if $OPTS{debug}; @@ -837,29 +854,16 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { } } + my %install = ( content => $x509, mode => $conf->{chmod} // "", owner => $conf->{chown} // "" ); + # install certificate if ((my $path = $conf->{'certificate'} // "") ne "") { print STDERR "Installing X.509 certificate $path\n"; - install_cert($path, $x509, 1); + install_cert(%install, path => $path, nochain => 1); } if ((my $path = $conf->{'certificate-chain'} // "") ne "") { print STDERR "Installing X.509 certificate chain $path\n"; - install_cert($path, $x509); - } - - if ((my $own = $conf->{chown} // "") ne "") { - my ($user, $group) = split /:/, $own, 2; - my $uid = getpwnam($user) // die "getpwnam($user)", ($! ? ": $!" : "\n"); - my $gid = getgrnam($group) // die "getgrnam($group)", ($! ? ": $!" : "\n") if defined $group; - foreach (@certs) { - chown($uid, $gid // -1, $_) or die "chown: $!"; - } - } - if ((my $mode = $conf->{chmod} // "") ne "") { - my $mode = oct($mode) // die; - foreach (@certs) { - chmod($mode, $_) or die "chown: $!"; - } + install_cert(%install, path => $path); } my @certopts = join ',', qw/no_header no_version no_pubkey no_sigdump/; -- cgit v1.2.3 From c612a7ff44995f4f9c39fa0fb68470d90c88decf Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 24 Feb 2021 21:01:12 +0100 Subject: lacme: Default mode for certificate(-chain) creation is 0644 minus umask restrictions. Also, always spawn the client with umask 0022 so a starting lacme(8) with a restrictive umask doesn't impede serving challenge response files. --- lacme | 1 + 1 file changed, 1 insertion(+) (limited to 'lacme') diff --git a/lacme b/lacme index 2366830..9012890 100755 --- a/lacme +++ b/lacme @@ -581,6 +581,7 @@ sub acme_client($@) { set_FD_CLOEXEC($client, 1); my $rv = spawn({in => $args->{in}, out => $args->{out}, child => sub() { drop_privileges($conf->{user}, $conf->{group}, $args->{chdir} // '/'); + umask(0022) // die; set_FD_CLOEXEC($_, 0) foreach ($CONFFILE, $client); seek($CONFFILE, SEEK_SET, 0) or die "seek: $!"; $ENV{DEBUG} = $OPTS{debug} // 0; -- cgit v1.2.3 From c6a4aaa6128d55ba5f7f3cd2bd75f789f69ae407 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 24 Feb 2021 21:24:13 +0100 Subject: lacme: Add 'owner' resp. 'mode' as (prefered) alias for 'chown' resp. 'chmod'. --- lacme | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 9012890..2d9202d 100755 --- a/lacme +++ b/lacme @@ -761,7 +761,8 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { my $def = delete $h->{_} // {}; $defaults{$_} = $def->{$_} foreach keys %$def; my @valid = qw/certificate certificate-chain certificate-key min-days CAfile - hash keyUsage subject subjectAltName tlsfeature chown chmod notify/; + hash keyUsage subject subjectAltName tlsfeature + owner chown mode chmod notify/; foreach my $s (keys %$h) { $conf->{$s} = { map { $_ => delete $h->{$s}->{$_} } @valid }; die "Unknown option(s) in [$s]: ".join(', ', keys %{$h->{$s}})."\n" if %{$h->{$s}}; @@ -855,7 +856,10 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { } } - my %install = ( content => $x509, mode => $conf->{chmod} // "", owner => $conf->{chown} // "" ); + my %install = ( content => $x509, + mode => $conf->{mode} // $conf->{chmod} // "", + owner => $conf->{owner} // $conf->{chown} // "" + ); # install certificate if ((my $path = $conf->{'certificate'} // "") ne "") { -- cgit v1.2.3 From ea5a51ecaa72c8277b4f878cf3635025d757fa37 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 24 Feb 2021 21:28:31 +0100 Subject: lacme: Return an error when the 'mode'/'chown' isn't a number. oct("foobar") is 0, definitely not what we want. --- lacme | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lacme') diff --git a/lacme b/lacme index 2d9202d..b52cddd 100755 --- a/lacme +++ b/lacme @@ -689,7 +689,8 @@ sub install_cert(%) { my $mode; if ((my $m = $args{mode}) ne "") { - $mode = oct($m) // die; + die "Not an octal string: $m\n" unless $m =~ /^[0-9]+$/; + $mode = oct($m); } else { my $umask = umask() // die; $mode = 0644 &~ $umask; -- cgit v1.2.3 From 491998131f18d136ca37f15898d07062ad7a1fae Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 24 Feb 2021 21:50:11 +0100 Subject: lacme: improve install_cert()'s handling of temporary files. --- lacme | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index b52cddd..102deb6 100755 --- a/lacme +++ b/lacme @@ -660,12 +660,10 @@ sub spawn($@) { # sub install_cert(%) { my %args = @_; - my $filename = $args{path} // die; + my $path = $args{path} // die; - my ($dirname, $basename) = - $filename =~ /\A(.*)\/([^\/]+)\z/ ? ($1, $2) : ('.', $filename); - my $fh = File::Temp::->new(UNLINK => 0, DIR => $dirname, - TEMPLATE => "$basename.XXXXXX") // die; + my $fh = File::Temp::->new(TEMPLATE => "$path.XXXXXXXXXX", UNLINK => 0) // die; + my $path_tmp = $fh->filename(); eval { if ($args{nochain}) { @@ -707,13 +705,14 @@ sub install_cert(%) { $fh->close() or die "close: $!"; }; - my $path = $fh->filename(); if ($@) { - print STDERR "Unlinking $path\n" if $OPTS{debug}; - unlink $path or warn "unlink($path): $!"; + print STDERR "Unlinking $path_tmp\n" if $OPTS{debug}; + unlink $path_tmp or warn "unlink($path_tmp): $!"; die $@; + } else { + # atomically replace $path if it exists + rename($path_tmp, $path) or die "rename($path_tmp, $path): $!"; } - rename($path, $filename) or die "rename($path, $filename): $!"; } -- cgit v1.2.3 From f09c95ea97c9bdee92f7c7622689aed540373a73 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 25 Feb 2021 00:30:37 +0100 Subject: lacme: split certificates using Net::SSLeay::PEM_* instead of calling openssl. --- lacme | 72 +++++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 102deb6..13c2ef5 100755 --- a/lacme +++ b/lacme @@ -37,7 +37,7 @@ use Socket 1.95 qw/AF_UNIX AF_INET AF_INET6 PF_UNIX PF_INET PF_INET6 PF_UNSPEC use Config::Tiny (); use Date::Parse (); -use Net::SSLeay (); +use Net::SSLeay 1.46 (); # Clean up PATH $ENV{PATH} = join ':', qw{/usr/bin /bin}; @@ -658,32 +658,14 @@ sub spawn($@) { ############################################################################# # Install the certificate (optionally excluding the chain of trust) # -sub install_cert(%) { - my %args = @_; - my $path = $args{path} // die; +sub install_cert($$%) { + my ($path, $content, %args) = @_; my $fh = File::Temp::->new(TEMPLATE => "$path.XXXXXXXXXX", UNLINK => 0) // die; my $path_tmp = $fh->filename(); eval { - if ($args{nochain}) { - # keep only the first certificate in the file - pipe my $rd, my $wd or die "pipe: $!"; - my $pid = fork // die "fork: $!"; - unless ($pid) { - open STDIN, '<&', $rd or die "dup: $!"; - open STDOUT, '>&', $fh or die "dup: $!"; - exec qw/openssl x509 -outform PEM/ or die; - } - $rd->close() or die "close: $!"; - $wd->print($args{content}); - $wd->close() or die "close: $!"; - - waitpid $pid => 0; - die $? if $? > 0; - } else { - $fh->print($args{content}) or die "print: $!"; - } + $fh->print($content) or die "print: $!"; my $mode; if ((my $m = $args{mode}) ne "") { @@ -785,15 +767,15 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { print STDERR " $_ = $conf->{$_}\n" foreach grep { defined $conf->{$_} } (sort keys %$conf); } - my @certs = grep {defined $_ and $_ ne ""} @$conf{qw/certificate-chain certificate/}; - unless (@certs) { + my @certpaths = grep {defined $_ and $_ ne ""} @$conf{qw/certificate-chain certificate/}; + unless (@certpaths) { print STDERR "[$s] Warning: Missing 'certificate' and 'certificate-chain', skipping\n"; $rv = 1; next; } # skip certificates that expire at least $conf->{'min-days'} days in the future - if (-f $certs[0] and defined (my $t = x509_enddate($certs[0]))) { + if (-f $certpaths[0] and defined (my $t = x509_enddate($certpaths[0]))) { my $d = $OPTS{'min-days'} // $conf->{'min-days'} // 21; if ($d >= 0 and $t - time > $d*86400) { my $d = POSIX::strftime('%Y-%m-%d %H:%M:%S UTC', gmtime($t)); @@ -826,18 +808,37 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { } } - my ($x509, $csr_pubkey, $x509_pubkey); + my $chain; print STDERR "[$s] Will request authorization for: ".join(", ", @authz), "\n" if $OPTS{debug}; - if (acme_client({chdir => $challenge_dir, in => $csr, out => \$x509}, @authz)) { + if (acme_client({chdir => $challenge_dir, in => $csr, out => \$chain}, @authz)) { print STDERR "[$s] Error: Couldn't issue X.509 certificate!\n"; $rv = 1; next; } + my $cert; + eval { + my $mem = Net::SSLeay::BIO_s_mem() or die; + my $bio = Net::SSLeay::BIO_new($mem) or die; + die "incomplete write" unless + Net::SSLeay::BIO_write($bio, $chain) == length($chain); + my $x509 = Net::SSLeay::PEM_read_bio_X509($bio); + $cert = Net::SSLeay::PEM_get_string_X509($x509); + Net::SSLeay::BIO_free($bio) or die; + }; + if ($@) { + print STDERR "[$s] Error: Received bogus X.509 certificate from ACME server!\n"; + $rv = 1; + next; + } + # extract pubkeys from CSR and cert, and ensure they match + # XXX would be nice to use X509_get_X509_PUBKEY and X509_REQ_get_X509_PUBKEY here, + # or EVP_PKEY_cmp(), but unfortunately Net::SSLeay 1.88 doesn't support these + my ($cert_pubkey, $csr_pubkey); + spawn({in => $cert, out => \$cert_pubkey}, qw/openssl x509 -inform PEM -noout -pubkey/); spawn({in => $csr, out => \$csr_pubkey }, qw/openssl req -inform DER -noout -pubkey/); - spawn({in => $x509, out => \$x509_pubkey}, qw/openssl x509 -inform PEM -noout -pubkey/); - unless (defined $x509_pubkey and defined $csr_pubkey and $x509_pubkey eq $csr_pubkey) { + unless (defined $cert_pubkey and defined $csr_pubkey and $cert_pubkey eq $csr_pubkey) { print STDERR "[$s] Error: Received bogus X.509 certificate from ACME server!\n"; $rv = 1; next; @@ -845,7 +846,7 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { # verify certificate validity against the CA bundle if ((my $CAfile = $conf->{CAfile} // '@@datadir@@/lacme/ca-certificates.crt') ne '') { - my %args = (in => $x509); + my %args = (in => $cert); $args{out} = \*STDERR if $OPTS{debug}; my @options = ('-trusted', $CAfile, '-purpose', 'sslserver', '-x509_strict'); push @options, '-show_chain' if $OPTS{debug}; @@ -856,25 +857,24 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') { } } - my %install = ( content => $x509, + # install certificate + my %install_opts = ( mode => $conf->{mode} // $conf->{chmod} // "", owner => $conf->{owner} // $conf->{chown} // "" ); - - # install certificate if ((my $path = $conf->{'certificate'} // "") ne "") { print STDERR "Installing X.509 certificate $path\n"; - install_cert(%install, path => $path, nochain => 1); + install_cert($path => $cert, %install_opts); } if ((my $path = $conf->{'certificate-chain'} // "") ne "") { print STDERR "Installing X.509 certificate chain $path\n"; - install_cert(%install, path => $path); + install_cert($path => $chain, %install_opts); } my @certopts = join ',', qw/no_header no_version no_pubkey no_sigdump/; open my $fh, '|-', qw/openssl x509 -noout -fingerprint -sha256 -text -certopt/, @certopts or die "fork: $!"; - print $fh $x509; + print $fh $cert; close $fh or die $! ? "close: $!" : "Error: x509(1ssl) exited with value ".($? >> 8)."\n"; -- cgit v1.2.3 From 9a8f705eddd18ccc9a24fe0e7efe6b5a87b2be09 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 25 Feb 2021 01:41:59 +0100 Subject: lacme: pass a temporary JSON file with the client configuration to the internal client. So it doesn't have to parse the INI file again. Also, while lacme.conf is world-readable by default, one might restrict permissions and add private information in there, not realizing that everything, including comments, will be readable by the client. --- lacme | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 13c2ef5..d7dac54 100755 --- a/lacme +++ b/lacme @@ -37,13 +37,14 @@ use Socket 1.95 qw/AF_UNIX AF_INET AF_INET6 PF_UNIX PF_INET PF_INET6 PF_UNSPEC use Config::Tiny (); use Date::Parse (); +use JSON (); use Net::SSLeay 1.46 (); # Clean up PATH $ENV{PATH} = join ':', qw{/usr/bin /bin}; delete @ENV{qw/IFS CDPATH ENV BASH_ENV/}; -my ($COMMAND, %OPTS, $CONFFILE, $CONFIG, @CLEANUP); +my ($COMMAND, %OPTS, $CONFIG, @CLEANUP); $SIG{$_} = sub() { exit 1 } foreach qw/INT TERM/; # run the END block upon SIGINT/SIGTERM @@ -99,14 +100,12 @@ sub spec_expand($) { return $str; } -sub set_FD_CLOEXEC($$); my $CONFFILENAME = spec_expand($OPTS{config} // "%E/lacme/$NAME.conf"); do { print STDERR "Using configuration file: $CONFFILENAME\n" if $OPTS{debug}; - open $CONFFILE, '<', $CONFFILENAME or die "Can't open $CONFFILENAME: $!\n"; - my $conf = do { local $/ = undef; <$CONFFILE> }; - # don't close $CONFFILE so we can pass it to the client - set_FD_CLOEXEC($CONFFILE, 1); + open my $fh, '<', $CONFFILENAME or die "Can't open $CONFFILENAME: $!\n"; + my $conf = do { local $/ = undef; <$fh> }; + close $fh or die "close: $!"; my $h = Config::Tiny::->read_string($conf) or die Config::Tiny::->errstr()."\n"; my $defaults = delete $h->{_} // {}; @@ -573,19 +572,26 @@ sub acme_client($@) { die "connect: $!"; } } + set_FD_CLOEXEC($client, 1); + + my $client_config; + do { + my $tmp = File::Temp::->new(TMPDIR => 1, TEMPLATE => "lacme-client.conf.json-XXXXXXXXXX", UNLINK => 1) // die; + print $tmp JSON::->new->encode($conf); + open $client_config, "<", $tmp->filename() or die "open: $!"; + }; # use execve(2) rather than a Perl pseudo-process to ensure that the # child doesn't have access to the parent's memory my ($cmd, @args2) = split(/\s+/, $conf->{command}) or die "Empty client command\n"; - my @fileno = map { fileno($_) =~ /^(\d+)$/ ? $1 : die } ($CONFFILE, $client); # untaint fileno - set_FD_CLOEXEC($client, 1); + my @fileno = map { fileno($_) =~ /^(\d+)$/ ? $1 : die } ($client_config, $client); # untaint fileno my $rv = spawn({in => $args->{in}, out => $args->{out}, child => sub() { drop_privileges($conf->{user}, $conf->{group}, $args->{chdir} // '/'); umask(0022) // die; - set_FD_CLOEXEC($_, 0) foreach ($CONFFILE, $client); - seek($CONFFILE, SEEK_SET, 0) or die "seek: $!"; + set_FD_CLOEXEC($_, 0) for ($client_config, $client); $ENV{DEBUG} = $OPTS{debug} // 0; }}, $cmd, @args2, $COMMAND, @fileno, @args); + close $client_config or die "close: $!\n"; if (defined $cleanup) { @CLEANUP = grep { $_ ne $cleanup } @CLEANUP; -- cgit v1.2.3 From b3af3526b293f396da02a6276ea86ca17dcd2d03 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 25 Jan 2023 03:23:51 +0100 Subject: Prepare new release v0.8.1. --- lacme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lacme') diff --git a/lacme b/lacme index d7dac54..21a184c 100755 --- a/lacme +++ b/lacme @@ -22,7 +22,7 @@ use v5.14.2; use strict; use warnings; -our $VERSION = '0.8.0'; +our $VERSION = '0.8.1'; my $NAME = 'lacme'; use Errno 'EINTR'; -- cgit v1.2.3