diff options
Diffstat (limited to 'lib/ssl/src')
-rw-r--r-- | lib/ssl/src/Makefile | 4 | ||||
-rw-r--r-- | lib/ssl/src/dtls_connection.erl | 13 | ||||
-rw-r--r-- | lib/ssl/src/dtls_listener_sup.erl (renamed from lib/ssl/src/dtls_udp_sup.erl) | 6 | ||||
-rw-r--r-- | lib/ssl/src/dtls_packet_demux.erl (renamed from lib/ssl/src/dtls_udp_listener.erl) | 56 | ||||
-rw-r--r-- | lib/ssl/src/dtls_socket.erl | 34 | ||||
-rw-r--r-- | lib/ssl/src/inet_tls_dist.erl | 286 | ||||
-rw-r--r-- | lib/ssl/src/ssl.app.src | 4 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 53 | ||||
-rw-r--r-- | lib/ssl/src/ssl_certificate.erl | 18 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection_sup.erl | 10 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 9 | ||||
-rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 5 |
12 files changed, 208 insertions, 290 deletions
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index 11b3e65912..c389aa8cfe 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -54,8 +54,8 @@ MODULES= \ ssl_connection_sup \ ssl_listen_tracker_sup\ dtls_connection_sup \ - dtls_udp_listener\ - dtls_udp_sup \ + dtls_packet_demux \ + dtls_listener_sup \ ssl_dist_sup\ ssl_dist_admin_sup\ ssl_dist_connection_sup\ diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 0fe568759d..4e3f65d9c6 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -137,9 +137,8 @@ next_record(#state{protocol_buffers = Buffers#protocol_buffers{dtls_cipher_texts = Rest}, connection_states = ConnectionStates}); next_record(#state{role = server, - socket = {Listener, {Client, _}}, - transport_cb = gen_udp} = State) -> - dtls_udp_listener:active_once(Listener, Client, self()), + socket = {Listener, {Client, _}}} = State) -> + dtls_packet_demux:active_once(Listener, Client, self()), {no_record, State}; next_record(#state{role = client, socket = {_Server, Socket} = DTLSSocket, @@ -448,7 +447,7 @@ init({call, From}, {start, Timeout}, }, {Record, State} = next_record(State3), next_event(hello, Record, State, Actions); -init({call, _} = Type, Event, #state{role = server, transport_cb = gen_udp} = State) -> +init({call, _} = Type, Event, #state{role = server, data_tag = udp} = State) -> Result = gen_handshake(?FUNCTION_NAME, Type, Event, State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}, protocol_specific = #{current_cookie_secret => dtls_v1:cookie_secret(), @@ -922,7 +921,7 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)). -handle_own_alert(Alert, Version, StateName, #state{transport_cb = gen_udp, +handle_own_alert(Alert, Version, StateName, #state{data_tag = udp, role = Role, ssl_options = Options} = State0) -> case ignore_alert(Alert, State0) of @@ -1013,10 +1012,10 @@ next_flight(Flight) -> change_cipher_spec => undefined, handshakes_after_change_cipher_spec => []}. -handle_flight_timer(#state{transport_cb = gen_udp, +handle_flight_timer(#state{data_tag = udp, flight_state = {retransmit, Timeout}} = State) -> start_retransmision_timer(Timeout, State); -handle_flight_timer(#state{transport_cb = gen_udp, +handle_flight_timer(#state{data_tag = udp, flight_state = connection} = State) -> {State, []}; handle_flight_timer(State) -> diff --git a/lib/ssl/src/dtls_udp_sup.erl b/lib/ssl/src/dtls_listener_sup.erl index 197882e92f..6939f1ef3b 100644 --- a/lib/ssl/src/dtls_udp_sup.erl +++ b/lib/ssl/src/dtls_listener_sup.erl @@ -23,7 +23,7 @@ %% Purpose: Supervisor for a procsses dispatching upd datagrams to %% correct DTLS handler %%---------------------------------------------------------------------- --module(dtls_udp_sup). +-module(dtls_listener_sup). -behaviour(supervisor). @@ -52,10 +52,10 @@ init(_O) -> MaxT = 3600, Name = undefined, % As simple_one_for_one is used. - StartFunc = {dtls_udp_listener, start_link, []}, + StartFunc = {dtls_packet_demux, start_link, []}, Restart = temporary, % E.g. should not be restarted Shutdown = 4000, - Modules = [dtls_udp_listener], + Modules = [dtls_packet_demux], Type = worker, ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, diff --git a/lib/ssl/src/dtls_udp_listener.erl b/lib/ssl/src/dtls_packet_demux.erl index 0608c6bd2b..1672626165 100644 --- a/lib/ssl/src/dtls_udp_listener.erl +++ b/lib/ssl/src/dtls_packet_demux.erl @@ -19,15 +19,15 @@ %% --module(dtls_udp_listener). +-module(dtls_packet_demux). -behaviour(gen_server). -include("ssl_internal.hrl"). %% API --export([start_link/4, active_once/3, accept/2, sockname/1, close/1, - get_all_opts/1, get_sock_opts/2, set_sock_opts/2]). +-export([start_link/5, active_once/3, accept/2, sockname/1, close/1, + get_all_opts/1, get_sock_opts/2, set_sock_opts/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -36,6 +36,7 @@ -record(state, {port, listener, + transport, dtls_options, emulated_options, dtls_msq_queues = kv_new(), @@ -50,35 +51,36 @@ %%% API %%%=================================================================== -start_link(Port, EmOpts, InetOptions, DTLSOptions) -> - gen_server:start_link(?MODULE, [Port, EmOpts, InetOptions, DTLSOptions], []). +start_link(Port, TransportInfo, EmOpts, InetOptions, DTLSOptions) -> + gen_server:start_link(?MODULE, [Port, TransportInfo, EmOpts, InetOptions, DTLSOptions], []). -active_once(UDPConnection, Client, Pid) -> - gen_server:cast(UDPConnection, {active_once, Client, Pid}). +active_once(PacketSocket, Client, Pid) -> + gen_server:cast(PacketSocket, {active_once, Client, Pid}). -accept(UDPConnection, Accepter) -> - call(UDPConnection, {accept, Accepter}). +accept(PacketSocket, Accepter) -> + call(PacketSocket, {accept, Accepter}). -sockname(UDPConnection) -> - call(UDPConnection, sockname). -close(UDPConnection) -> - call(UDPConnection, close). -get_sock_opts(UDPConnection, SplitSockOpts) -> - call(UDPConnection, {get_sock_opts, SplitSockOpts}). -get_all_opts(UDPConnection) -> - call(UDPConnection, get_all_opts). -set_sock_opts(UDPConnection, Opts) -> - call(UDPConnection, {set_sock_opts, Opts}). +sockname(PacketSocket) -> + call(PacketSocket, sockname). +close(PacketSocket) -> + call(PacketSocket, close). +get_sock_opts(PacketSocket, SplitSockOpts) -> + call(PacketSocket, {get_sock_opts, SplitSockOpts}). +get_all_opts(PacketSocket) -> + call(PacketSocket, get_all_opts). +set_sock_opts(PacketSocket, Opts) -> + call(PacketSocket, {set_sock_opts, Opts}). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== -init([Port, EmOpts, InetOptions, DTLSOptions]) -> +init([Port, {TransportModule, _,_,_} = TransportInfo, EmOpts, InetOptions, DTLSOptions]) -> try - {ok, Socket} = gen_udp:open(Port, InetOptions), + {ok, Socket} = TransportModule:open(Port, InetOptions), {ok, #state{port = Port, first = true, + transport = TransportInfo, dtls_options = DTLSOptions, emulated_options = EmOpts, listener = Socket, @@ -134,20 +136,20 @@ handle_cast({active_once, Client, Pid}, State0) -> State = handle_active_once(Client, Pid, State0), {noreply, State}. -handle_info({udp, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket} = State0) -> +handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket, transport = {_,Transport,_,_}} = State0) -> State = handle_datagram({IP, InPortNo}, Msg, State0), next_datagram(Socket), {noreply, State}; %% UDP socket does not have a connection and should not receive an econnreset -%% This does however happens on on some windows versions. Just ignoring it +%% This does however happens on some windows versions. Just ignoring it %% appears to make things work as expected! -handle_info({udp_error, Socket, econnreset = Error}, #state{listener = Socket} = State) -> +handle_info({Error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error}} = State) -> Report = io_lib:format("Ignore SSL UDP Listener: Socket error: ~p ~n", [Error]), error_logger:info_report(Report), {noreply, State}; -handle_info({udp_error, Socket, Error}, #state{listener = Socket} = State) -> - Report = io_lib:format("SSL UDP Listener shutdown: Socket error: ~p ~n", [Error]), +handle_info({Error, Socket, Error}, #state{listener = Socket, transport = {_,_,_, Error}} = State) -> + Report = io_lib:format("SSL Packet muliplxer shutdown: Socket error: ~p ~n", [Error]), error_logger:info_report(Report), {noreply, State#state{close=true}}; @@ -231,7 +233,7 @@ setup_new_connection(User, From, Client, Msg, #state{dtls_processes = Processes, listener = Socket, emulated_options = EmOpts} = State) -> ConnArgs = [server, "localhost", Port, {self(), {Client, Socket}}, - {DTLSOpts, EmOpts, udp_listener}, User, dtls_socket:default_cb_info()], + {DTLSOpts, EmOpts, dtls_listener}, User, dtls_socket:default_cb_info()], case dtls_connection_sup:start_child(ConnArgs) of {ok, Pid} -> erlang:monitor(process, Pid), diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl index 0e4ab089dc..8dd62bc352 100644 --- a/lib/ssl/src/dtls_socket.erl +++ b/lib/ssl/src/dtls_socket.erl @@ -22,31 +22,31 @@ -include("ssl_internal.hrl"). -include("ssl_api.hrl"). --export([send/3, listen/3, accept/3, connect/4, socket/4, setopts/3, getopts/3, getstat/3, +-export([send/3, listen/2, accept/3, connect/4, socket/4, setopts/3, getopts/3, getstat/3, peername/2, sockname/2, port/2, close/2]). -export([emulated_options/0, emulated_options/1, internal_inet_values/0, default_inet_values/0, default_cb_info/0]). send(Transport, {{IP,Port},Socket}, Data) -> Transport:send(Socket, IP, Port, Data). -listen(gen_udp = Transport, Port, #config{transport_info = {Transport, _, _, _}, - ssl = SslOpts, - emulated = EmOpts, - inet_user = Options} = Config) -> +listen(Port, #config{transport_info = TransportInfo, + ssl = SslOpts, + emulated = EmOpts, + inet_user = Options} = Config) -> - case dtls_udp_sup:start_child([Port, emulated_socket_options(EmOpts, #socket_options{}), + case dtls_listener_sup:start_child([Port, TransportInfo, emulated_socket_options(EmOpts, #socket_options{}), Options ++ internal_inet_values(), SslOpts]) of {ok, Pid} -> - {ok, #sslsocket{pid = {udp, Config#config{udp_handler = {Pid, Port}}}}}; + {ok, #sslsocket{pid = {dtls, Config#config{dtls_handler = {Pid, Port}}}}}; Err = {error, _} -> Err end. -accept(udp, #config{transport_info = {Transport = gen_udp,_,_,_}, +accept(dtls, #config{transport_info = {Transport,_,_,_}, connection_cb = ConnectionCb, - udp_handler = {Listner, _}}, _Timeout) -> - case dtls_udp_listener:accept(Listner, self()) of + dtls_handler = {Listner, _}}, _Timeout) -> + case dtls_packet_demux:accept(Listner, self()) of {ok, Pid, Socket} -> {ok, socket(Pid, Transport, {Listner, Socket}, ConnectionCb)}; {error, Reason} -> @@ -69,7 +69,9 @@ connect(Address, Port, #config{transport_info = {Transport, _, _, _} = CbInfo, end. close(gen_udp, {_Client, _Socket}) -> - ok. + ok; +close(Transport, {_Client, Socket}) -> + Transport:close(Socket). socket(Pid, gen_udp = Transport, {{_, _}, Socket}, ConnectionCb) -> #sslsocket{pid = Pid, @@ -79,18 +81,18 @@ socket(Pid, Transport, Socket, ConnectionCb) -> #sslsocket{pid = Pid, %% "The name "fd" is keept for backwards compatibility fd = {Transport, Socket, ConnectionCb}}. -setopts(_, #sslsocket{pid = {udp, #config{udp_handler = {ListenPid, _}}}}, Options) -> +setopts(_, #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}, Options) -> SplitOpts = tls_socket:split_options(Options), - dtls_udp_listener:set_sock_opts(ListenPid, SplitOpts); + dtls_packet_demux:set_sock_opts(ListenPid, SplitOpts); %%% Following clauses will not be called for emulated options, they are handled in the connection process setopts(gen_udp, Socket, Options) -> inet:setopts(Socket, Options); setopts(Transport, Socket, Options) -> Transport:setopts(Socket, Options). -getopts(_, #sslsocket{pid = {udp, #config{udp_handler = {ListenPid, _}}}}, Options) -> +getopts(_, #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}, Options) -> SplitOpts = tls_socket:split_options(Options), - dtls_udp_listener:get_sock_opts(ListenPid, SplitOpts); + dtls_packet_demux:get_sock_opts(ListenPid, SplitOpts); getopts(gen_udp, #sslsocket{pid = {Socket, #config{emulated = EmOpts}}}, Options) -> {SockOptNames, EmulatedOptNames} = tls_socket:split_options(Options), EmulatedOpts = get_emulated_opts(EmOpts, EmulatedOptNames), @@ -112,7 +114,7 @@ getstat(gen_udp, {_,Socket}, Options) -> inet:getstat(Socket, Options); getstat(Transport, Socket, Options) -> Transport:getstat(Socket, Options). -peername(udp, _) -> +peername(_, undefined) -> {error, enotconn}; peername(gen_udp, {_, {Client, _Socket}}) -> {ok, Client}; diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index a6ceff25cb..aa3d7e3f72 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -31,7 +31,7 @@ -export([nodelay/0]). --export([verify_client/3, verify_server/3, cert_nodes/1]). +-export([verify_client/3, cert_nodes/1]). -export([dbg/0]). % Debug @@ -236,12 +236,10 @@ accept_loop(Driver, Listen, Kernel) -> end. accept_loop(Driver, Listen, Kernel, Socket) -> - {Opts,CertNodesFun} = - setup_verify_client( - Driver, Socket, get_ssl_options(server)), + Opts = setup_verify_client(Socket, get_ssl_options(server)), wait_for_code_server(), case - ssl:ssl_accept( + ssl:handshake( Socket, trace([{active, false},{packet, 4}|Opts]), net_kernel:connecttime()) @@ -255,7 +253,7 @@ accept_loop(Driver, Listen, Kernel, Socket) -> {Kernel, controller, Pid} -> ok = ssl:controlling_process(SslSocket, Pid), trace( - Pid ! {self(), controller, CertNodesFun}); + Pid ! {self(), controller}); {Kernel, unsupported_protocol} -> exit(trace(unsupported_protocol)) end, @@ -278,48 +276,59 @@ accept_loop(Driver, Listen, Kernel, Socket) -> %% as a configuration marker that verify_client/3 shall be used. %% %% Replace the State in the first occurence of -%% {verify_fun,{fun ?MODULE:verify_client/3,State}} and remove the rest. +%% {verify_fun,{fun ?MODULE:verify_client/3,State}} +%% and remove the rest. %% The inserted state is not accesible from a configuration file %% since it is dynamic and connection dependent. %% -setup_verify_client(Driver, Socket, Opts) -> - setup_verify_client(Driver, Socket, Opts, undefined, []). +setup_verify_client(Socket, Opts) -> + setup_verify_client(Socket, Opts, true, []). %% -setup_verify_client(_Driver, _Socket, [], CertNodesFun, OptsR) -> - {lists:reverse(OptsR),CertNodesFun}; -setup_verify_client(Driver, Socket, [Opt|Opts], CertNodesFun, OptsR) -> +setup_verify_client(_Socket, [], _, OptsR) -> + lists:reverse(OptsR); +setup_verify_client(Socket, [Opt|Opts], First, OptsR) -> case Opt of - {verify_fun,{Fun,NewCertNodesFun}} -> + {verify_fun,{Fun,_}} -> case Fun =:= fun ?MODULE:verify_client/3 of - true when is_function(NewCertNodesFun, 1) -> + true -> if - CertNodesFun =:= undefined -> + First -> case inet:peername(Socket) of {ok,{PeerIP,_Port}} -> + {ok,Allowed} = net_kernel:allowed(), + AllowedHosts = allowed_hosts(Allowed), setup_verify_client( - Driver, Socket, Opts, NewCertNodesFun, + Socket, Opts, false, [{verify_fun, - {Fun, - {NewCertNodesFun,Driver,PeerIP}}} + {Fun, {AllowedHosts,PeerIP}}} |OptsR]); {error,Reason} -> exit(trace({no_peername,Reason})) end; true -> setup_verify_client( - Driver, Socket, Opts, CertNodesFun, OptsR) + Socket, Opts, First, OptsR) end; - true -> - exit( - trace( - {verify_client_bad_argument,CertNodesFun})); false -> setup_verify_client( - Driver, Socket, Opts, CertNodesFun, [Opt|OptsR]) + Socket, Opts, First, [Opt|OptsR]) end; _ -> - setup_verify_client( - Driver, Socket, Opts, CertNodesFun, [Opt|OptsR]) + setup_verify_client(Socket, Opts, First, [Opt|OptsR]) + end. + +allowed_hosts(Allowed) -> + lists:usort(allowed_node_hosts(Allowed)). +%% +allowed_node_hosts([]) -> []; +allowed_node_hosts([Node|Allowed]) -> + case dist_util:split_node(Node) of + {node,_,Host} -> + [Host|allowed_node_hosts(Allowed)]; + {host,Host} -> + [Host|allowed_node_hosts(Allowed)]; + _ -> + allowed_node_hosts(Allowed) end. %% Same as verify_peer but check cert host names for @@ -330,48 +339,19 @@ verify_client(_, {extension,_}, S) -> {unknown,S}; verify_client(_, valid, S) -> {valid,S}; -verify_client(PeerCert, valid_peer, {CertNodesFun,Driver,PeerIP} = S) -> - %% - %% Parse out all node names from the peer's certificate - %% - case CertNodesFun(PeerCert) of - undefined -> - %% Certificate allows all nodes +verify_client(_, valid_peer, {[],_} = S) -> + %% Allow all hosts + {valid,S}; +verify_client(PeerCert, valid_peer, {AllowedHosts,PeerIP} = S) -> + case + public_key:pkix_verify_hostname( + PeerCert, + [{ip,PeerIP}|[{dns_id,Host} || Host <- AllowedHosts]]) + of + true -> {valid,S}; - [] -> - %% Certificate allows no nodes - {fail,cert_missing_node_name}; - CertNodes -> - %% Check if the IP address of one of the nodes - %% in the peer certificate has the peer's IP address - case filter_nodes_by_ip(CertNodes, Driver, PeerIP) of - [] -> - {fail,cert_no_host_with_peer_ip}; - _ -> - {valid,S} - end - end. - -%% Filter out the nodes that has got the given IP address -%% -filter_nodes_by_ip(Nodes, Driver, IP) -> - [Node || - Node <- Nodes, - case dist_util:split_node(Node) of - {node,_,Host} -> - filter_host_by_ip(Host, Driver, IP); - {host,Host} -> - filter_host_by_ip(Host, Driver, IP); - {name,_Name} -> - true - end]. - -filter_host_by_ip(Host, Driver, IP) -> - case Driver:getaddr(Host) of - {ok,IP} -> - true; - _ -> - false + false -> + {fail,cert_no_hostname_nor_ip_match} end. @@ -417,19 +397,18 @@ gen_accept_connection( spawn_opt( fun() -> do_accept( - Driver, Kernel, AcceptPid, DistCtrl, - MyNode, Allowed, SetupTime) + Driver, AcceptPid, DistCtrl, + MyNode, Allowed, SetupTime, Kernel) end, [link, {priority, max}])). -do_accept(Driver, Kernel, AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) -> +do_accept( + _Driver, AcceptPid, DistCtrl, MyNode, Allowed, SetupTime, Kernel) -> SslSocket = ssl_connection:get_sslsocket(DistCtrl), receive - {AcceptPid, controller, CertNodesFun} -> + {AcceptPid, controller} -> Timer = dist_util:start_timer(SetupTime), - NewAllowed = - allowed_nodes( - Driver, CertNodesFun, SslSocket, Allowed), + NewAllowed = allowed_nodes(SslSocket, Allowed), HSData0 = hs_data_common(SslSocket), HSData = HSData0#hs_data{ @@ -443,65 +422,67 @@ do_accept(Driver, Kernel, AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) -> dist_util:handshake_other_started(trace(HSData)) end. -%% Return a list of allowed nodes according to -%% the given Allowed list that matches the peer certificate -%% -allowed_nodes(_Driver, undefined, _SslSocket, Allowed) -> - Allowed; -allowed_nodes(Driver, CertNodesFun, SslSocket, Allowed) -> +allowed_nodes(_SslSocket, []) -> + %% Allow all + []; +allowed_nodes(SslSocket, Allowed) -> case ssl:peercert(SslSocket) of {ok,PeerCertDER} -> case ssl:peername(SslSocket) of {ok,{PeerIP,_Port}} -> - PeerCert = public_key:pkix_decode_cert(PeerCertDER, otp), - %% - %% Parse out all node names from the peer's certificate - %% - case CertNodesFun(PeerCert) of - undefined -> - %% Certificate allows all nodes - Allowed; + PeerCert = + public_key:pkix_decode_cert(PeerCertDER, otp), + case + allowed_nodes( + PeerCert, allowed_hosts(Allowed), PeerIP) + of [] -> - %% Certificate allows no nodes - ?shutdown(cert_missing_node_name); - CertNodes -> - %% Filter out all nodes in the - %% allowed list that is in peer - %% certificate and that has got - %% the same IP address as the peer - allowed( - filter_nodes_by_ip( - CertNodes, Driver, PeerIP), - Allowed) + error_logger:error_msg( + "** Connection attempt from " + "disallowed node(s) ~p ** ~n", [PeerIP]), + ?shutdown2( + PeerIP, trace({is_allowed, not_allowed})); + AllowedNodes -> + AllowedNodes end; Error1 -> ?shutdown2(no_peer_ip, trace(Error1)) end; + {error,no_peercert} -> + Allowed; Error2 -> ?shutdown2(no_peer_cert, trace(Error2)) end. -allowed(CertNodes, []) -> - %% Empty allowed list means all allowed - %% -> allow only certificate nodes - CertNodes; -allowed(CertNodes, Allowed) -> - %% Find the intersection of the allowed list and certificate nodes - case - [CertNode || - CertNode <- CertNodes, - dist_util:is_allowed(CertNode, Allowed)] - of - [] -> - error_logger:error_msg( - "** Connection attempt from " - "disallowed node(s) ~p ** ~n", [CertNodes]), - ?shutdown2(CertNodes, trace({is_allowed, not_allowed})); - NewAllowed -> - NewAllowed +allowed_nodes(PeerCert, [], PeerIP) -> + case public_key:pkix_verify_hostname(PeerCert, [{ip,PeerIP}]) of + true -> + Host = inet:ntoa(PeerIP), + true = is_list(Host), + [Host]; + false -> + [] + end; +allowed_nodes(PeerCert, [Node|Allowed], PeerIP) -> + case dist_util:split_node(Node) of + {node,_,Host} -> + allowed_nodes(PeerCert, Allowed, PeerIP, Node, Host); + {host,Host} -> + allowed_nodes(PeerCert, Allowed, PeerIP, Node, Host); + _ -> + allowed_nodes(PeerCert, Allowed, PeerIP) + end. + +allowed_nodes(PeerCert, Allowed, PeerIP, Node, Host) -> + case public_key:pkix_verify_hostname(PeerCert, [{dns_id,Host}]) of + true -> + [Node|allowed_nodes(PeerCert, Allowed, PeerIP)]; + false -> + allowed_nodes(PeerCert, Allowed, PeerIP) end. + setup(Node, Type, MyNode, LongOrShortNames, SetupTime) -> gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames, SetupTime). @@ -541,15 +522,7 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> end. do_setup_connect(Driver, Kernel, Node, Address, Ip, TcpPort, Version, Type, MyNode, Timer) -> - Opts = - trace( - connect_options( - %% - %% Use verify_server/3 to verify that - %% the server's certificate is for Node - %% - setup_verify_server( - get_ssl_options(client), Node))), + Opts = trace(connect_options(get_ssl_options(client))), dist_util:reset_timer(Timer), case ssl:connect( Address, TcpPort, @@ -587,67 +560,6 @@ close(Socket) -> gen_close(Driver, Socket) -> trace(Driver:close(Socket)). -%% {verify_fun,{fun ?MODULE:verify_server/3,_}} is used -%% as a configuration marker that verify_server/3 shall be used. -%% -%% Replace the State in the first occurence of -%% {verify_fun,{fun ?MODULE:verify_server/3,State}} and remove the rest. -%% The inserted state is not accesible from a configuration file -%% since it is dynamic and connection dependent. -%% -setup_verify_server(Opts, Node) -> - setup_verify_server(Opts, Node, true). -%% -setup_verify_server([], _Node, _) -> - []; -setup_verify_server([Opt|Opts], Node, Once) -> - case Opt of - {verify_fun,{Fun,CertNodesFun}} -> - case Fun =:= fun ?MODULE:verify_server/3 of - true when not is_function(CertNodesFun, 1) -> - ?shutdown2( - Node, - {verify_server_bad_argument,CertNodesFun}); - true when Once -> - [{verify_fun,{Fun,{CertNodesFun,Node}}} - |setup_verify_server(Opts, Node, false)]; - true -> - setup_verify_server(Opts, Node, Once); - false -> - [Opt|setup_verify_server(Opts, Node, Once)] - end; - _ -> - [Opt|setup_verify_server(Opts, Node, Once)] - end. - -verify_server(_, {bad_cert,_} = Reason, _) -> - {fail,Reason}; -verify_server(_, {extension,_}, S) -> - {unknown,S}; -verify_server(_, valid, S) -> - {valid,S}; -verify_server(PeerCert, valid_peer, {CertNodesFun,Node} = S) -> - %% - %% Parse out all node names from the peer's certificate - %% - case CertNodesFun(PeerCert) of - undefined -> - %% Certificate allows all nodes - {valid,S}; - [] -> - %% Certificate allows no nodes - {fail,cert_missing_node_name}; - CertNodes -> - %% Check that the node we are connecting to - %% is in the peer certificate - case dist_util:is_allowed(Node, CertNodes) of - true -> - {valid,S}; - false -> - {fail,wrong_nodes_in_cert} - end - end. - %% ------------------------------------------------------------ %% Determine if EPMD module supports address resolving. Default diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 2aecb6836e..da281829cb 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -17,8 +17,8 @@ dtls_socket, dtls_v1, dtls_connection_sup, - dtls_udp_listener, - dtls_udp_sup, + dtls_packet_demux, + dtls_listener_sup, %% API ssl, %% Main API tls, %% TLS specific diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index fb13a1ce7e..565cc9e1bc 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -234,7 +234,7 @@ handshake(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts, Timeout) when handshake(#sslsocket{pid = Pid, fd = {_, _, _}} = Socket, SslOpts, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)-> try - {ok, EmOpts, _} = dtls_udp_listener:get_all_opts(Pid), + {ok, EmOpts, _} = dtls_packet_demux:get_all_opts(Pid), ssl_connection:handshake(Socket, {SslOpts, tls_socket:emulated_socket_options(EmOpts, #socket_options{})}, Timeout) catch @@ -283,8 +283,8 @@ handshake_cancel(Socket) -> %%-------------------------------------------------------------------- close(#sslsocket{pid = Pid}) when is_pid(Pid) -> ssl_connection:close(Pid, {close, ?DEFAULT_TIMEOUT}); -close(#sslsocket{pid = {udp, #config{udp_handler = {Pid, _}}}}) -> - dtls_udp_listener:close(Pid); +close(#sslsocket{pid = {dtls, #config{dtls_handler = {Pid, _}}}}) -> + dtls_packet_demux:close(Pid); close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}}}}) -> Transport:close(ListenSocket). @@ -311,10 +311,10 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _} %%-------------------------------------------------------------------- send(#sslsocket{pid = Pid}, Data) when is_pid(Pid) -> ssl_connection:send(Pid, Data); -send(#sslsocket{pid = {_, #config{transport_info={gen_udp, _, _, _}}}}, _) -> +send(#sslsocket{pid = {_, #config{transport_info={_, udp, _, _}}}}, _) -> {error,enotconn}; %% Emulate connection behaviour -send(#sslsocket{pid = {udp,_}}, _) -> - {error,enotconn}; +send(#sslsocket{pid = {dtls,_}}, _) -> + {error,enotconn}; %% Emulate connection behaviour send(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport, _, _, _}}}}, Data) -> Transport:send(ListenSocket, Data). %% {error,enotconn} @@ -329,7 +329,7 @@ recv(Socket, Length) -> recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid), (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)-> ssl_connection:recv(Pid, Length, Timeout); -recv(#sslsocket{pid = {udp,_}}, _, _) -> +recv(#sslsocket{pid = {dtls,_}}, _, _) -> {error,enotconn}; recv(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}, _,_) when is_port(Listen)-> @@ -343,7 +343,7 @@ recv(#sslsocket{pid = {Listen, %%-------------------------------------------------------------------- controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid), is_pid(NewOwner) -> ssl_connection:new_user(Pid, NewOwner); -controlling_process(#sslsocket{pid = {udp, _}}, +controlling_process(#sslsocket{pid = {dtls, _}}, NewOwner) when is_pid(NewOwner) -> ok; %% Meaningless but let it be allowed to conform with TLS controlling_process(#sslsocket{pid = {Listen, @@ -368,7 +368,7 @@ connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) -> end; connection_information(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> {error, enotconn}; -connection_information(#sslsocket{pid = {udp,_}}) -> +connection_information(#sslsocket{pid = {dtls,_}}) -> {error,enotconn}. %%-------------------------------------------------------------------- @@ -394,13 +394,11 @@ peername(#sslsocket{pid = Pid, fd = {Transport, Socket, _}}) when is_pid(Pid)-> dtls_socket:peername(Transport, Socket); peername(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid)-> tls_socket:peername(Transport, Socket); -peername(#sslsocket{pid = {udp = Transport, #config{udp_handler = {_Pid, _}}}}) -> - dtls_socket:peername(Transport, undefined); -peername(#sslsocket{pid = Pid, fd = {gen_udp= Transport, Socket, _, _}}) when is_pid(Pid) -> - dtls_socket:peername(Transport, Socket); +peername(#sslsocket{pid = {dtls, #config{dtls_handler = {_Pid, _}}}}) -> + dtls_socket:peername(dtls, undefined); peername(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}}) -> tls_socket:peername(Transport, ListenSocket); %% Will return {error, enotconn} -peername(#sslsocket{pid = {udp,_}}) -> +peername(#sslsocket{pid = {dtls,_}}) -> {error,enotconn}. %%-------------------------------------------------------------------- @@ -415,7 +413,7 @@ peercert(#sslsocket{pid = Pid}) when is_pid(Pid) -> Result -> Result end; -peercert(#sslsocket{pid = {udp, _}}) -> +peercert(#sslsocket{pid = {dtls, _}}) -> {error, enotconn}; peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> {error, enotconn}. @@ -565,7 +563,7 @@ eccs_filter_supported(Curves) -> %%-------------------------------------------------------------------- getopts(#sslsocket{pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) -> ssl_connection:get_opts(Pid, OptionTags); -getopts(#sslsocket{pid = {udp, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, OptionTags) when is_list(OptionTags) -> +getopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, OptionTags) when is_list(OptionTags) -> try dtls_socket:getopts(Transport, ListenSocket, OptionTags) of {ok, _} = Result -> Result; @@ -603,7 +601,7 @@ setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) -> _:_ -> {error, {options, {not_a_proplist, Options0}}} end; -setopts(#sslsocket{pid = {udp, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) -> +setopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) -> try dtls_socket:setopts(Transport, ListenSocket, Options) of ok -> ok; @@ -660,7 +658,7 @@ getstat(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}, Options) when is_ shutdown(#sslsocket{pid = {Listen, #config{transport_info = {Transport,_, _, _}}}}, How) when is_port(Listen) -> Transport:shutdown(Listen, How); -shutdown(#sslsocket{pid = {udp,_}},_) -> +shutdown(#sslsocket{pid = {dtls,_}},_) -> {error, enotconn}; shutdown(#sslsocket{pid = Pid}, How) -> ssl_connection:shutdown(Pid, How). @@ -672,8 +670,8 @@ shutdown(#sslsocket{pid = Pid}, How) -> %%-------------------------------------------------------------------- sockname(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}) when is_port(Listen) -> tls_socket:sockname(Transport, Listen); -sockname(#sslsocket{pid = {udp, #config{udp_handler = {Pid, _}}}}) -> - dtls_udp_listener:sockname(Pid); +sockname(#sslsocket{pid = {dtls, #config{dtls_handler = {Pid, _}}}}) -> + dtls_packet_demux:sockname(Pid); sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _}}) when is_pid(Pid) -> dtls_socket:sockname(Transport, Socket); sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid) -> @@ -707,7 +705,7 @@ versions() -> %%-------------------------------------------------------------------- renegotiate(#sslsocket{pid = Pid}) when is_pid(Pid) -> ssl_connection:renegotiation(Pid); -renegotiate(#sslsocket{pid = {udp,_}}) -> +renegotiate(#sslsocket{pid = {dtls,_}}) -> {error, enotconn}; renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> {error, enotconn}. @@ -722,7 +720,7 @@ renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> prf(#sslsocket{pid = Pid}, Secret, Label, Seed, WantedLength) when is_pid(Pid) -> ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength); -prf(#sslsocket{pid = {udp,_}}, _,_,_,_) -> +prf(#sslsocket{pid = {dtls,_}}, _,_,_,_) -> {error, enotconn}; prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) -> {error, enotconn}. @@ -795,8 +793,8 @@ supported_suites(anonymous, Version) -> do_listen(Port, #config{transport_info = {Transport, _, _, _}} = Config, tls_connection) -> tls_socket:listen(Transport, Port, Config); -do_listen(Port, #config{transport_info = {Transport, _, _, _}} = Config, dtls_connection) -> - dtls_socket:listen(Transport, Port, Config). +do_listen(Port, Config, dtls_connection) -> + dtls_socket:listen(Port, Config). %% Handle extra ssl options given to ssl_accept -spec handle_options([any()], #ssl_options{}) -> #ssl_options{} @@ -940,7 +938,8 @@ handle_options(Opts0, Role, Host) -> crl_check = handle_option(crl_check, Opts, false), crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}), max_handshake_size = handle_option(max_handshake_size, Opts, ?DEFAULT_MAX_HANDSHAKE_SIZE), - handshake = handle_option(handshake, Opts, full) + handshake = handle_option(handshake, Opts, full), + customize_hostname_check = handle_option(customize_hostname_check, Opts, []) }, CbInfo = proplists:get_value(cb_info, Opts, default_cb_info(Protocol)), @@ -956,7 +955,7 @@ handle_options(Opts0, Role, Host) -> client_preferred_next_protocols, log_alert, server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache, fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation, - max_handshake_size, handshake], + max_handshake_size, handshake, customize_hostname_check], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) end, Opts, SslOptions), @@ -1199,6 +1198,8 @@ validate_option(handshake, hello = Value) -> Value; validate_option(handshake, full = Value) -> Value; +validate_option(customize_hostname_check, Value) when is_list(Value) -> + Value; validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index a3333d35e9..dbd2ebf539 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -138,8 +138,8 @@ validate(_, {bad_cert, _} = Reason, _) -> {fail, Reason}; validate(_, valid, UserState) -> {valid, UserState}; -validate(Cert, valid_peer, UserState = {client, _,_, Hostname, _, _}) when Hostname =/= disable -> - verify_hostname(Hostname, Cert, UserState); +validate(Cert, valid_peer, UserState = {client, _,_, {Hostname, Customize}, _, _}) when Hostname =/= disable -> + verify_hostname(Hostname, Customize, Cert, UserState); validate(_, valid_peer, UserState) -> {valid, UserState}. @@ -333,12 +333,12 @@ new_trusteded_chain(DerCert, [_ | Rest]) -> new_trusteded_chain(_, []) -> unknown_ca. -verify_hostname({fallback, Hostname}, Cert, UserState) when is_list(Hostname) -> - case public_key:pkix_verify_hostname(Cert, [{dns_id, Hostname}]) of +verify_hostname({fallback, Hostname}, Customize, Cert, UserState) when is_list(Hostname) -> + case public_key:pkix_verify_hostname(Cert, [{dns_id, Hostname}], Customize) of true -> {valid, UserState}; false -> - case public_key:pkix_verify_hostname(Cert, [{ip, Hostname}]) of + case public_key:pkix_verify_hostname(Cert, [{ip, Hostname}], Customize) of true -> {valid, UserState}; false -> @@ -346,16 +346,16 @@ verify_hostname({fallback, Hostname}, Cert, UserState) when is_list(Hostname) -> end end; -verify_hostname({fallback, Hostname}, Cert, UserState) -> - case public_key:pkix_verify_hostname(Cert, [{ip, Hostname}]) of +verify_hostname({fallback, Hostname}, Customize, Cert, UserState) -> + case public_key:pkix_verify_hostname(Cert, [{ip, Hostname}], Customize) of true -> {valid, UserState}; false -> {fail, {bad_cert, hostname_check_failed}} end; -verify_hostname(Hostname, Cert, UserState) -> - case public_key:pkix_verify_hostname(Cert, [{dns_id, Hostname}]) of +verify_hostname(Hostname, Customize, Cert, UserState) -> + case public_key:pkix_verify_hostname(Cert, [{dns_id, Hostname}], Customize) of true -> {valid, UserState}; false -> diff --git a/lib/ssl/src/ssl_connection_sup.erl b/lib/ssl/src/ssl_connection_sup.erl index 1a1f43e683..1aa7c5844f 100644 --- a/lib/ssl/src/ssl_connection_sup.erl +++ b/lib/ssl/src/ssl_connection_sup.erl @@ -51,12 +51,12 @@ init([]) -> ListenOptionsTracker = listen_options_tracker_child_spec(), DTLSConnetionManager = dtls_connection_manager_child_spec(), - DTLSUdpListeners = dtls_udp_listeners_spec(), + DTLSListeners = dtls_listeners_spec(), {ok, {{one_for_one, 10, 3600}, [TLSConnetionManager, ListenOptionsTracker, DTLSConnetionManager, - DTLSUdpListeners + DTLSListeners ]}}. @@ -91,9 +91,9 @@ listen_options_tracker_child_spec() -> Type = supervisor, {Name, StartFunc, Restart, Shutdown, Type, Modules}. -dtls_udp_listeners_spec() -> - Name = dtls_udp_listener, - StartFunc = {dtls_udp_sup, start_link, []}, +dtls_listeners_spec() -> + Name = dtls_listener, + StartFunc = {dtls_listener_sup, start_link, []}, Restart = permanent, Shutdown = 4000, Modules = [], diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index ebbb633b22..71eeb00183 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -345,6 +345,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, Opts#ssl_options.partial_chain), ValidationFunAndState = validation_fun_and_state(Opts#ssl_options.verify_fun, Role, CertDbHandle, CertDbRef, ServerName, + Opts#ssl_options.customize_hostname_check, Opts#ssl_options.crl_check, CRLDbHandle, CertPath), case public_key:pkix_path_validation(TrustedCert, CertPath, @@ -1243,7 +1244,7 @@ certificate_authorities_from_db(_CertDbHandle, {extracted, CertDbData}) -> %%-------------Handle handshake messages -------------------------------- validation_fun_and_state({Fun, UserState0}, Role, CertDbHandle, CertDbRef, - ServerNameIndication, CRLCheck, CRLDbHandle, CertPath) -> + ServerNameIndication, CustomizeHostCheck, CRLCheck, CRLDbHandle, CertPath) -> {fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) -> case ssl_certificate:validate(OtpCert, Extension, @@ -1260,9 +1261,9 @@ validation_fun_and_state({Fun, UserState0}, Role, CertDbHandle, CertDbRef, (OtpCert, VerifyResult, {SslState, UserState}) -> apply_user_fun(Fun, OtpCert, VerifyResult, UserState, SslState, CertPath) - end, {{Role, CertDbHandle, CertDbRef, ServerNameIndication, CRLCheck, CRLDbHandle}, UserState0}}; + end, {{Role, CertDbHandle, CertDbRef, {ServerNameIndication, CustomizeHostCheck}, CRLCheck, CRLDbHandle}, UserState0}}; validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef, - ServerNameIndication, CRLCheck, CRLDbHandle, CertPath) -> + ServerNameIndication, CustomizeHostCheck, CRLCheck, CRLDbHandle, CertPath) -> {fun(OtpCert, {extension, _} = Extension, SslState) -> ssl_certificate:validate(OtpCert, Extension, @@ -1282,7 +1283,7 @@ validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef, ssl_certificate:validate(OtpCert, VerifyResult, SslState) - end, {Role, CertDbHandle, CertDbRef, ServerNameIndication, CRLCheck, CRLDbHandle}}. + end, {Role, CertDbHandle, CertDbRef, {ServerNameIndication, CustomizeHostCheck}, CRLCheck, CRLDbHandle}}. apply_user_fun(Fun, OtpCert, VerifyResult, UserState0, {_, CertDbHandle, CertDbRef, _, CRLCheck, CRLDbHandle} = SslState, CertPath) when diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 5df00de0e5..b736047678 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -145,7 +145,8 @@ eccs, honor_ecc_order :: boolean(), max_handshake_size :: integer(), - handshake + handshake, + customize_hostname_check }). -record(socket_options, @@ -160,7 +161,7 @@ -record(config, {ssl, %% SSL parameters inet_user, %% User set inet options emulated, %% Emulated option list or "inherit_tracker" pid - udp_handler, + dtls_handler, inet_ssl, %% inet options for internal ssl socket transport_info, %% Callback info connection_cb |