./ct_report/coverage/just_tls.COVER.html

1 %%%=============================================================================
2 %%% @copyright (C) 1999-2018, Erlang Solutions Ltd
3 %%% @author Denys Gonchar <denys.gonchar@erlang-solutions.com>
4 %%% @doc TLS backend based on standard Erlang's SSL application
5 %%% @end
6 %%%=============================================================================
7 -module(just_tls).
8 -copyright("2018, Erlang Solutions Ltd.").
9 -author('denys.gonchar@erlang-solutions.com').
10
11 -behaviour(mongoose_tls).
12
13 -include_lib("public_key/include/public_key.hrl").
14
15 -record(tls_socket, {verify_results = [],
16 ssl_socket
17 }).
18
19 -type tls_socket() :: #tls_socket{}.
20 -export_type([tls_socket/0]).
21
22 % mongoose_tls behaviour
23 -export([tcp_to_tls/2,
24 send/2,
25 recv_data/2,
26 controlling_process/2,
27 sockname/1,
28 peername/1,
29 setopts/2,
30 get_peer_certificate/1,
31 close/1]).
32
33 % API
34 -export([make_ssl_opts/1, make_cowboy_ssl_opts/1]).
35
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 %% APIs
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39
40 -spec tcp_to_tls(inet:socket(), mongoose_tls:options()) ->
41 {ok, mongoose_tls:tls_socket()} | {error, any()}.
42 tcp_to_tls(TCPSocket, Options) ->
43 519 inet:setopts(TCPSocket, [{active, false}]),
44 519 {Ref, SSLOpts} = format_opts_with_ref(Options, false),
45 519 Ret = case Options of
46 #{connect := true} ->
47 % Currently unused as ejabberd_s2s_out uses fast_tls,
48 % and outgoing pools use Erlang SSL directly
49
:-(
ssl:connect(TCPSocket, SSLOpts);
50 #{} ->
51 519 ssl:handshake(TCPSocket, SSLOpts, 5000)
52 end,
53 519 VerifyResults = receive_verify_results(Ref),
54 519 case Ret of
55 {ok, SSLSocket} ->
56 78 {ok, #tls_socket{ssl_socket = SSLSocket, verify_results = VerifyResults}};
57 441 _ -> Ret
58 end.
59
60 %% -callback send(tls_socket(), binary()) -> ok | {error, any()}.
61 263 send(#tls_socket{ssl_socket = SSLSocket}, Packet) -> ssl:send(SSLSocket, Packet).
62
63 %% -callback recv_data(tls_socket(), binary()) -> {ok, binary()} | {error, any()}.
64 recv_data(_, <<"">>) ->
65 %% such call is required for fast_tls to accomplish
66 %% tls handshake, for just_tls we can ignore it
67
:-(
{ok, <<"">>};
68 recv_data(#tls_socket{ssl_socket = SSLSocket}, Data1) ->
69
:-(
case ssl:recv(SSLSocket, 0, 0) of
70
:-(
{ok, Data2} -> {ok, <<Data1/binary, Data2/binary>>};
71
:-(
_ -> {ok, Data1}
72 end.
73
74 %% -callback controlling_process(tls_socket(), pid()) -> ok | {error, any()}.
75 controlling_process(#tls_socket{ssl_socket = SSLSocket}, Pid) ->
76
:-(
ssl:controlling_process(SSLSocket, Pid).
77
78
79 %% -callback sockname(tls_socket()) -> {ok, {inet:ip_address(), inet:port_number()}} |
80 %% {error, any()}.
81
:-(
sockname(#tls_socket{ssl_socket = SSLSocket}) -> ssl:sockname(SSLSocket).
82
83
84 %% -callback peername(tls_socket()) -> {ok, {inet:ip_address(), inet:port_number()}} |
85 %% {error, any()}.
86
:-(
peername(#tls_socket{ssl_socket = SSLSocket}) -> ssl:peername(SSLSocket).
87
88
89 %% -callback setopts(tls_socket(), Opts::list()) -> ok | {error, any()}.
90 289 setopts(#tls_socket{ssl_socket = SSLSocket}, Opts) -> ssl:setopts(SSLSocket, Opts).
91
92
93 %% -callback get_peer_certificate(tls_socket()) -> {ok, Cert::any()} |
94 %% {bad_cert, bitstring()} |
95 %% no_peer_cert.
96 get_peer_certificate(#tls_socket{verify_results = [], ssl_socket = SSLSocket}) ->
97 117 case ssl:peercert(SSLSocket) of
98 {ok, PeerCert} ->
99 111 Cert = public_key:pkix_decode_cert(PeerCert, plain),
100 111 {ok, Cert};
101 6 _ -> no_peer_cert
102 end;
103 get_peer_certificate(#tls_socket{verify_results = [Err | _]}) ->
104 3 {bad_cert, error_to_list(Err)}.
105
106 %% -callback close(tls_socket()) -> ok.
107 78 close(#tls_socket{ssl_socket = SSLSocket}) -> ssl:close(SSLSocket).
108
109 %% @doc Prepare SSL options for direct use of ssl:connect/2 or ssl:handshake/2
110 %% The `disconnect_on_failure' option is not supported
111 -spec make_ssl_opts(mongoose_tls:options()) -> [ssl:tls_option()].
112 make_ssl_opts(Opts) ->
113 276 {dummy_ref, SSLOpts} = format_opts_with_ref(Opts, false),
114 276 SSLOpts.
115
116 -spec make_cowboy_ssl_opts(mongoose_tls:options()) -> [ssl:tls_option()].
117 make_cowboy_ssl_opts(Opts) ->
118 115 FailIfNoPeerCert = fail_if_no_peer_cert_opt(Opts),
119 115 {dummy_ref, SSLOpts} = format_opts_with_ref(Opts, FailIfNoPeerCert),
120 115 SSLOpts.
121
122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123 %% local functions
124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125
126 format_opts_with_ref(Opts, FailIfNoPeerCert) ->
127 910 Verify = verify_opt(Opts),
128 910 {Ref, VerifyFun} = verify_fun_opt(Opts),
129 910 SNIOpts = sni_opts(Opts),
130 910 SSLOpts = maps:to_list(maps:with(ssl_option_keys(), Opts)),
131 910 {Ref, [{fail_if_no_peer_cert, FailIfNoPeerCert}, {verify, Verify}, {verify_fun, VerifyFun}] ++
132 SNIOpts ++ SSLOpts}.
133
134 ssl_option_keys() ->
135 910 [certfile, cacertfile, ciphers, keyfile, password, versions, dhfile].
136
137 sni_opts(#{server_name_indication := SNIOpts}) ->
138 276 process_sni_opts(SNIOpts);
139 sni_opts(#{}) ->
140 634 [].
141
142 process_sni_opts(#{enabled := false}) ->
143
:-(
[{server_name_indication, disable}];
144 process_sni_opts(#{enabled := true, host := SNIHost, protocol := https}) ->
145
:-(
[{server_name_indication, SNIHost},
146 {customize_hostname_check, [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]}];
147 process_sni_opts(#{enabled := true, host := SNIHost, protocol := default}) ->
148
:-(
[{server_name_indication, SNIHost}];
149 process_sni_opts(#{enabled := true}) ->
150 276 [].
151
152 error_to_list(_Error) ->
153 %TODO: implement later if needed
154 3 "verify_fun failed".
155
156 78 verify_opt(#{verify_mode := none}) -> verify_none;
157 832 verify_opt(#{}) -> verify_peer.
158
159 25 fail_if_no_peer_cert_opt(#{verify_mode := peer}) -> true;
160 13 fail_if_no_peer_cert_opt(#{verify_mode := selfsigned_peer}) -> true;
161 77 fail_if_no_peer_cert_opt(#{}) -> false.
162
163 %% This function translates TLS options to the function
164 %% which will later be used when TCP socket is upgraded to TLS
165 %% `verify_mode` is one of the following:
166 %% none - no validation of the clients certificate - any cert is accepted.
167 %% peer - standard verification of the certificate.
168 %% selfsigned_peer - the same as peer but also accepts self-signed certificates
169 %% `disconnect_on_failure` is a boolean parameter:
170 %% true - drop connection if certificate verification failed
171 %% false - connect anyway, but later return {bad_cert,Error}
172 %% on certificate verification (the same as fast_tls do).
173 verify_fun_opt(#{verify_mode := Mode, disconnect_on_failure := false}) ->
174 20 Ref = erlang:make_ref(),
175 20 {Ref, verify_fun(Ref, Mode)};
176 verify_fun_opt(#{verify_mode := Mode}) ->
177 890 {dummy_ref, verify_fun(Mode)}.
178
179 verify_fun(Ref, Mode) when is_reference(Ref) ->
180 20 {Fun, State} = verify_fun(Mode),
181 20 {verify_fun_wrapper(Ref, Fun), State}.
182
183 verify_fun_wrapper(Ref, Fun) when is_reference(Ref), is_function(Fun, 3) ->
184 20 Pid = self(),
185 20 fun(Cert, Event, UserState) ->
186 91 Ret = Fun(Cert, Event, UserState),
187 91 case {Ret, Event} of
188 18 {{valid, _}, _} -> Ret;
189 {{unknown, NewState}, {extension, #'Extension'{critical = true}}} ->
190
:-(
send_verification_failure(Pid, Ref, unknown_critical_extension),
191
:-(
{valid, NewState};
192 72 {{unknown, _}, {extension, _}} -> Ret;
193 {_, _} -> %% {fail,Reason} = Ret
194 1 send_verification_failure(Pid, Ref, Ret),
195 1 {valid, UserState} %return the last valid user state
196 end
197 end.
198
199 verify_fun(peer) ->
200 799 {fun
201 11 (_, {bad_cert, _} = R, _) -> {fail, R};
202 1316 (_, {extension, _}, S) -> {unknown, S};
203
:-(
(_, valid, S) -> {valid, S};
204 329 (_, valid_peer, S) -> {valid, S}
205 end, []};
206 verify_fun(selfsigned_peer) ->
207 33 {fun
208 21 (_, {bad_cert, selfsigned_peer}, S) -> {valid, S};
209 1 (_, {bad_cert, _} = R, _) -> {fail, R};
210 12 (_, {extension, _}, S) -> {unknown, S};
211
:-(
(_, valid, S) -> {valid, S};
212 3 (_, valid_peer, S) -> {valid, S}
213 end, []};
214 verify_fun(none) ->
215 78 {fun(_, _, S) -> {valid, S} end, []}.
216
217
218 send_verification_failure(Pid, Ref, Reason) ->
219 1 Pid ! {cert_verification_failure, Ref, Reason}.
220
221 499 receive_verify_results(dummy_ref) -> [];
222 20 receive_verify_results(Ref) -> receive_verify_results(Ref, []).
223
224 receive_verify_results(Ref, Acc) ->
225 21 receive
226 {cert_verification_failure, Ref, Reason} ->
227 1 receive_verify_results(Ref, [Reason | Acc])
228 after 0 ->
229 20 lists:reverse(Acc)
230 end.
Line Hits Source