Skip to content

Commit f9d841d

Browse files
committed
public_key: Fix crash when no OS CA certificates are found
When no OS CA certificate bundle can be found, for example on a minimal system where none of the default paths exist, or when the configured path is an empty directory, pubkey_os_cacerts:get/0 is supposed to raise a descriptive {failed_load_cacerts, Reason} error. Instead it crashed with a function_clause error in the internal error conversion helper, masking the real problem: ** exception error: no function clause matching pubkey_os_cacerts:conv_error_reason(no_cacerts_found) This can also be triggered indirectly through httpc's default verify_peer setup (httpc:ssl_verify_host_options/1). format_error/2 had the same gap, so error formatting would have crashed in the same way instead of producing a message. Add the missing no_cacerts_found clauses so that the intended {failed_load_cacerts, no_cacerts_found} error is raised and formatted properly, and extend the cacerts_load test case to cover this.
1 parent 7a0217a commit f9d841d

2 files changed

Lines changed: 22 additions & 0 deletions

File tree

lib/public_key/src/pubkey_os_cacerts.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ load_nif() ->
299299
%%%
300300

301301
conv_error_reason(enoent) -> enoent;
302+
conv_error_reason(no_cacerts_found) -> no_cacerts_found;
302303
conv_error_reason({enotsup, _OS}) -> enotsup;
303304
conv_error_reason({eopnotsupp, _Reason}) -> eopnotsupp;
304305
conv_error_reason({eopnotsupp, _Status, _Acc}) -> eopnotsupp.
@@ -316,6 +317,8 @@ format_error(Reason, [{_M, _F, _As, Info} | _]) ->
316317
Message = case Cause of
317318
enoent ->
318319
"operating system CA bundle could not be located";
320+
no_cacerts_found ->
321+
"no CA certificates were found on the system";
319322
{enotsup, OS} ->
320323
io_lib:format("operating system ~p is not supported", [OS]);
321324
{eopnotsupp, SubReason} ->

lib/public_key/test/public_key_SUITE.erl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2250,6 +2250,25 @@ cacerts_load(Config) ->
22502250
Datadir = proplists:get_value(data_dir, Config),
22512251
{error, enoent} = public_key:cacerts_load("/dummy.file"),
22522252

2253+
%% When no CA certificates can be found (missing bundle or one
2254+
%% without any CA certificates) this should surface as a
2255+
%% {failed_load_cacerts, no_cacerts_found} error through cacerts_get()
2256+
%% and be formattable, not crash the error conversion
2257+
_ = public_key:cacerts_clear(),
2258+
EmptyDir = filename:join(proplists:get_value(priv_dir, Config),
2259+
"empty_cacerts_dir"),
2260+
ok = filelib:ensure_path(EmptyDir),
2261+
{error, no_cacerts_found} = pubkey_os_cacerts:load([EmptyDir]),
2262+
application:set_env(public_key, cacerts_path, EmptyDir),
2263+
try public_key:cacerts_get() of
2264+
_ -> ct:fail(no_cacerts_found_not_raised)
2265+
catch
2266+
error:{failed_load_cacerts, no_cacerts_found} = Error:ST ->
2267+
#{general := _, reason := _} =
2268+
pubkey_os_cacerts:format_error(Error, ST)
2269+
end,
2270+
application:unset_env(public_key, cacerts_path),
2271+
22532272
%% White box testing of paths loading
22542273
%% TestDirs
22552274
ok = pubkey_os_cacerts:load([filename:join(Datadir, "non_existing_dir"),

0 commit comments

Comments
 (0)