./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 368 inet:setopts(TCPSocket, [{active, false}]),
44 368 {Ref, SSLOpts} = format_opts_with_ref(Options, false),
45 368 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 368 ssl:handshake(TCPSocket, SSLOpts, 5000)
52 end,
53 368 VerifyResults = receive_verify_results(Ref),
54 368 case Ret of
55 {ok, SSLSocket} ->
56 81 {ok, #tls_socket{ssl_socket = SSLSocket, verify_results = VerifyResults}};
57 287 _ -> Ret
58 end.
59
60 %% -callback send(tls_socket(), binary()) -> ok | {error, any()}.
61 285 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 307 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 129 case ssl:peercert(SSLSocket) of
98 {ok, PeerCert} ->
99 123 Cert = public_key:pkix_decode_cert(PeerCert, plain),
100 123 {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 81 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 1011 {dummy_ref, SSLOpts} = format_opts_with_ref(Opts, false),
114 1011 SSLOpts.
115
116 -spec make_cowboy_ssl_opts(mongoose_tls:options()) -> [ssl:tls_option()].
117 make_cowboy_ssl_opts(Opts) ->
118 211 FailIfNoPeerCert = fail_if_no_peer_cert_opt(Opts),
119 211 {dummy_ref, SSLOpts} = format_opts_with_ref(Opts, FailIfNoPeerCert),
120 211 SSLOpts.
121
122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123 %% local functions
124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125
126 format_opts_with_ref(Opts, FailIfNoPeerCert) ->
127 1590 Verify = verify_opt(Opts),
128 1590 {Ref, VerifyFun} = verify_fun_opt(Opts),
129 1590 SNIOpts = sni_opts(Opts),
130 1590 SSLOpts = maps:to_list(maps:with(ssl_option_keys(), Opts)),
131 1590 {Ref, [{fail_if_no_peer_cert, FailIfNoPeerCert}, {verify, Verify}, {verify_fun, VerifyFun}] ++
132 SNIOpts ++ SSLOpts}.
133
134 ssl_option_keys() ->
135 1590 [certfile, cacertfile, ciphers, keyfile, password, versions, dhfile].
136
137 sni_opts(#{server_name_indication := SNIOpts}) ->
138 1011 process_sni_opts(SNIOpts);
139 sni_opts(#{}) ->
140 579 [].
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 1011 [].
151
152 error_to_list(_Error) ->
153 %TODO: implement later if needed
154 3 "verify_fun failed".
155
156 174 verify_opt(#{verify_mode := none}) -> verify_none;
157 1416 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 173 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 22 Ref = erlang:make_ref(),
175 22 {Ref, verify_fun(Ref, Mode)};
176 verify_fun_opt(#{verify_mode := Mode}) ->
177 1568 {dummy_ref, verify_fun(Mode)}.
178
179 verify_fun(Ref, Mode) when is_reference(Ref) ->
180 22 {Fun, State} = verify_fun(Mode),
181 22 {verify_fun_wrapper(Ref, Fun), State}.
182
183 verify_fun_wrapper(Ref, Fun) when is_reference(Ref), is_function(Fun, 3) ->
184 22 Pid = self(),
185 22 fun(Cert, Event, UserState) ->
186 101 Ret = Fun(Cert, Event, UserState),
187 101 case {Ret, Event} of
188 20 {{valid, _}, _} -> Ret;
189 {{unknown, NewState}, {extension, #'Extension'{critical = true}}} ->
190
:-(
send_verification_failure(Pid, Ref, unknown_critical_extension),
191
:-(
{valid, NewState};
192 80 {{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 1381 {fun
201 9 (_, {bad_cert, _} = R, _) -> {fail, R};
202 4256 (_, {extension, _}, S) -> {unknown, S};
203
:-(
(_, valid, S) -> {valid, S};
204 1064 (_, valid_peer, S) -> {valid, S}
205 end, []};
206 verify_fun(selfsigned_peer) ->
207 35 {fun
208 22 (_, {bad_cert, selfsigned_peer}, S) -> {valid, S};
209
:-(
(_, {bad_cert, _} = R, _) -> {fail, R};
210 8 (_, {extension, _}, S) -> {unknown, S};
211
:-(
(_, valid, S) -> {valid, S};
212 2 (_, valid_peer, S) -> {valid, S}
213 end, []};
214 verify_fun(none) ->
215 174 {fun(_, _, S) -> {valid, S} end, []}.
216
217
218 send_verification_failure(Pid, Ref, Reason) ->
219 1 Pid ! {cert_verification_failure, Ref, Reason}.
220
221 346 receive_verify_results(dummy_ref) -> [];
222 22 receive_verify_results(Ref) -> receive_verify_results(Ref, []).
223
224 receive_verify_results(Ref, Acc) ->
225 23 receive
226 {cert_verification_failure, Ref, Reason} ->
227 1 receive_verify_results(Ref, [Reason | Acc])
228 after 0 ->
229 22 lists:reverse(Acc)
230 end.
Line Hits Source