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 |
1642 |
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 |
21 |
(_, {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. |