./ct_report/coverage/mongoose_tls.COVER.html

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 mongoose_tls behaviour
8 %%% @end
9 %%%=============================================================================
10 -module(mongoose_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([prepare_options/2,
16 tcp_to_tls/2,
17 default_ciphers/0,
18 send/2,
19 recv_data/2,
20 controlling_process/2,
21 sockname/1,
22 peername/1,
23 setopts/2,
24 get_peer_certificate/1,
25 get_tls_last_message/1,
26 close/1]).
27
28 -export([get_sockmod/1]).
29
30 -ignore_xref([behaviour_info/1, close/1, controlling_process/2, peername/1,
31 send/2, setopts/2, sockname/1]).
32
33 -type tls_socket() :: any().
34 -type cert() :: {ok, Cert::any()} | {bad_cert, bitstring()} | no_peer_cert.
35
36 %% Options used for client-side and server-side TLS connections.
37 %% All modules implementing this behaviour have to support the mandatory 'verify_mode' option.
38 %% Other options should be supported if the implementing module supports it.
39 -type options() :: #{module => module(), % fast_tls by default
40 connect => boolean(), % set to 'true' for a client-side call to tcp_to_tls/2
41 verify_mode := peer | selfsigned_peer | none,
42 certfile => string(),
43 cacertfile => string(),
44 ciphers => string(),
45 dhfile => string(), % server-only
46
47 %% only for just_tls
48 disconnect_on_failure => boolean(),
49 keyfile => string(),
50 password => string(),
51 versions => [atom()],
52 server_name_indication => sni_options(), % client-only
53
54 % only for fast_tls
55 protocol_options => [string()]}.
56
57 -type sni_options() :: #{enabled := boolean,
58 protocol := default | https,
59 host => string()}.
60
61 -export_type([tls_socket/0]).
62
63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 %% behaviour definition
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 -callback tcp_to_tls(inet:socket(), options()) -> {ok, tls_socket()} | {error, any()}.
67
68 -callback send(tls_socket(), binary()) -> ok | {error , any()}.
69
70 -callback recv_data(tls_socket(), binary()) -> {ok, binary()} | {error, any()}.
71
72 -callback controlling_process(tls_socket(), pid()) -> ok | {error, any()}.
73
74 -callback sockname(tls_socket()) -> {ok, mongoose_transport:peer()} |
75 {error, any()}.
76
77 -callback peername(tls_socket()) -> {ok, mongoose_transport:peer()} |
78 {error, any()}.
79
80 -callback setopts(tls_socket(), Opts::list()) -> ok | {error, any()}.
81
82 -callback get_peer_certificate(tls_socket()) -> cert().
83
84 -callback close(tls_socket()) -> ok.
85
86 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 %% socket type definition
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89
90 -record(mongoose_tls_socket, {tls_module :: module(),
91 tcp_socket :: inet:socket(),
92 tls_socket :: tls_socket(),
93 tls_opts :: options(),
94 has_cert :: boolean()
95 }).
96
97 -type socket() :: #mongoose_tls_socket{}.
98
99 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100 %% APIs
101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102
103 -spec tcp_to_tls(inet:socket(), options()) -> {ok, socket()} | {error, any()}.
104 tcp_to_tls(TCPSocket, Opts) ->
105 1118 Module = maps:get(module, Opts, fast_tls),
106 1118 PreparedOpts = prepare_options(Module, maps:remove(module, Opts)),
107 1118 case Module:tcp_to_tls(TCPSocket, PreparedOpts) of
108 {ok, TLSSocket} ->
109 960 HasCert = has_peer_cert(Opts),
110 960 {ok, #mongoose_tls_socket{tls_module = Module,
111 tcp_socket = TCPSocket,
112 tls_socket = TLSSocket,
113 tls_opts = Opts,
114 has_cert = HasCert}};
115 158 Error -> Error
116 end.
117
118 -spec prepare_options(module(), options()) -> any().
119 prepare_options(fast_tls, Opts) ->
120 %% fast_tls is an external library and its API cannot use Opts directly
121 4064 lists:flatmap(fun({K, V}) -> fast_tls_opt(K, V) end, maps:to_list(Opts));
122 prepare_options(_Module, Opts) ->
123 199 Opts.
124
125 1579 fast_tls_opt(connect, true) -> [connect];
126 4033 fast_tls_opt(verify_mode, peer) -> [];
127 31 fast_tls_opt(verify_mode, none) -> [verify_none];
128 3172 fast_tls_opt(cacertfile, File) -> [{cafile, File}];
129 743 fast_tls_opt(dhfile, File) -> [{dhfile, File}];
130 4064 fast_tls_opt(certfile, File) -> [{certfile, File}];
131 4064 fast_tls_opt(ciphers, Ciphers) -> [{ciphers, Ciphers}];
132 4047 fast_tls_opt(protocol_options, ProtoOpts) -> [{protocol_options, string:join(ProtoOpts, "|")}].
133
134 default_ciphers() ->
135 362 "TLSv1.2:TLSv1.3".
136
137 -spec send(socket(), binary()) -> ok | {error, any()}.
138 907 send(#mongoose_tls_socket{tls_module = M, tls_socket = S}, B) -> M:send(S, B).
139
140
141 -spec recv_data(socket(), binary()) -> {ok, binary()} | {error, any()}.
142 3108 recv_data(#mongoose_tls_socket{tls_module = M, tls_socket = S}, B) -> M:recv_data(S, B).
143
144
145 -spec controlling_process(socket(), pid()) -> ok | {error, any()}.
146 controlling_process(#mongoose_tls_socket{tls_module = M, tls_socket = S}, Pid) ->
147
:-(
M:controlling_process(S, Pid).
148
149
150 -spec sockname(tls_socket()) -> {ok, mongoose_transport:peer()} | {error, any()}.
151
:-(
sockname(#mongoose_tls_socket{tls_module = M, tls_socket = S}) -> M:sockname(S).
152
153
154 -spec peername(tls_socket()) -> {ok, mongoose_transport:peer()} | {error, any()}.
155 2442 peername(#mongoose_tls_socket{tls_module = M, tls_socket = S}) -> M:peername(S).
156
157
158 -spec setopts(socket(), Opts::list()) -> ok | {error, any()}.
159 2442 setopts(#mongoose_tls_socket{tls_module = M, tls_socket = S}, Opts) -> M:setopts(S, Opts).
160
161
162 -spec get_peer_certificate(socket()) -> cert().
163 get_peer_certificate(#mongoose_tls_socket{has_cert = false}) ->
164 31 no_peer_cert;
165 get_peer_certificate(#mongoose_tls_socket{tls_module = just_tls, tls_socket = S}) ->
166 120 just_tls:get_peer_certificate(S);
167 get_peer_certificate(#mongoose_tls_socket{tls_module = fast_tls, tls_socket = S,
168 tls_opts = TLSOpts}) ->
169 109 case {fast_tls:get_verify_result(S), fast_tls:get_peer_certificate(S)} of
170 64 {0, {ok, Cert}} -> {ok, Cert};
171 {Error, {ok, Cert}} ->
172 18 maybe_allow_selfsigned(Error, Cert, TLSOpts);
173 27 {_, error} -> no_peer_cert
174 end.
175
176 -spec close(socket()) -> ok.
177 960 close(#mongoose_tls_socket{tls_module = M, tls_socket = S}) -> M:close(S).
178
179 -spec get_sockmod(socket()) -> module().
180 294 get_sockmod(#mongoose_tls_socket{tls_module = Module}) -> Module.
181
182 -spec get_tls_last_message(ejabberd_socket:socket()) -> {ok, binary()} | {error, term()}.
183 get_tls_last_message(#mongoose_tls_socket{} = Socket) ->
184 20 case get_sockmod(Socket) of
185 fast_tls ->
186 20 fast_tls:get_tls_last_message(peer, Socket#mongoose_tls_socket.tls_socket);
187 _ ->
188
:-(
{error, undefined}
189 end;
190 get_tls_last_message(_) ->
191
:-(
{error, undefined}.
192
193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
194 %% local functions
195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196 -spec has_peer_cert(options()) -> boolean().
197 has_peer_cert(#{connect := true}) ->
198 17 true; % server always provides cert
199 has_peer_cert(#{verify_mode := VerifyMode}) ->
200 943 VerifyMode =/= none. % client provides cert only when requested
201
202 %% 18 is OpenSSL's and fast_tls's error code for self-signed certs
203 maybe_allow_selfsigned(18, Cert, #{verify_mode := selfsigned_peer}) ->
204
:-(
{ok, Cert};
205 maybe_allow_selfsigned(Error, Cert, _SSLOpts) ->
206 18 cert_verification_error(Error, Cert).
207
208 cert_verification_error(Error, Cert) ->
209 18 {bad_cert, fast_tls:get_cert_verify_string(Error, Cert)}.
Line Hits Source