1%% This Source Code Form is subject to the terms of the Mozilla Public
2%% License, v. 2.0. If a copy of the MPL was not distributed with this
3%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4%%
5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates.  All rights reserved.
6%%
7
8-module(rabbit_trust_store_app).
9-behaviour(application).
10-export([change_SSL_options/0]).
11-export([revert_SSL_options/0]).
12-export([start/2, stop/1]).
13
14-rabbit_boot_step({rabbit_trust_store, [
15    {description, "Overrides TLS options in take RabbitMQ trust store into account"},
16    {mfa, {?MODULE, change_SSL_options, []}},
17    {cleanup, {?MODULE, revert_SSL_options, []}},
18    %% {requires, ...},
19    {enables, networking}]}).
20
21change_SSL_options() ->
22    After = case application:get_env(rabbit, ssl_options) of
23        undefined ->
24            Before = [],
25            edit(Before);
26        {ok, Before} when is_list(Before) ->
27            ok = application:set_env(rabbit, initial_SSL_options, Before),
28            edit(Before)
29    end,
30    ok = application:set_env(rabbit,
31        ssl_options, After).
32
33revert_SSL_options() ->
34    {ok, Cfg} = application:get_env(rabbit, initial_SSL_options),
35    ok = application:set_env(rabbit, ssl_options, Cfg).
36
37start(normal, _) ->
38    rabbit_trust_store_sup:start_link().
39
40stop(_) ->
41    ok.
42
43
44%% Ancillary & Constants
45
46edit(Options) ->
47    case proplists:get_value(verify_fun, Options) of
48        undefined ->
49            ok;
50        Val       ->
51            rabbit_log:warning("RabbitMQ trust store plugin is used "
52                               "and the verify_fun TLS option is set: ~p. "
53                               "It will be overwritten by the plugin.", [Val]),
54            ok
55    end,
56    %% Only enter those options neccessary for this application.
57    lists:keymerge(1, required_options(),
58        [{verify_fun, {delegate(), continue}},
59         {partial_chain, fun partial_chain/1} | Options]).
60
61delegate() -> fun rabbit_trust_store:whitelisted/3.
62
63partial_chain(Chain) ->
64    % special handling of clients that present a chain rather than just a peer cert.
65    case lists:reverse(Chain) of
66        [PeerDer, Ca | _] ->
67            Peer = public_key:pkix_decode_cert(PeerDer, otp),
68            % If the Peer is whitelisted make it's immediate Authority a trusted one.
69            % This means the peer will automatically be validated.
70            case rabbit_trust_store:is_whitelisted(Peer) of
71                true -> {trusted_ca, Ca};
72                false -> unknown_ca
73            end;
74        _ -> unknown_ca
75    end.
76
77required_options() ->
78    [{verify, verify_peer}, {fail_if_no_peer_cert, true}].
79