diff options
author | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2017-11-17 11:59:40 +0300 |
---|---|---|
committer | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2017-11-17 11:59:40 +0300 |
commit | ce982266037f1a8872e6f6be936327d764132a93 (patch) | |
tree | 6f5594750a2cbac3278365db2364547a6598a35a /src | |
parent | Fix 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.erl | 130 | ||||
-rw-r--r-- | src/ejabberd_pkix.erl | 24 | ||||
-rw-r--r-- | src/ejabberd_sup.erl | 3 |
3 files changed, 83 insertions, 74 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]. diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl index 68b8226c8..2fad9ea4c 100644 --- a/src/ejabberd_pkix.erl +++ b/src/ejabberd_pkix.erl @@ -28,7 +28,7 @@ %% API -export([start_link/0, add_certfile/1, format_error/1, opt_type/1, get_certfile/1, try_certfile/1, route_registered/1, - config_reloaded/0]). + config_reloaded/0, certs_dir/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -190,8 +190,17 @@ init([]) -> end. handle_call({add_certfile, Path}, _, State) -> - {Result, NewState} = add_certfile(Path, State), - {reply, Result, NewState}; + case add_certfile(Path, State) of + {ok, State1} -> + case build_chain_and_check(State1) of + {ok, State2} -> + {reply, ok, State2}; + Err -> + {reply, Err, State} + end; + {Err, State1} -> + {reply, Err, State1} + end; handle_call({route_registered, Host}, _, State) -> case add_certfiles(Host, State) of {ok, NewState} -> @@ -301,14 +310,7 @@ add_certfiles(Host, State) -> NewAccState end end, State, certfiles_from_config_options()), - State2 = case ejabberd_acme:certificate_exists(Host) of - {true, Path} -> - {_, State3} = add_certfile(Path, State1), - State3; - false -> - State1 - end, - if State /= State2 -> + if State /= State1 -> case build_chain_and_check(State1) of ok -> {ok, State1}; {error, _} = Err -> Err diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl index 463e7ea29..dbbc4d5e4 100644 --- a/src/ejabberd_sup.erl +++ b/src/ejabberd_sup.erl @@ -156,6 +156,8 @@ init([]) -> permanent, 5000, worker, [cyrsasl]}, PKIX = {ejabberd_pkix, {ejabberd_pkix, start_link, []}, permanent, 5000, worker, [ejabberd_pkix]}, + ACME = {ejabberd_acme, {ejabberd_acme, start_link, []}, + permanent, 5000, worker, [ejabberd_acme]}, IQ = {ejabberd_iq, {ejabberd_iq, start_link, []}, permanent, 5000, worker, [ejabberd_iq]}, {ok, {{one_for_one, 10, 1}, @@ -168,6 +170,7 @@ init([]) -> Commands, Admin, PKIX, + ACME, Listener, SystemMonitor, S2S, |