}
sub fromjs($) {
- return from_json($_[0]);
+ my ($data) = @_;
+ ($data) = ($data =~ /^(.*)$/s); # untaint from_json croaks on error anyways.
+ return from_json($data);
}
sub fatal($$;$$) {
# Get certificate
# GET-as-POST to order's certificate URL
+# if $root is specified, attempts to find a matching (alternate) chain
# Expects a '200 OK' reply
# returns certificate chain in PEM format
sub get_certificate {
- my ($self, $order) = @_;
+ my ($self, $order, $root) = @_;
$self->fatal("no certificate URL available (yet?)", $order)
if !$order->{certificate};
+ my $check_root = sub {
+ my ($chain) = @_;
+
+ my @certs = PVE::Certificate::split_pem($chain);
+ my $root_pem = $certs[-1];
+
+ my ($file, $fh) = PVE::Tools::tempfile_contents($root_pem);
+ my $info = PVE::Certificate::get_certificate_info($file);
+
+ return defined($info->{issuer}) && $info->{issuer} =~ m/\Q$root\E/i;
+ };
+
my $r = $self->do(POST => $order->{certificate}, '');
- my $return = eval { __get_result($r, 200, 1); };
+ my $return = eval {
+ # default chain
+ my $res = __get_result($r, 200, 1);
+ if ($root && !$check_root->($res)) {
+ # alternate chains if requested and default didn't match
+ $res = undef;
+ my @links = $r->header('link');
+ for my $link (@links) {
+ if ($link =~ /^<(.*)>;rel="alternate"$/) {
+ my $url = $1;
+ my $chain = eval { __get_result($self->do(POST => $url, ''), 200, 1); };
+ die "failed to retrieve alternate chain from '$url' - $@\n" if $@;
+ if ($check_root->($chain)) {
+ $res = $chain;
+ last;
+ }
+ }
+ }
+ die "no matching alternate chain for '$root' returned by server\n"
+ if !defined($res);
+ }
+
+ if ($res =~ /^(-----BEGIN CERTIFICATE-----)(.+)(-----END CERTIFICATE-----)$/s) { # untaint
+ return $1 . $2 . $3;
+ }
+ die "Server reply does not look like a PEM encoded certificate\n";
+ };
$self->fatal("POST of '$order->{certificate}' failed - $@", $r) if $@;
return $return;
}
return $return;
}
-# return all availible subplugins from the plugins
-sub get_subplugins {
-
- my $tmp = [];
- my $plugins = PVE::ACME::Challenge->lookup_types();
-
- foreach my $plugin_name (@$plugins) {
- my $plugin = PVE::ACME::Challenge->lookup($plugin_name);
- push @$tmp, $plugin->get_subplugins();
- }
-
- my $subplugins = [];
- foreach my $array (@$tmp) {
- foreach my $subplugin ( @$array) {
- push @$subplugins, $subplugin;
- }
- }
-
- return $subplugins;
-}
-
# actually 'do' a $method request on $url
# $data: input for JWS, optional
# $use_jwk: use JWK instead of KID in JWD (see sub jws)