1 |
|
%%%============================================================================= |
2 |
|
%%% @copyright (C) 1999-2018, Erlang Solutions Ltd |
3 |
|
%%% @author Denys Gonchar <denys.gonchar@erlang-solutions.com> |
4 |
|
%%% @doc this module provides general TLS interface for MongooseIM. |
5 |
|
%%% |
6 |
|
%%% by default tls_module is set to fast_tls, alternatively it can be any |
7 |
|
%%% module that implements ejabberd_tls behaviour |
8 |
|
%%% @end |
9 |
|
%%%============================================================================= |
10 |
|
-module(ejabberd_tls). |
11 |
|
-copyright("2018, Erlang Solutions Ltd."). |
12 |
|
-author('denys.gonchar@erlang-solutions.com'). |
13 |
|
|
14 |
|
%% tls interfaces required by ejabberd_socket & ejabberd_receiver modules. |
15 |
|
-export([tcp_to_tls/2, |
16 |
|
default_ciphers/0, |
17 |
|
send/2, |
18 |
|
recv_data/2, |
19 |
|
controlling_process/2, |
20 |
|
sockname/1, |
21 |
|
peername/1, |
22 |
|
setopts/2, |
23 |
|
get_peer_certificate/1, |
24 |
|
get_tls_last_message/1, |
25 |
|
close/1]). |
26 |
|
|
27 |
|
-export([get_sockmod/1]). |
28 |
|
|
29 |
|
-ignore_xref([behaviour_info/1, close/1, controlling_process/2, peername/1, |
30 |
|
send/2, setopts/2, sockname/1]). |
31 |
|
|
32 |
|
-type tls_socket() :: any(). |
33 |
|
-type cert() :: {ok, Cert::any()} | {bad_cert, bitstring()} | no_peer_cert. |
34 |
|
|
35 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
36 |
|
%% behaviour definition |
37 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
38 |
|
-callback tcp_to_tls(inet:socket(), Opts::list()) -> {ok, tls_socket()} | {error, any()}. |
39 |
|
|
40 |
|
-callback send(tls_socket(), binary()) -> ok | {error , any()}. |
41 |
|
|
42 |
|
-callback recv_data(tls_socket(), binary()) -> {ok, binary()} | {error, any()}. |
43 |
|
|
44 |
|
-callback controlling_process(tls_socket(), pid()) -> ok | {error, any()}. |
45 |
|
|
46 |
|
-callback sockname(tls_socket()) -> {ok, mongoose_transport:peer()} | |
47 |
|
{error, any()}. |
48 |
|
|
49 |
|
-callback peername(tls_socket()) -> {ok, mongoose_transport:peer()} | |
50 |
|
{error, any()}. |
51 |
|
|
52 |
|
-callback setopts(tls_socket(), Opts::list()) -> ok | {error, any()}. |
53 |
|
|
54 |
|
-callback get_peer_certificate(tls_socket()) -> cert(). |
55 |
|
|
56 |
|
-callback close(tls_socket()) -> ok. |
57 |
|
|
58 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
59 |
|
%% socket type definition |
60 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
61 |
|
|
62 |
|
-record(ejabberd_tls_socket, {tls_module :: module(), |
63 |
|
tcp_socket :: inet:socket(), |
64 |
|
tls_socket :: tls_socket(), |
65 |
|
tls_opts :: list(), |
66 |
|
has_cert :: boolean() |
67 |
|
}). |
68 |
|
|
69 |
|
-type socket() :: #ejabberd_tls_socket{}. |
70 |
|
|
71 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
72 |
|
%% APIs |
73 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
74 |
|
|
75 |
|
-spec tcp_to_tls(inet:socket(), Opts::list()) -> {ok, socket()} | {error, any()}. |
76 |
|
tcp_to_tls(TCPSocket, Opts) -> |
77 |
1093 |
Module = proplists:get_value(tls_module, Opts, fast_tls), |
78 |
1093 |
NewOpts1 = proplists:delete(tls_module, Opts), |
79 |
1093 |
NewOpts2 = case proplists:get_value(ciphers, NewOpts1) of |
80 |
917 |
undefined -> [{ciphers, default_ciphers()} | NewOpts1]; |
81 |
176 |
_ -> NewOpts1 |
82 |
|
end, |
83 |
1093 |
case Module:tcp_to_tls(TCPSocket, NewOpts2) of |
84 |
|
{ok, TLSSocket} -> |
85 |
640 |
HasCert = has_peer_cert(NewOpts2), |
86 |
640 |
{ok, #ejabberd_tls_socket{tls_module = Module, |
87 |
|
tcp_socket = TCPSocket, |
88 |
|
tls_socket = TLSSocket, |
89 |
|
tls_opts = NewOpts2, |
90 |
|
has_cert = HasCert}}; |
91 |
453 |
Error -> Error |
92 |
|
end. |
93 |
|
|
94 |
|
default_ciphers() -> |
95 |
1081 |
"TLSv1.2:TLSv1.3". |
96 |
|
|
97 |
|
-spec send(socket(), binary()) -> ok | {error, any()}. |
98 |
640 |
send(#ejabberd_tls_socket{tls_module = M, tls_socket = S}, B) -> M:send(S, B). |
99 |
|
|
100 |
|
|
101 |
|
-spec recv_data(socket(), binary()) -> {ok, binary()} | {error, any()}. |
102 |
1978 |
recv_data(#ejabberd_tls_socket{tls_module = M, tls_socket = S}, B) -> M:recv_data(S, B). |
103 |
|
|
104 |
|
|
105 |
|
-spec controlling_process(socket(), pid()) -> ok | {error, any()}. |
106 |
|
controlling_process(#ejabberd_tls_socket{tls_module = M, tls_socket = S}, Pid) -> |
107 |
:-( |
M:controlling_process(S, Pid). |
108 |
|
|
109 |
|
|
110 |
|
-spec sockname(tls_socket()) -> {ok, mongoose_transport:peer()} | {error, any()}. |
111 |
:-( |
sockname(#ejabberd_tls_socket{tls_module = M, tls_socket = S}) -> M:sockname(S). |
112 |
|
|
113 |
|
|
114 |
|
-spec peername(tls_socket()) -> {ok, mongoose_transport:peer()} | {error, any()}. |
115 |
1566 |
peername(#ejabberd_tls_socket{tls_module = M, tls_socket = S}) -> M:peername(S). |
116 |
|
|
117 |
|
|
118 |
|
-spec setopts(socket(), Opts::list()) -> ok | {error, any()}. |
119 |
1566 |
setopts(#ejabberd_tls_socket{tls_module = M, tls_socket = S}, Opts) -> M:setopts(S, Opts). |
120 |
|
|
121 |
|
|
122 |
|
-spec get_peer_certificate(socket()) -> cert(). |
123 |
|
get_peer_certificate(#ejabberd_tls_socket{has_cert = false}) -> |
124 |
24 |
no_peer_cert; |
125 |
|
get_peer_certificate(#ejabberd_tls_socket{tls_module = just_tls, tls_socket = S}) -> |
126 |
132 |
just_tls:get_peer_certificate(S); |
127 |
|
get_peer_certificate(#ejabberd_tls_socket{tls_module = fast_tls, tls_socket = S, |
128 |
|
tls_opts = TLSOpts}) -> |
129 |
91 |
case {fast_tls:get_verify_result(S), fast_tls:get_peer_certificate(S)} of |
130 |
70 |
{0, {ok, Cert}} -> {ok, Cert}; |
131 |
|
{Error, {ok, Cert}} -> |
132 |
18 |
SSLOpts = proplists:get_value(ssl_options, TLSOpts, []), |
133 |
18 |
maybe_allow_selfsigned(Error, Cert, SSLOpts); |
134 |
3 |
{_, error} -> no_peer_cert |
135 |
|
end. |
136 |
|
|
137 |
|
-spec close(socket()) -> ok. |
138 |
640 |
close(#ejabberd_tls_socket{tls_module = M, tls_socket = S}) -> M:close(S). |
139 |
|
|
140 |
|
-spec get_sockmod(socket()) -> module(). |
141 |
:-( |
get_sockmod(#ejabberd_tls_socket{tls_module = Module}) -> Module. |
142 |
|
|
143 |
|
-spec get_tls_last_message(ejabberd_socket:socket()) -> {ok, binary()} | {error, term()}. |
144 |
|
get_tls_last_message(#ejabberd_tls_socket{} = Socket) -> |
145 |
:-( |
case get_sockmod(Socket) of |
146 |
|
fast_tls -> |
147 |
:-( |
fast_tls:get_tls_last_message(peer, Socket#ejabberd_tls_socket.tls_socket); |
148 |
|
_ -> |
149 |
:-( |
{error, undefined} |
150 |
|
end; |
151 |
|
get_tls_last_message(_) -> |
152 |
:-( |
{error, undefined}. |
153 |
|
|
154 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
155 |
|
%% local functions |
156 |
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
157 |
|
has_peer_cert(Opts) -> |
158 |
|
%% server always provides cert, client - only per request |
159 |
640 |
case {lists:member(connect, Opts), lists:member(verify_none, Opts)} of |
160 |
|
{false, true} -> %% we are tls server, and we haven't requested client's certificate |
161 |
535 |
false; |
162 |
|
_ -> |
163 |
105 |
true |
164 |
|
end. |
165 |
|
|
166 |
|
%% 18 is OpenSSL's and fast_tls's error code for self-signed certs |
167 |
|
maybe_allow_selfsigned(18 = Error, Cert, SSLOpts) -> |
168 |
:-( |
case lists:keyfind(verify_fun, 1, SSLOpts) of |
169 |
|
{verify_fun, {selfsigned_peer, _}} -> |
170 |
:-( |
{ok, Cert}; |
171 |
|
_ -> |
172 |
:-( |
cert_verification_error(Error, Cert) |
173 |
|
end; |
174 |
|
maybe_allow_selfsigned(Error, Cert, _SSLOpts) -> |
175 |
18 |
cert_verification_error(Error, Cert). |
176 |
|
|
177 |
|
cert_verification_error(Error, Cert) -> |
178 |
18 |
{bad_cert, fast_tls:get_cert_verify_string(Error, Cert)}. |