Add support to revoke a certificate by providing the pem

This is important so that a user can revoke a certificate that is not acquired or logged from our acme client
This commit is contained in:
Konstantinos Kallas 2017-08-12 17:14:23 +03:00
parent 73f0b6707a
commit a72a7f830a
2 changed files with 50 additions and 16 deletions

View File

@ -9,6 +9,7 @@
is_valid_account_opt/1, is_valid_account_opt/1,
is_valid_verbose_opt/1, is_valid_verbose_opt/1,
is_valid_domain_opt/1, is_valid_domain_opt/1,
is_valid_revoke_cert/1,
%% Key Related %% Key Related
generate_key/0, generate_key/0,
to_public/1 to_public/1
@ -53,6 +54,11 @@ is_valid_domain_opt(DomainString) ->
SeparatedDomains -> SeparatedDomains ->
true true
end. end.
-spec is_valid_revoke_cert(string()) -> boolean().
is_valid_revoke_cert(DomainOrFile) ->
lists:prefix("file:", DomainOrFile) orelse
lists:prefix("domain:", DomainOrFile).
@ -457,10 +463,10 @@ get_utc_validity(#'Certificate'{tbsCertificate = TbsCertificate}) ->
%% %%
-spec revoke_certificate(string()) -> {ok, deleted} | {error, _}. -spec revoke_certificate(string()) -> {ok, deleted} | {error, _}.
revoke_certificate(Domain) -> revoke_certificate(DomainOrFile) ->
try try
CAUrl = binary_to_list(get_config_ca_url()), CAUrl = binary_to_list(get_config_ca_url()),
revoke_certificate0(CAUrl, Domain) revoke_certificate0(CAUrl, DomainOrFile)
catch catch
throw:Throw -> throw:Throw ->
Throw; Throw;
@ -469,28 +475,50 @@ revoke_certificate(Domain) ->
{error, revoke_certificate} {error, revoke_certificate}
end. end.
-spec revoke_certificate0(url(), string()) -> {ok, deleted} | {error, not_found}. -spec revoke_certificate0(url(), string()) -> {ok, deleted}.
revoke_certificate0(CAUrl, Domain) -> revoke_certificate0(CAUrl, DomainOrFile) ->
BinDomain = list_to_bitstring(Domain), ParsedCert = parse_revoke_cert_argument(DomainOrFile),
case domain_certificate_exists(BinDomain) of revoke_certificate1(CAUrl, ParsedCert).
{BinDomain, Certificate} ->
ok = revoke_certificate1(CAUrl, Certificate), -spec revoke_certificate1(url(), {domain, bitstring()} | {file, file:filename()}) ->
{ok, deleted}.
revoke_certificate1(CAUrl, {domain, Domain}) ->
case domain_certificate_exists(Domain) of
{Domain, Cert = #data_cert{pem=PemCert}} ->
ok = revoke_certificate2(CAUrl, PemCert),
ok = remove_certificate_persistent(Cert),
{ok, deleted}; {ok, deleted};
false -> false ->
{error, not_found} ?ERROR_MSG("Certificate for domain: ~p not found", [Domain]),
throw({error, not_found})
end;
revoke_certificate1(CAUrl, {file, File}) ->
case file:read_file(File) of
{ok, Pem} ->
ok = revoke_certificate2(CAUrl, Pem),
{ok, deleted};
{error, Reason} ->
?ERROR_MSG("Error: ~p reading pem certificate-key file: ~p", [Reason, File]),
throw({error, Reason})
end. end.
-spec revoke_certificate1(url(), data_cert()) -> ok. -spec revoke_certificate2(url(), data_cert()) -> ok.
revoke_certificate1(CAUrl, Cert = #data_cert{pem=PemEncodedCert}) -> revoke_certificate2(CAUrl, PemEncodedCert) ->
{Certificate, CertPrivateKey} = prepare_certificate_revoke(PemEncodedCert), {Certificate, CertPrivateKey} = prepare_certificate_revoke(PemEncodedCert),
{ok, Dirs, Nonce} = ejabberd_acme_comm:directory(CAUrl), {ok, Dirs, Nonce} = ejabberd_acme_comm:directory(CAUrl),
Req = [{<<"certificate">>, Certificate}], Req = [{<<"certificate">>, Certificate}],
{ok, [], Nonce1} = ejabberd_acme_comm:revoke_cert(Dirs, CertPrivateKey, Req, Nonce), {ok, [], Nonce1} = ejabberd_acme_comm:revoke_cert(Dirs, CertPrivateKey, Req, Nonce),
ok = remove_certificate_persistent(Cert),
ok. ok.
-spec parse_revoke_cert_argument(string()) -> {domain, bitstring()} | {file, file:filename()}.
parse_revoke_cert_argument([$f, $i, $l, $e, $:|File]) ->
{file, File};
parse_revoke_cert_argument([$d, $o, $m, $a, $i, $n, $: | Domain]) ->
{domain, list_to_bitstring(Domain)}.
-spec prepare_certificate_revoke(pem()) -> bitstring(). -spec prepare_certificate_revoke(pem()) -> bitstring().
prepare_certificate_revoke(PemEncodedCert) -> prepare_certificate_revoke(PemEncodedCert) ->
PemList = public_key:pem_decode(PemEncodedCert), PemList = public_key:pem_decode(PemEncodedCert),

View File

@ -270,8 +270,8 @@ get_commands_spec() ->
#ejabberd_commands{name = revoke_certificate, tags = [acme], #ejabberd_commands{name = revoke_certificate, tags = [acme],
desc = "Revokes the selected certificate", desc = "Revokes the selected certificate",
module = ?MODULE, function = revoke_certificate, module = ?MODULE, function = revoke_certificate,
args_desc = ["The domain of the certificate in question"], args_desc = ["The domain or file (in pem format) of the certificate in question {domain:Domain | file:File}"],
args = [{domain, string}], args = [{domain_or_file, string}],
result = {res, restuple}}, result = {res, restuple}},
#ejabberd_commands{name = import_piefxis, tags = [mnesia], #ejabberd_commands{name = import_piefxis, tags = [mnesia],
@ -602,8 +602,14 @@ list_certificates(Verbose) ->
{invalid_option, String} {invalid_option, String}
end. end.
revoke_certificate(Domain) -> revoke_certificate(DomainOrFile) ->
ejabberd_acme:revoke_certificate(Domain). case ejabberd_acme:is_valid_revoke_cert(DomainOrFile) of
true ->
ejabberd_acme:revoke_certificate(DomainOrFile);
false ->
String = io_lib:format("Bad argument: ~s", [DomainOrFile]),
{invalid_argument, String}
end.
%%% %%%
%%% Purge DB %%% Purge DB