aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_acme.erl
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2017-11-17 11:59:40 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2017-11-17 11:59:40 +0300
commitce982266037f1a8872e6f6be936327d764132a93 (patch)
tree6f5594750a2cbac3278365db2364547a6598a35a /src/ejabberd_acme.erl
parentFix sed invocation that was incompatible with FreeBSD sed (diff)
Make ACME code working with ejabberd_pkix
Diffstat (limited to '')
-rw-r--r--src/ejabberd_acme.erl130
1 files changed, 67 insertions, 63 deletions
diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl
index e9636f1e5..e0a881d25 100644
--- a/src/ejabberd_acme.erl
+++ b/src/ejabberd_acme.erl
@@ -1,21 +1,23 @@
-module (ejabberd_acme).
+-behaviour(gen_server).
+-behavior(ejabberd_config).
--export([%% Ejabberdctl Commands
- get_certificates/1,
+%% ejabberdctl commands
+-export([get_certificates/1,
renew_certificates/0,
list_certificates/1,
- revoke_certificate/1,
- %% Command Options Validity
- is_valid_account_opt/1,
+ revoke_certificate/1]).
+%% Command Options Validity
+-export([is_valid_account_opt/1,
is_valid_verbose_opt/1,
is_valid_domain_opt/1,
- is_valid_revoke_cert/1,
- %% Called by ejabberd_pkix
- certificate_exists/1,
- %% Key Related
- generate_key/0,
- to_public/1
- ]).
+ is_valid_revoke_cert/1]).
+%% Key Related
+-export([generate_key/0, to_public/1]).
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+-export([start_link/0, opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -24,16 +26,44 @@
-include("ejabberd_acme.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([opt_type/1]).
+-define(DEFAULT_CONFIG_CONTACT, <<"mailto:example-admin@example.com">>).
+-define(DEFAULT_CONFIG_CA_URL, "https://acme-v01.api.letsencrypt.org").
--behavior(ejabberd_config).
+-record(state, {}).
-%%
-%% Default ACME configuration
-%%
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
--define(DEFAULT_CONFIG_CONTACT, <<"mailto:example-admin@example.com">>).
--define(DEFAULT_CONFIG_CA_URL, "https://acme-v01.api.letsencrypt.org").
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+init([]) ->
+ case filelib:ensure_dir(filename:join(acme_certs_dir(), "foo")) of
+ ok ->
+ register_certfiles(),
+ {ok, #state{}};
+ {error, Why} ->
+ ?CRITICAL_MSG("Failed to create directory ~s: ~s",
+ [acme_certs_dir(), file:format_error(Why)]),
+ {stop, Why}
+ end.
+
+handle_call(_Request, _From, State) ->
+ {stop, {unexpected_call, _Request, _From}, State}.
+
+handle_cast(_Msg, State) ->
+ ?WARNING_MSG("unexpected cast: ~p", [_Msg]),
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ ?WARNING_MSG("unexpected info: ~p", [_Info]),
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -591,26 +621,6 @@ domain_certificate_exists(Domain) ->
Certs = read_certificates_persistent(),
lists:keyfind(Domain, 1, Certs).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% Called by ejabberd_pkix to check
-%% if a certificate exists for a
-%% specific host
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
--spec certificate_exists(bitstring()) -> {true, file:filename()} | false.
-certificate_exists(Host) ->
- Certificates = read_certificates_persistent(),
- case lists:keyfind(Host, 1 , Certificates) of
- false ->
- false;
- {Host, #data_cert{path=Path}} ->
- {true, Path}
- end.
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Certificate Request Functions
@@ -951,8 +961,8 @@ data_remove_certificate(Data, _DataCert = #data_cert{domain=Domain}) ->
-spec persistent_file() -> file:filename().
persistent_file() ->
- MnesiaDir = mnesia:system_info(directory),
- filename:join(MnesiaDir, "acme.DAT").
+ AcmeDir = acme_certs_dir(),
+ filename:join(AcmeDir, "acme.DAT").
%% The persistent file should be read and written only by its owner
-spec persistent_file_mode() -> 384.
@@ -1032,9 +1042,9 @@ save_certificate({error, _, _} = Error) ->
Error;
save_certificate({ok, DomainName, Cert}) ->
try
- CertDir = get_config_cert_dir(),
+ CertDir = acme_certs_dir(),
DomainString = bitstring_to_list(DomainName),
- CertificateFile = filename:join([CertDir, DomainString ++ "_cert.pem"]),
+ CertificateFile = filename:join([CertDir, DomainString ++ ".pem"]),
%% TODO: At some point do the following using a Transaction so
%% that there is no certificate saved if it cannot be added in
%% certificate persistent storage
@@ -1045,7 +1055,7 @@ save_certificate({ok, DomainName, Cert}) ->
path = CertificateFile
},
add_certificate_persistent(DataCert),
- ejabberd_pkix:add_certfile(CertificateFile),
+ ok = ejabberd_pkix:add_certfile(CertificateFile),
{ok, DomainName, saved}
catch
throw:Throw ->
@@ -1064,6 +1074,15 @@ save_renewed_certificate({ok, _, no_expire} = Cert) ->
save_renewed_certificate({ok, DomainName, Cert}) ->
save_certificate({ok, DomainName, Cert}).
+-spec register_certfiles() -> ok.
+register_certfiles() ->
+ Dir = acme_certs_dir(),
+ Paths = filelib:wildcard(filename:join(Dir, "*.pem")),
+ lists:foreach(
+ fun(Path) ->
+ ejabberd_pkix:add_certfile(Path)
+ end, Paths).
+
-spec write_cert(file:filename(), binary(), bitstring()) -> {ok, bitstring(), saved}.
write_cert(CertificateFile, Cert, DomainName) ->
case file:write_file(CertificateFile, Cert) of
@@ -1121,17 +1140,9 @@ get_config_hosts() ->
Hosts
end.
--spec get_config_cert_dir() -> file:filename().
-get_config_cert_dir() ->
- case ejabberd_config:get_option(cert_dir, undefined) of
- undefined ->
- ?WARNING_MSG("No cert_dir configuration has been specified in configuration", []),
- mnesia:system_info(directory);
- %% throw({error, configuration});
- CertDir ->
- CertDir
- end.
-
+-spec acme_certs_dir() -> file:filename().
+acme_certs_dir() ->
+ filename:join(ejabberd_pkix:certs_dir(), "acme").
generate_key() ->
jose_jwk:generate_key({ec, secp256r1}).
@@ -1151,16 +1162,9 @@ parse_acme_opt({ca_url, CaUrl}) when is_bitstring(CaUrl) ->
parse_acme_opt({contact, Contact}) when is_bitstring(Contact) ->
{contact, Contact}.
-parse_cert_dir_opt(Opt) when is_bitstring(Opt) ->
- true = filelib:is_dir(Opt),
- Opt.
-
-spec opt_type(acme) -> fun((acme_config()) -> (acme_config()));
- (cert_dir) -> fun((bitstring()) -> (bitstring()));
(atom()) -> [atom()].
opt_type(acme) ->
fun parse_acme_opts/1;
-opt_type(cert_dir) ->
- fun parse_cert_dir_opt/1;
opt_type(_) ->
- [acme, cert_dir].
+ [acme].