./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(ejabberd_tls).
12
13 -include_lib("public_key/include/public_key.hrl").
14
15 -record(tls_socket, {verify_results = [],
16 ssl_socket
17 }).
18
19 %ejabberd_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 -export([verify_fun/1]).
31
32 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33 %% APIs
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
36 %% -callback tcp_to_tls(inet:socket(), Opts :: list()) -> {ok, tls_socket()} | {error, any()}.
37 tcp_to_tls(TCPSocket, Options) ->
38 534 inet:setopts(TCPSocket, [{active, false}]),
39 534 Opts = format_opts(Options),
40 534 {Ref, NewOpts} = set_verify_fun(Opts),
41 534 Ret = case lists:member(connect, Opts) of
42 534 false -> ssl:handshake(TCPSocket, NewOpts);
43
:-(
true -> ssl:connect(TCPSocket, NewOpts)
44 end,
45 534 VerifyResults = receive_verify_results(Ref),
46 534 case Ret of
47 {ok, SSLSocket} ->
48 81 {ok, #tls_socket{ssl_socket = SSLSocket, verify_results = VerifyResults}};
49 453 _ -> Ret
50 end.
51
52
53 %% -callback send(tls_socket(), binary()) -> ok | {error, any()}.
54 295 send(#tls_socket{ssl_socket = SSLSocket}, Packet) -> ssl:send(SSLSocket, Packet).
55
56
57 %% -callback recv_data(tls_socket(), binary()) -> {ok, binary()} | {error, any()}.
58 recv_data(_, <<"">>) ->
59 %% such call is required for fast_tls to accomplish
60 %% tls handshake, for just_tls we can ignore it
61 81 {ok, <<"">>};
62 recv_data(#tls_socket{ssl_socket = SSLSocket}, Data1) ->
63 238 case ssl:recv(SSLSocket, 0, 0) of
64
:-(
{ok, Data2} -> {ok, <<Data1/binary, Data2/binary>>};
65 238 _ -> {ok, Data1}
66 end.
67
68
69 %% -callback controlling_process(tls_socket(), pid()) -> ok | {error, any()}.
70 controlling_process(#tls_socket{ssl_socket = SSLSocket}, Pid) ->
71
:-(
ssl:controlling_process(SSLSocket, Pid).
72
73
74 %% -callback sockname(tls_socket()) -> {ok, {inet:ip_address(), inet:port_number()}} |
75 %% {error, any()}.
76
:-(
sockname(#tls_socket{ssl_socket = SSLSocket}) -> ssl:sockname(SSLSocket).
77
78
79 %% -callback peername(tls_socket()) -> {ok, {inet:ip_address(), inet:port_number()}} |
80 %% {error, any()}.
81 319 peername(#tls_socket{ssl_socket = SSLSocket}) -> ssl:peername(SSLSocket).
82
83
84 %% -callback setopts(tls_socket(), Opts::list()) -> ok | {error, any()}.
85 319 setopts(#tls_socket{ssl_socket = SSLSocket}, Opts) -> ssl:setopts(SSLSocket, Opts).
86
87
88 %% -callback get_peer_certificate(tls_socket()) -> {ok, Cert::any()} |
89 %% {bad_cert, bitstring()} |
90 %% no_peer_cert.
91 get_peer_certificate(#tls_socket{verify_results = [], ssl_socket = SSLSocket}) ->
92 117 case ssl:peercert(SSLSocket) of
93 {ok, PeerCert} ->
94 111 Cert = public_key:pkix_decode_cert(PeerCert, plain),
95 111 {ok, Cert};
96 6 _ -> no_peer_cert
97 end;
98 get_peer_certificate(#tls_socket{verify_results = [Err | _]}) ->
99 3 {bad_cert, error_to_list(Err)}.
100
101
102 %% -callback close(tls_socket()) -> ok.
103 81 close(#tls_socket{ssl_socket = SSLSocket}) -> ssl:close(SSLSocket).
104
105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 %% local functions
107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108
109 format_opts(Opts) ->
110 534 NewOpts = remove_duplicates(format_opts([], Opts)),
111 534 case proplists:is_defined(verify, NewOpts) of
112 494 true -> NewOpts;
113 40 false -> [{verify, verify_peer} | NewOpts]
114 end.
115
116 534 format_opts(NewOpts, []) -> NewOpts;
117 format_opts(NewOpts, [verify_none | T]) ->
118 494 format_opts([{verify, verify_none} | NewOpts], T);
119 format_opts(NewOpts, [{certfile, File} | T]) ->
120 335 format_opts([{certfile, File} | NewOpts], T);
121 format_opts(NewOpts, [{cafile, File} | T]) ->
122
:-(
format_opts([{cacertfile, File} | NewOpts], T);
123 format_opts(NewOpts, [{dhfile, File} | T]) ->
124 335 format_opts([{dhfile, File} | NewOpts], T);
125 format_opts(NewOpts, [{ssl_options, SSLOpts} | T]) ->
126 199 format_opts(NewOpts ++ SSLOpts, T);
127 1068 format_opts(NewOpts, [_ | T]) -> format_opts(NewOpts, T). %ignore unknown options
128
129 remove_duplicates(PropList) ->
130 534 F = fun
131 (T, NewPropList) when is_tuple(T) ->
132 1642 lists:keystore(element(1, T), 1, NewPropList, T);
133
:-(
(_, NewPropList) -> NewPropList
134 end,
135 534 lists:foldl(F, [], PropList).
136
137 error_to_list(_Error) ->
138 %TODO: implement later if needed
139 3 "verify_fun failed".
140 %% doc
141 %% this function translates the `verify_fun` tuple from `ssl_options` to the real fun
142 %% which will later be used when TCP socket is upgraded to TLS
143 %% accepted format is:
144 %% {verify_fun, {PredefinedValidationFun,DisconnectOnFailure}
145 %% PredefinedValidationFun is one of the following:
146 %% none - no validation of the clients certificate - any cert is accepted.
147 %% peer - standard verification of the certificate.
148 %% selfsigned_peer - the same as peer but also accepts self-signed certificates
149 %% DisconnectOnFailure is boolean parameter:
150 %% true - drop connection if certificate verification failed
151 %% false - connect anyway, but later return {bad_cert,Error}
152 %% on certificate verification (the same as fast_tls do).
153 set_verify_fun(Opts) ->
154 534 case proplists:get_value(verify_fun, Opts) of
155 494 undefined -> {dummy_ref, Opts};
156 {VerifyFun, true} ->
157 20 {dummy_ref, lists:keyreplace(verify_fun, 1, Opts, {verify_fun, verify_fun(VerifyFun)})};
158 {VerifyFun, false} ->
159 20 Ref = erlang:make_ref(),
160 20 {Ref, lists:keyreplace(verify_fun, 1, Opts, {verify_fun, verify_fun(Ref, VerifyFun)})}
161 end.
162
163 verify_fun(Ref, VerifyFun) when is_reference(Ref) ->
164 20 {Fun, State} = verify_fun(VerifyFun),
165 20 {verify_fun_wrapper(Ref, Fun), State}.
166
167 verify_fun_wrapper(Ref, Fun) when is_reference(Ref), is_function(Fun, 3) ->
168 20 Pid = self(),
169 20 fun(Cert, Event, UserState) ->
170 91 Ret = Fun(Cert, Event, UserState),
171 91 case {Ret, Event} of
172 18 {{valid, _}, _} -> Ret;
173 {{unknown, NewState}, {extension, #'Extension'{critical = true}}} ->
174
:-(
send_verification_failure(Pid, Ref, unknown_critical_extension),
175
:-(
{valid, NewState};
176 72 {{unknown, _}, {extension, _}} -> Ret;
177 {_, _} -> %% {fail,Reason} = Ret
178 1 send_verification_failure(Pid, Ref, Ret),
179 1 {valid, UserState} %return the last valid user state
180 end
181 end.
182
183 verify_fun(peer) ->
184 44 {fun
185 9 (_, {bad_cert, _} = R, _) -> {fail, R};
186 104 (_, {extension, _}, S) -> {unknown, S};
187
:-(
(_, valid, S) -> {valid, S};
188 26 (_, valid_peer, S) -> {valid, S}
189 end, []};
190 verify_fun(selfsigned_peer) ->
191 32 {fun
192 20 (_, {bad_cert, selfsigned_peer}, S) -> {valid, S};
193
:-(
(_, {bad_cert, _} = R, _) -> {fail, R};
194 8 (_, {extension, _}, S) -> {unknown, S};
195
:-(
(_, valid, S) -> {valid, S};
196 2 (_, valid_peer, S) -> {valid, S}
197 end, []};
198 verify_fun(none) ->
199
:-(
{fun(_, _, S) -> {valid, S} end, []}.
200
201
202 send_verification_failure(Pid, Ref, Reason) ->
203 1 Pid ! {cert_verification_failure, Ref, Reason}.
204
205 514 receive_verify_results(dummy_ref) -> [];
206 20 receive_verify_results(Ref) -> receive_verify_results(Ref, []).
207
208 receive_verify_results(Ref, Acc) ->
209 21 receive
210 {cert_verification_failure, Ref, Reason} ->
211 1 receive_verify_results(Ref, [Reason | Acc])
212 after 0 ->
213 20 lists:reverse(Acc)
214 end.
Line Hits Source