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