mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Introduce option 'ca_file'
The option is supposed to be used as a fallback for certificates validation. For instance, the option will be used if 's2s_cafile' option is not set. The value should be a path to a file containing CA certificate(s) in PEM format, e.g.: ca_file: "/etc/ssl/certs/ca-bundle.pem"
This commit is contained in:
parent
242b3d3968
commit
783ebd1080
@ -28,7 +28,7 @@
|
|||||||
%% API
|
%% API
|
||||||
-export([start_link/0, add_certfile/1, format_error/1, opt_type/1,
|
-export([start_link/0, add_certfile/1, format_error/1, opt_type/1,
|
||||||
get_certfile/1, try_certfile/1, route_registered/1,
|
get_certfile/1, try_certfile/1, route_registered/1,
|
||||||
config_reloaded/0, certs_dir/0]).
|
config_reloaded/0, certs_dir/0, ca_file/0]).
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
@ -146,7 +146,11 @@ config_reloaded() ->
|
|||||||
gen_server:call(?MODULE, config_reloaded, 60000).
|
gen_server:call(?MODULE, config_reloaded, 60000).
|
||||||
|
|
||||||
opt_type(ca_path) ->
|
opt_type(ca_path) ->
|
||||||
fun(Path) -> iolist_to_binary(Path) end;
|
fun(Path) -> binary_to_list(Path) end;
|
||||||
|
opt_type(ca_file) ->
|
||||||
|
fun(Path) ->
|
||||||
|
binary_to_list(misc:try_read_file(Path))
|
||||||
|
end;
|
||||||
opt_type(certfiles) ->
|
opt_type(certfiles) ->
|
||||||
fun(CertList) ->
|
fun(CertList) ->
|
||||||
[binary_to_list(Path) || Path <- CertList]
|
[binary_to_list(Path) || Path <- CertList]
|
||||||
@ -157,7 +161,7 @@ opt_type(O) when O == c2s_certfile; O == s2s_certfile; O == domain_certfile ->
|
|||||||
misc:try_read_file(File)
|
misc:try_read_file(File)
|
||||||
end;
|
end;
|
||||||
opt_type(_) ->
|
opt_type(_) ->
|
||||||
[ca_path, certfiles, c2s_certfile, s2s_certfile, domain_certfile].
|
[ca_path, ca_file, certfiles, c2s_certfile, s2s_certfile, domain_certfile].
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
@ -175,7 +179,7 @@ init([]) ->
|
|||||||
erlang:function_exported(
|
erlang:function_exported(
|
||||||
public_key, short_name_hash, 1)
|
public_key, short_name_hash, 1)
|
||||||
end,
|
end,
|
||||||
if Validate -> check_ca_dir();
|
if Validate -> check_ca();
|
||||||
true -> ok
|
true -> ok
|
||||||
end,
|
end,
|
||||||
State = #state{validate = Validate, notify = Notify},
|
State = #state{validate = Validate, notify = Notify},
|
||||||
@ -524,6 +528,10 @@ validate_path([Cert|_] = Certs) ->
|
|||||||
ca_dir() ->
|
ca_dir() ->
|
||||||
ejabberd_config:get_option(ca_path, "/etc/ssl/certs").
|
ejabberd_config:get_option(ca_path, "/etc/ssl/certs").
|
||||||
|
|
||||||
|
-spec ca_file() -> string() | undefined.
|
||||||
|
ca_file() ->
|
||||||
|
ejabberd_config:get_option(ca_file).
|
||||||
|
|
||||||
-spec certs_dir() -> string().
|
-spec certs_dir() -> string().
|
||||||
certs_dir() ->
|
certs_dir() ->
|
||||||
MnesiaDir = mnesia:system_info(directory),
|
MnesiaDir = mnesia:system_info(directory),
|
||||||
@ -543,11 +551,12 @@ clean_dir(Dir) ->
|
|||||||
end
|
end
|
||||||
end, Files).
|
end, Files).
|
||||||
|
|
||||||
-spec check_ca_dir() -> ok.
|
-spec check_ca() -> ok.
|
||||||
check_ca_dir() ->
|
check_ca() ->
|
||||||
|
CAFile = ca_file(),
|
||||||
case wildcard(filename:join(ca_dir(), "*.0")) of
|
case wildcard(filename:join(ca_dir(), "*.0")) of
|
||||||
[] ->
|
[] when CAFile == undefined ->
|
||||||
Hint = "configuring 'ca_path' option might help",
|
Hint = "configuring 'ca_path' or 'ca_file' options might help",
|
||||||
case file:list_dir(ca_dir()) of
|
case file:list_dir(ca_dir()) of
|
||||||
{error, Why} ->
|
{error, Why} ->
|
||||||
?WARNING_MSG("failed to read CA directory ~s: ~s; ~s",
|
?WARNING_MSG("failed to read CA directory ~s: ~s; ~s",
|
||||||
@ -563,10 +572,23 @@ check_ca_dir() ->
|
|||||||
|
|
||||||
-spec find_local_issuer(cert()) -> {ok, cert()} | {error, {bad_cert, unknown_ca}}.
|
-spec find_local_issuer(cert()) -> {ok, cert()} | {error, {bad_cert, unknown_ca}}.
|
||||||
find_local_issuer(Cert) ->
|
find_local_issuer(Cert) ->
|
||||||
|
case find_issuer_in_dir(Cert, ca_dir()) of
|
||||||
|
{ok, IssuerCert} ->
|
||||||
|
{ok, IssuerCert};
|
||||||
|
{error, _} = Err ->
|
||||||
|
case ca_file() of
|
||||||
|
undefined -> Err;
|
||||||
|
CAFile -> find_issuer_in_file(Cert, CAFile)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec find_issuer_in_dir(cert(), file:filename_all())
|
||||||
|
-> {ok, cert()} | {error, {bad_cert, unknown_ca}}.
|
||||||
|
find_issuer_in_dir(Cert, CADir) ->
|
||||||
{ok, {_, IssuerID}} = public_key:pkix_issuer_id(Cert, self),
|
{ok, {_, IssuerID}} = public_key:pkix_issuer_id(Cert, self),
|
||||||
Hash = short_name_hash(IssuerID),
|
Hash = short_name_hash(IssuerID),
|
||||||
filelib:fold_files(
|
filelib:fold_files(
|
||||||
ca_dir(), Hash ++ "\\.[0-9]+", false,
|
CADir, Hash ++ "\\.[0-9]+", false,
|
||||||
fun(_, {ok, IssuerCert}) ->
|
fun(_, {ok, IssuerCert}) ->
|
||||||
{ok, IssuerCert};
|
{ok, IssuerCert};
|
||||||
(CertFile, Acc) ->
|
(CertFile, Acc) ->
|
||||||
@ -586,6 +608,29 @@ find_local_issuer(Cert) ->
|
|||||||
end
|
end
|
||||||
end, {error, {bad_cert, unknown_ca}}).
|
end, {error, {bad_cert, unknown_ca}}).
|
||||||
|
|
||||||
|
-spec find_issuer_in_file(cert(), file:filename_all() | undefined)
|
||||||
|
-> {ok, cert()} | {error, {bad_cert, unknown_ca}}.
|
||||||
|
find_issuer_in_file(_Cert, undefined) ->
|
||||||
|
{error, {bad_cert, unknown_ca}};
|
||||||
|
find_issuer_in_file(Cert, CAFile) ->
|
||||||
|
try
|
||||||
|
{ok, Data} = file:read_file(CAFile),
|
||||||
|
{ok, IssuerCerts, _} = pem_decode(Data),
|
||||||
|
lists:foldl(
|
||||||
|
fun(_, {ok, _} = Res) ->
|
||||||
|
Res;
|
||||||
|
(IssuerCert, Err) ->
|
||||||
|
case public_key:pkix_is_issuer(Cert, IssuerCert) of
|
||||||
|
true -> {ok, IssuerCert};
|
||||||
|
false -> Err
|
||||||
|
end
|
||||||
|
end, {error, {bad_cert, unknown_ca}}, IssuerCerts)
|
||||||
|
catch _:{badmatch, {error, Why}} ->
|
||||||
|
?ERROR_MSG("failed to read CA certificates from \"~s\": ~s",
|
||||||
|
[CAFile, format_error(Why)]),
|
||||||
|
{error, {bad_cert, unknown_ca}}
|
||||||
|
end.
|
||||||
|
|
||||||
-spec match_cert_keys([{path, [cert()]}], [priv_key()])
|
-spec match_cert_keys([{path, [cert()]}], [priv_key()])
|
||||||
-> {ok, [{cert(), priv_key()}]} | {error, {bad_cert, missing_priv_key}}.
|
-> {ok, [{cert(), priv_key()}]} | {error, {bad_cert, missing_priv_key}}.
|
||||||
match_cert_keys(CertPaths, PrivKeys) ->
|
match_cert_keys(CertPaths, PrivKeys) ->
|
||||||
|
@ -222,8 +222,7 @@ tls_options(LServer, DefaultOpts) ->
|
|||||||
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
|
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
|
||||||
{dhfile, DHFile})
|
{dhfile, DHFile})
|
||||||
end,
|
end,
|
||||||
TLSOpts5 = case ejabberd_config:get_option(
|
TLSOpts5 = case get_cafile(LServer) of
|
||||||
{s2s_cafile, LServer}) of
|
|
||||||
undefined -> TLSOpts4;
|
undefined -> TLSOpts4;
|
||||||
CAFile -> lists:keystore(cafile, 1, TLSOpts4,
|
CAFile -> lists:keystore(cafile, 1, TLSOpts4,
|
||||||
{cafile, CAFile})
|
{cafile, CAFile})
|
||||||
@ -267,7 +266,7 @@ queue_type(LServer) ->
|
|||||||
{s2s_queue_type, LServer},
|
{s2s_queue_type, LServer},
|
||||||
ejabberd_config:default_queue_type(LServer)).
|
ejabberd_config:default_queue_type(LServer)).
|
||||||
|
|
||||||
-spec get_certfile(binary()) -> file:filename_all().
|
-spec get_certfile(binary()) -> file:filename_all() | undefined.
|
||||||
get_certfile(LServer) ->
|
get_certfile(LServer) ->
|
||||||
case ejabberd_pkix:get_certfile(LServer) of
|
case ejabberd_pkix:get_certfile(LServer) of
|
||||||
{ok, CertFile} ->
|
{ok, CertFile} ->
|
||||||
@ -278,6 +277,15 @@ get_certfile(LServer) ->
|
|||||||
ejabberd_config:get_option({s2s_certfile, LServer}))
|
ejabberd_config:get_option({s2s_certfile, LServer}))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec get_cafile(binary()) -> file:filename_all() | undefined.
|
||||||
|
get_cafile(LServer) ->
|
||||||
|
case ejabberd_config:get_option({s2s_cafile, LServer}) of
|
||||||
|
undefined ->
|
||||||
|
ejabberd_pkix:ca_file();
|
||||||
|
File ->
|
||||||
|
File
|
||||||
|
end.
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
Loading…
Reference in New Issue
Block a user