aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/notes.xml15
-rw-r--r--lib/ssl/doc/src/ssl.xml22
-rw-r--r--lib/ssl/src/dtls_connection.erl31
-rw-r--r--lib/ssl/src/dtls_handshake.erl3
-rw-r--r--lib/ssl/src/dtls_record.erl55
-rw-r--r--lib/ssl/src/ssl.erl33
-rw-r--r--lib/ssl/src/ssl_certificate.erl13
-rw-r--r--lib/ssl/src/ssl_connection.erl9
-rw-r--r--lib/ssl/src/ssl_handshake.erl57
-rw-r--r--lib/ssl/src/tls_handshake.erl2
-rw-r--r--lib/ssl/test/make_certs.erl12
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_bench_SUITE.erl20
-rw-r--r--lib/ssl/test/ssl_test_lib.erl18
-rw-r--r--lib/ssl/test/x509_test.erl25
15 files changed, 221 insertions, 98 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index d8e26d635f..29ec3f9d57 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,21 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Remove debug printout</p>
+ <p>
+ Own Id: OTP-14396</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 8.1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 2c09122fe6..2940ccb1e7 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -127,7 +127,7 @@
<item><p><c>hostname() | ipaddress()</c></p></item>
<tag><c>hostname() =</c></tag>
- <item><p><c>string()</c></p></item>
+ <item><p><c>string() - DNS hostname</c></p></item>
<tag><c>ip_address() =</c></tag>
<item><p><c>{N1,N2,N3,N4} % IPv4 | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6
@@ -249,7 +249,7 @@
be PEER, CA, ROOT-CA; if 2 the path can be PEER, CA, CA,
ROOT-CA, and so on. The default value is 1.</p></item>
- <tag><c>{verify_fun, {Verifyfun :: fun(), InitialUserState ::
+ <tag><marker id="verify_fun"/><c>{verify_fun, {Verifyfun :: fun(), InitialUserState ::
term()}}</c></tag>
<item><p>The verification fun is to be defined as follows:</p>
@@ -582,15 +582,23 @@ fun(srp, Username :: string(), UserState :: term()) ->
<item><p>Specifies the username and password to use to authenticate
to the server.</p></item>
- <tag><c>{server_name_indication, hostname()}</c></tag>
- <item><p>Can be specified when upgrading a TCP socket to a TLS
- socket to use the TLS Server Name Indication extension.</p></item>
+ <tag><c>{server_name_indication, HostName :: hostname()}</c></tag>
+ <item><p>Specify the hostname to be used in TLS Server Name Indication extension.
+ Is usefull when upgrading a TCP socket to a TLS socket or if the hostname can not be
+ derived from the Host argument to <seealso marker="ssl#connect-3">ssl:connect/3</seealso>.
+ Will also cause the client to preform host name verification of the peer certificate
+ <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname(PeerCert, [{dns_id, HostName}])</seealso>
+ </p> during the x509-path validation. If the check fails the error {bad_cert, hostname_check_failiure} will be
+ propagated to the path validation fun <seealso marker="#verify_fun">verify_fun</seealso>
+ </item>
<tag><c>{server_name_indication, disable}</c></tag>
<item>
<p>When starting a TLS connection without upgrade, the Server Name
- Indication extension is sent if possible. This option can be
- used to disable that behavior.</p>
+ Indication extension is sent if possible that is can be derived from the Host argument
+ to <seealso marker="ssl#connect-3">ssl:connect/3</seealso>.
+ This option can be used to disable that behavior.</p>
+ <note><p> Note that this also disables the default host name verification check of the peer certificate.</p></note>
</item>
<tag><c>{fallback, boolean()}</c></tag>
<item>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 9937373e6e..e6a0925ceb 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -688,16 +688,18 @@ next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
{no_record, State#state{unprocessed_handshake_events = N-1}};
next_record(#state{protocol_buffers =
- #protocol_buffers{dtls_cipher_texts = [CT | Rest]}
+ #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} = CT | Rest]}
= Buffers,
- connection_states = ConnStates0} = State) ->
- case dtls_record:decode_cipher_text(CT, ConnStates0) of
- {Plain, ConnStates} ->
- {Plain, State#state{protocol_buffers =
- Buffers#protocol_buffers{dtls_cipher_texts = Rest},
- connection_states = ConnStates}};
- #alert{} = Alert ->
- {Alert, State}
+ connection_states = ConnectionStates} = State) ->
+ CurrentRead = dtls_record:get_connection_state_by_epoch(Epoch, ConnectionStates, read),
+ case dtls_record:replay_detect(CT, CurrentRead) of
+ false ->
+ decode_cipher_text(State#state{connection_states = ConnectionStates}) ;
+ true ->
+ %% Ignore replayed record
+ next_record(State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnectionStates})
end;
next_record(#state{role = server,
socket = {Listener, {Client, _}},
@@ -770,6 +772,17 @@ next_event(StateName, Record,
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end.
+decode_cipher_text(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts = [ CT | Rest]} = Buffers,
+ connection_states = ConnStates0} = State) ->
+ case dtls_record:decode_cipher_text(CT, ConnStates0) of
+ {Plain, ConnStates} ->
+ {Plain, State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnStates}};
+ #alert{} = Alert ->
+ {Alert, State}
+ end.
+
dtls_version(hello, Version, #state{role = server} = State) ->
State#state{negotiated_version = Version}; %%Inital version
dtls_version(_,_, State) ->
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 1ed63f8a83..37a46b862e 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -65,9 +65,8 @@ client_hello(Host, Port, Cookie, ConnectionStates,
TLSVersion = dtls_v1:corresponding_tls_version(Version),
CipherSuites = ssl_handshake:available_suites(UserSuites, TLSVersion),
- Extensions = ssl_handshake:client_hello_extensions(Host, TLSVersion, CipherSuites,
+ Extensions = ssl_handshake:client_hello_extensions(TLSVersion, CipherSuites,
SslOpts, ConnectionStates, Renegotiation),
-
Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
#client_hello{session_id = Id,
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 6a418c6fb1..8a7f8c1d0a 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -46,7 +46,7 @@
is_higher/2, supported_protocol_versions/0,
is_acceptable_version/2, hello_version/2]).
--export([save_current_connection_state/2, next_epoch/2]).
+-export([save_current_connection_state/2, next_epoch/2, get_connection_state_by_epoch/3, replay_detect/2]).
-export([init_connection_state_seq/2, current_connection_state_epoch/2]).
@@ -55,6 +55,8 @@
-type dtls_version() :: ssl_record:ssl_version().
-type dtls_atom_version() :: dtlsv1 | 'dtlsv1.2'.
+-define(REPLAY_WINDOW_SIZE, 64).
+
-compile(inline).
%%====================================================================
@@ -73,7 +75,7 @@ init_connection_states(Role, BeastMitigation) ->
Initial = initial_connection_state(ConnectionEnd, BeastMitigation),
Current = Initial#{epoch := 0},
InitialPending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation),
- Pending = InitialPending#{epoch => undefined},
+ Pending = InitialPending#{epoch => undefined, replay_window => init_replay_window(?REPLAY_WINDOW_SIZE)},
#{saved_read => Current,
current_read => Current,
pending_read => Pending,
@@ -96,11 +98,13 @@ save_current_connection_state(#{current_write := Current} = States, write) ->
next_epoch(#{pending_read := Pending,
current_read := #{epoch := Epoch}} = States, read) ->
- States#{pending_read := Pending#{epoch := Epoch + 1}};
+ States#{pending_read := Pending#{epoch := Epoch + 1,
+ replay_window := init_replay_window(?REPLAY_WINDOW_SIZE)}};
next_epoch(#{pending_write := Pending,
current_write := #{epoch := Epoch}} = States, write) ->
- States#{pending_write := Pending#{epoch := Epoch + 1}}.
+ States#{pending_write := Pending#{epoch := Epoch + 1,
+ replay_window := init_replay_window(?REPLAY_WINDOW_SIZE)}}.
get_connection_state_by_epoch(Epoch, #{current_write := #{epoch := Epoch} = Current},
write) ->
@@ -411,6 +415,7 @@ hello_version(Version, Versions) ->
lowest_protocol_version(Versions)
end.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -419,6 +424,7 @@ initial_connection_state(ConnectionEnd, BeastMitigation) ->
ssl_record:initial_security_params(ConnectionEnd),
epoch => undefined,
sequence_number => 0,
+ replay_window => init_replay_window(?REPLAY_WINDOW_SIZE),
beast_mitigation => BeastMitigation,
compression_state => undefined,
cipher_state => undefined,
@@ -499,8 +505,9 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
{PlainFragment, CipherState} ->
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ReadState = ReadState0#{compression_state => CompressionS1,
+ ReadState0 = ReadState0#{compression_state => CompressionS1,
cipher_state => CipherState},
+ ReadState = update_replay_window(Seq, ReadState0),
ConnnectionStates = set_connection_state_by_epoch(ReadState, Epoch, ConnnectionStates0, read),
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
#alert{} = Alert ->
@@ -523,7 +530,8 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
{Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
PlainFragment, CompressionS0),
- ReadState = ReadState1#{compression_state => CompressionS1},
+ ReadState2 = ReadState1#{compression_state => CompressionS1},
+ ReadState = update_replay_window(Seq, ReadState2),
ConnnectionStates = set_connection_state_by_epoch(ReadState, Epoch, ConnnectionStates0, read),
{CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
false ->
@@ -555,3 +563,38 @@ mac_hash({Major, Minor}, MacAlg, MacSecret, Epoch, SeqNo, Type, Length, Fragment
calc_aad(Type, {MajVer, MinVer}, Epoch, SeqNo) ->
<<?UINT16(Epoch), ?UINT48(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
+
+init_replay_window(Size) ->
+ #{size => Size,
+ top => Size,
+ bottom => 0,
+ mask => 0 bsl 64
+ }.
+
+replay_detect(#ssl_tls{sequence_number = SequenceNumber}, #{replay_window := Window}) ->
+ is_replay(SequenceNumber, Window).
+
+
+is_replay(SequenceNumber, #{bottom := Bottom}) when SequenceNumber < Bottom ->
+ true;
+is_replay(SequenceNumber, #{size := Size,
+ top := Top,
+ bottom := Bottom,
+ mask := Mask}) when (SequenceNumber >= Bottom) andalso (SequenceNumber =< Top) ->
+ Index = (SequenceNumber rem Size),
+ (Index band Mask) == 1;
+
+is_replay(_, _) ->
+ false.
+
+update_replay_window(SequenceNumber, #{replay_window := #{size := Size,
+ top := Top,
+ bottom := Bottom,
+ mask := Mask0} = Window0} = ConnectionStates) ->
+ NoNewBits = SequenceNumber - Top,
+ Index = SequenceNumber rem Size,
+ Mask = (Mask0 bsl NoNewBits) bor Index,
+ Window = Window0#{top => SequenceNumber,
+ bottom => Bottom + NoNewBits,
+ mask => Mask},
+ ConnectionStates#{replay_window := Window}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index dfee4b2243..d9e47c43ad 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -112,7 +112,7 @@ connect(Host, Port, Options) ->
connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
try
- {ok, Config} = handle_options(Options, client),
+ {ok, Config} = handle_options(Options, client, Host),
case Config#config.connection_cb of
tls_connection ->
tls_socket:connect(Host,Port,Config,Timeout);
@@ -632,8 +632,12 @@ do_listen(Port, #config{transport_info = {Transport, _, _, _}} = Config, dtls_c
%% Handle extra ssl options given to ssl_accept
-spec handle_options([any()], #ssl_options{}) -> #ssl_options{}
; ([any()], client | server) -> {ok, #config{}}.
+handle_options(Opts, Role) ->
+ handle_options(Opts, Role, undefined).
+
+
handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
- cacertfile = CaCertFile0} = InheritedSslOpts) ->
+ cacertfile = CaCertFile0} = InheritedSslOpts, _) ->
RecordCB = record_cb(Protocol),
CaCerts = handle_option(cacerts, Opts0, CaCerts0),
{Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder,
@@ -666,7 +670,7 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
end;
%% Handle all options in listen and connect
-handle_options(Opts0, Role) ->
+handle_options(Opts0, Role, Host) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
assert_proplist(Opts),
@@ -738,7 +742,9 @@ handle_options(Opts0, Role) ->
make_next_protocol_selector(
handle_option(client_preferred_next_protocols, Opts, undefined)),
log_alert = handle_option(log_alert, Opts, true),
- server_name_indication = handle_option(server_name_indication, Opts, undefined),
+ server_name_indication = handle_option(server_name_indication, Opts,
+ default_option_role(client,
+ server_name_indication_default(Host), Role)),
sni_hosts = handle_option(sni_hosts, Opts, []),
sni_fun = handle_option(sni_fun, Opts, undefined),
honor_cipher_order = handle_option(honor_cipher_order, Opts,
@@ -982,12 +988,20 @@ validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) ->
validate_option(next_protocols_advertised, undefined) ->
undefined;
-validate_option(server_name_indication, Value) when is_list(Value) ->
+validate_option(server_name_indication = Opt, Value) when is_list(Value) ->
+ %% RFC 6066, Section 3: Currently, the only server names supported are
+ %% DNS hostnames
+ case inet_parse:domain(Value) of
+ false ->
+ throw({error, {options, {{Opt, Value}}}});
+ true ->
+ Value
+ end;
+validate_option(server_name_indication, undefined = Value) ->
Value;
validate_option(server_name_indication, disable) ->
- disable;
-validate_option(server_name_indication, undefined) ->
undefined;
+
validate_option(sni_hosts, []) ->
[];
validate_option(sni_hosts, [{Hostname, SSLOptions} | Tail]) when is_list(Hostname) ->
@@ -1445,3 +1459,8 @@ include_security_info([Item | Items]) ->
false ->
include_security_info(Items)
end.
+
+server_name_indication_default(Host) when is_list(Host) ->
+ Host;
+server_name_indication_default(_) ->
+ undefined.
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 2046ec75b3..0dd5e5c5cf 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -125,7 +125,7 @@ file_to_crls(File, DbHandle) ->
%% Description: Validates ssl/tls specific extensions
%%--------------------------------------------------------------------
validate(_,{extension, #'Extension'{extnID = ?'id-ce-extKeyUsage',
- extnValue = KeyUse}}, UserState = {Role, _,_, _, _}) ->
+ extnValue = KeyUse}}, UserState = {Role, _,_, _, _, _}) ->
case is_valid_extkey_usage(KeyUse, Role) of
true ->
{valid, UserState};
@@ -138,8 +138,15 @@ validate(_, {bad_cert, _} = Reason, _) ->
{fail, Reason};
validate(_, valid, UserState) ->
{valid, UserState};
-validate(_, valid_peer, UserState) ->
- {valid, UserState}.
+validate(Cert, valid_peer, UserState = {client, _,_, Hostname, _, _}) when Hostname =/= undefined ->
+ case public_key:pkix_verify_hostname(Cert, [{dns_id, Hostname}]) of
+ true ->
+ {valid, UserState};
+ false ->
+ {fail, {bad_cert, hostname_check_failed}}
+ end;
+validate(_, valid_peer, UserState) ->
+ {valid, UserState}.
%%--------------------------------------------------------------------
-spec is_valid_key_usage(list(), term()) -> boolean().
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index d9707115d5..e935e07b65 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -501,13 +501,7 @@ certify(internal, #certificate{} = Cert,
crl_db = CRLDbInfo,
ssl_options = Opts} = State, Connection) ->
case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef,
- Opts#ssl_options.depth,
- Opts#ssl_options.verify,
- Opts#ssl_options.verify_fun,
- Opts#ssl_options.partial_chain,
- Opts#ssl_options.crl_check,
- CRLDbInfo,
- Role) of
+ Opts, CRLDbInfo, Role) of
{PeerCert, PublicKeyInfo} ->
handle_peer_cert(Role, PeerCert, PublicKeyInfo,
State#state{client_certificate_requested = false}, Connection);
@@ -848,7 +842,6 @@ handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName,
StateName, State);
handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State,
_) ->
- ct:pal("Unexpected msg ~p", [Msg]),
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
handle_own_alert(Alert, Version, {StateName, Msg}, State).
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 2520fee238..58db8449d6 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -50,7 +50,7 @@
finished/5, next_protocol/1]).
%% Handle handshake messages
--export([certify/10, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
+-export([certify/6, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
master_secret/4, server_key_exchange_hash/2, verify_connection/6,
init_handshake_history/0, update_handshake_history/3, verify_server_key/5
]).
@@ -68,7 +68,7 @@
select_session/11, supported_ecc/1, available_signature_algs/4]).
%% Extensions handling
--export([client_hello_extensions/6,
+-export([client_hello_extensions/5,
handle_client_hello_extensions/9, %% Returns server hello extensions
handle_server_hello_extensions/9, select_curve/2, select_curve/3
]).
@@ -119,7 +119,7 @@ server_hello(SessionId, Version, ConnectionStates, Extensions) ->
server_hello_done() ->
#server_hello_done{}.
-client_hello_extensions(Host, Version, CipherSuites,
+client_hello_extensions(Version, CipherSuites,
#ssl_options{signature_algs = SupportedHashSigns,
eccs = SupportedECCs} = SslOpts, ConnectionStates, Renegotiation) ->
{EcPointFormats, EllipticCurves} =
@@ -142,7 +142,7 @@ client_hello_extensions(Host, Version, CipherSuites,
next_protocol_negotiation =
encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector,
Renegotiation),
- sni = sni(Host, SslOpts#ssl_options.server_name_indication)}.
+ sni = sni(SslOpts#ssl_options.server_name_indication)}.
%%--------------------------------------------------------------------
-spec certificate(der_cert(), db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}.
@@ -388,24 +388,26 @@ verify_signature(_, Hash, {HashAlgo, _SignAlg}, Signature,
%%--------------------------------------------------------------------
--spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit,
- verify_peer | verify_none, {fun(), term}, fun(), term(), term(),
+-spec certify(#certificate{}, db_handle(), certdb_ref(), #ssl_options{}, term(),
client | server) -> {der_cert(), public_key_info()} | #alert{}.
%%
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
- MaxPathLen, _Verify, ValidationFunAndState0, PartialChain, CRLCheck, CRLDbHandle, Role) ->
+ Opts, CRLDbHandle, Role) ->
+
[PeerCert | _] = ASN1Certs,
try
{TrustedCert, CertPath} =
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef, PartialChain),
- ValidationFunAndState = validation_fun_and_state(ValidationFunAndState0, Role,
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef,
+ Opts#ssl_options.partial_chain),
+ ValidationFunAndState = validation_fun_and_state(Opts#ssl_options.verify_fun, Role,
CertDbHandle, CertDbRef,
- CRLCheck, CRLDbHandle, CertPath),
+ Opts#ssl_options.server_name_indication,
+ Opts#ssl_options.crl_check, CRLDbHandle, CertPath),
case public_key:pkix_path_validation(TrustedCert,
CertPath,
- [{max_path_length, MaxPathLen},
+ [{max_path_length, Opts#ssl_options.depth},
{verify_fun, ValidationFunAndState}]) of
{ok, {PublicKeyInfo,_}} ->
{PeerCert, PublicKeyInfo};
@@ -1522,25 +1524,16 @@ select_shared_curve([Curve | Rest], Curves) ->
select_shared_curve(Rest, Curves)
end.
-%% RFC 6066, Section 3: Currently, the only server names supported are
-%% DNS hostnames
-sni(_, disable) ->
+sni(undefined) ->
undefined;
-sni(Host, undefined) ->
- sni1(Host);
-sni(_Host, SNIOption) ->
- sni1(SNIOption).
-
-sni1(Hostname) ->
- case inet_parse:domain(Hostname) of
- false -> undefined;
- true -> #sni{hostname = Hostname}
- end.
+sni(Hostname) ->
+ #sni{hostname = Hostname}.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
validation_fun_and_state({Fun, UserState0}, Role, CertDbHandle, CertDbRef,
- CRLCheck, CRLDbHandle, CertPath) ->
+ ServerNameIndication, CRLCheck, CRLDbHandle, CertPath) ->
{fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) ->
case ssl_certificate:validate(OtpCert,
Extension,
@@ -1557,9 +1550,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, CRLCheck, CRLDbHandle}, UserState0}};
+ end, {{Role, CertDbHandle, CertDbRef, ServerNameIndication, CRLCheck, CRLDbHandle}, UserState0}};
validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef,
- CRLCheck, CRLDbHandle, CertPath) ->
+ ServerNameIndication, CRLCheck, CRLDbHandle, CertPath) ->
{fun(OtpCert, {extension, _} = Extension, SslState) ->
ssl_certificate:validate(OtpCert,
Extension,
@@ -1568,8 +1561,10 @@ validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef,
(VerifyResult == valid_peer) ->
case crl_check(OtpCert, CRLCheck, CertDbHandle, CertDbRef,
CRLDbHandle, VerifyResult, CertPath) of
- valid ->
- {VerifyResult, SslState};
+ valid ->
+ ssl_certificate:validate(OtpCert,
+ VerifyResult,
+ SslState);
Reason ->
{fail, Reason}
end;
@@ -1577,10 +1572,10 @@ validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef,
ssl_certificate:validate(OtpCert,
VerifyResult,
SslState)
- end, {Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle}}.
+ end, {Role, CertDbHandle, CertDbRef, ServerNameIndication, CRLCheck, CRLDbHandle}}.
apply_user_fun(Fun, OtpCert, VerifyResult, UserState0,
- {_, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle} = SslState, CertPath) when
+ {_, CertDbHandle, CertDbRef, _, CRLCheck, CRLDbHandle} = SslState, CertPath) when
(VerifyResult == valid) or (VerifyResult == valid_peer) ->
case Fun(OtpCert, VerifyResult, UserState0) of
{Valid, UserState} when (Valid == valid) or (Valid == valid_peer) ->
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 9da7b43be3..b54540393a 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -56,7 +56,7 @@ client_hello(Host, Port, ConnectionStates,
Version = tls_record:highest_protocol_version(Versions),
#{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read),
AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version),
- Extensions = ssl_handshake:client_hello_extensions(Host, Version,
+ Extensions = ssl_handshake:client_hello_extensions(Version,
AvailableCipherSuites,
SslOpts, ConnectionStates, Renegotiation),
CipherSuites =
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index e14f7f60c4..74505169a0 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -385,6 +385,7 @@ req_cnf(Root, C) ->
"subjectAltName = email:copy\n"].
ca_cnf(Root, C = #config{issuing_distribution_point = true}) ->
+ Hostname = net_adm:localhost(),
["# Purpose: Configuration for CAs.\n"
"\n"
"ROOTDIR = " ++ Root ++ "\n"
@@ -434,7 +435,7 @@ ca_cnf(Root, C = #config{issuing_distribution_point = true}) ->
"keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
"subjectKeyIdentifier = hash\n"
"authorityKeyIdentifier = keyid,issuer:always\n"
- "subjectAltName = email:copy\n"
+ "subjectAltName = DNS.1:" ++ Hostname ++ "\n"
"issuerAltName = issuer:copy\n"
"crlDistributionPoints=@crl_section\n"
@@ -449,7 +450,7 @@ ca_cnf(Root, C = #config{issuing_distribution_point = true}) ->
"keyUsage = digitalSignature\n"
"subjectKeyIdentifier = hash\n"
"authorityKeyIdentifier = keyid,issuer:always\n"
- "subjectAltName = email:copy\n"
+ "subjectAltName = DNS.1:" ++ Hostname ++ "\n"
"issuerAltName = issuer:copy\n"
"\n"
@@ -458,12 +459,13 @@ ca_cnf(Root, C = #config{issuing_distribution_point = true}) ->
"keyUsage = cRLSign, keyCertSign\n"
"subjectKeyIdentifier = hash\n"
"authorityKeyIdentifier = keyid:always,issuer:always\n"
- "subjectAltName = email:copy\n"
+ "subjectAltName = DNS.1:" ++ Hostname ++ "\n"
"issuerAltName = issuer:copy\n"
"crlDistributionPoints=@crl_section\n"
];
ca_cnf(Root, C = #config{issuing_distribution_point = false}) ->
+ Hostname = net_adm:localhost(),
["# Purpose: Configuration for CAs.\n"
"\n"
"ROOTDIR = " ++ Root ++ "\n"
@@ -513,7 +515,7 @@ ca_cnf(Root, C = #config{issuing_distribution_point = false}) ->
"keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
"subjectKeyIdentifier = hash\n"
"authorityKeyIdentifier = keyid,issuer:always\n"
- "subjectAltName = email:copy\n"
+ "subjectAltName = DNS.1:" ++ Hostname ++ "\n"
"issuerAltName = issuer:copy\n"
%"crlDistributionPoints=@crl_section\n"
@@ -528,7 +530,7 @@ ca_cnf(Root, C = #config{issuing_distribution_point = false}) ->
"keyUsage = digitalSignature\n"
"subjectKeyIdentifier = hash\n"
"authorityKeyIdentifier = keyid,issuer:always\n"
- "subjectAltName = email:copy\n"
+ "subjectAltName = DNS.1:" ++ Hostname ++ "\n"
"issuerAltName = issuer:copy\n"
"\n"
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 018110514c..58870a3419 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -2880,10 +2880,10 @@ der_input(Config) when is_list(Config) ->
Size = ets:info(CADb, size),
- SeverVerifyOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ SeverVerifyOpts = ssl_test_lib:ssl_options(server_opts, Config),
{ServerCert, ServerKey, ServerCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
SeverVerifyOpts]),
- ClientVerifyOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientVerifyOpts = ssl_test_lib:ssl_options(client_opts, Config),
{ClientCert, ClientKey, ClientCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
ClientVerifyOpts]),
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true},
diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl
index 960ddf7808..ae2928b1c3 100644
--- a/lib/ssl/test/ssl_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_bench_SUITE.erl
@@ -410,13 +410,19 @@ ssl_opts(connect_der) ->
[{verify, verify_peer} | ssl_opts("client_der")];
ssl_opts(Role) ->
CertData = cert_data(Role),
- [{active, false},
- {depth, 2},
- {reuseaddr, true},
- {mode,binary},
- {nodelay, true},
- {ciphers, [{dhe_rsa,aes_256_cbc,sha}]}
- |CertData].
+ Opts = [{active, false},
+ {depth, 2},
+ {reuseaddr, true},
+ {mode,binary},
+ {nodelay, true},
+ {ciphers, [{dhe_rsa,aes_256_cbc,sha}]}
+ |CertData],
+ case Role of
+ "client" ++ _ ->
+ [{server_name_indication, disable} | Opts];
+ "server" ++ _ ->
+ Opts
+ end.
cert_data(Der) when Der =:= "server_der"; Der =:= "client_der" ->
[Role,_] = string:tokens(Der, "_"),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index f44d1d5523..68a99c28dd 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -34,13 +34,13 @@
run_where(_) ->
ClientNode = node(),
ServerNode = node(),
- {ok, Host} = rpc:call(ServerNode, inet, gethostname, []),
+ Host = rpc:call(ServerNode, net_adm, localhost, []),
{ClientNode, ServerNode, Host}.
run_where(_, ipv6) ->
ClientNode = node(),
ServerNode = node(),
- {ok, Host} = rpc:call(ServerNode, inet, gethostname, []),
+ Host = rpc:call(ServerNode, net_adm, localhost, []),
{ClientNode, ServerNode, Host}.
node_to_hostip(Node) ->
@@ -1394,6 +1394,18 @@ portable_open_port(Exe, Args) ->
open_port({spawn_executable, AbsPath},
[{args, Args}, stderr_to_stdout]).
+supports_ssl_tls_version(sslv2 = Version) ->
+ case os:cmd("openssl version") of
+ "OpenSSL 1" ++ _ ->
+ false;
+ _ ->
+ VersionFlag = version_flag(Version),
+ Exe = "openssl",
+ Args = ["s_client", VersionFlag],
+ Port = ssl_test_lib:portable_open_port(Exe, Args),
+ do_supports_ssl_tls_version(Port)
+ end;
+
supports_ssl_tls_version(Version) ->
VersionFlag = version_flag(Version),
Exe = "openssl",
@@ -1403,6 +1415,8 @@ supports_ssl_tls_version(Version) ->
do_supports_ssl_tls_version(Port) ->
receive
+ {Port, {data, "u"}} ->
+ false;
{Port, {data, "unknown option" ++ _}} ->
false;
{Port, {data, Data}} ->
diff --git a/lib/ssl/test/x509_test.erl b/lib/ssl/test/x509_test.erl
index c36e96013b..4da1537ef6 100644
--- a/lib/ssl/test/x509_test.erl
+++ b/lib/ssl/test/x509_test.erl
@@ -105,7 +105,7 @@ root_cert(Role, PrivKey, Opts) ->
validity = validity(Opts),
subject = Issuer,
subjectPublicKeyInfo = public_key(PrivKey),
- extensions = extensions(ca, Opts)
+ extensions = extensions(Role, ca, Opts)
},
public_key:pkix_sign(OTPTBS, PrivKey).
@@ -175,22 +175,27 @@ validity(Opts) ->
#'Validity'{notBefore={generalTime, Format(DefFrom)},
notAfter ={generalTime, Format(DefTo)}}.
-extensions(Type, Opts) ->
+extensions(Role, Type, Opts) ->
Exts = proplists:get_value(extensions, Opts, []),
- lists:flatten([extension(Ext) || Ext <- default_extensions(Type, Exts)]).
+ lists:flatten([extension(Ext) || Ext <- default_extensions(Role, Type, Exts)]).
%% Common extension: name_constraints, policy_constraints, ext_key_usage, inhibit_any,
%% auth_key_id, subject_key_id, policy_mapping,
-default_extensions(ca, Exts) ->
+default_extensions(_, ca, Exts) ->
Def = [{key_usage, [keyCertSign, cRLSign]},
{basic_constraints, default}],
add_default_extensions(Def, Exts);
-default_extensions(peer, Exts) ->
- Def = [{key_usage, [digitalSignature, keyAgreement]}],
- add_default_extensions(Def, Exts).
+default_extensions(server, peer, Exts) ->
+ Hostname = net_adm:localhost(),
+ Def = [{key_usage, [digitalSignature, keyAgreement]},
+ {subject_alt, Hostname}],
+ add_default_extensions(Def, Exts);
+default_extensions(_, peer, Exts) ->
+ Exts.
+
add_default_extensions(Def, Exts) ->
Filter = fun({Key, _}, D) ->
lists:keydelete(Key, 1, D);
@@ -228,6 +233,10 @@ extension({key_usage, Value}) ->
#'Extension'{extnID = ?'id-ce-keyUsage',
extnValue = Value,
critical = false};
+extension({subject_alt, Hostname}) ->
+ #'Extension'{extnID = ?'id-ce-subjectAltName',
+ extnValue = [{dNSName, Hostname}],
+ critical = false};
extension({Id, Data, Critical}) ->
#'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
@@ -309,7 +318,7 @@ cert(Role, #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = Iss
validity = validity(CertOpts),
subject = subject(Contact, atom_to_list(Role) ++ Name),
subjectPublicKeyInfo = public_key(Key),
- extensions = extensions(Type,
+ extensions = extensions(Role, Type,
add_default_extensions([{auth_key_id, {auth_key_oid(Role), Issuer, SNr}}],
CertOpts))
},