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