diff options
Diffstat (limited to 'lib/ssl/src')
| -rw-r--r-- | lib/ssl/src/ssl.erl | 8 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_alert.erl | 4 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_alert.hrl | 4 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_cipher.erl | 5 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_cipher.hrl | 6 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 3 | ||||
| -rw-r--r-- | lib/ssl/src/tls_handshake.erl | 82 | ||||
| -rw-r--r-- | lib/ssl/src/tls_record.erl | 9 | 
8 files changed, 83 insertions, 38 deletions
| diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 4b7f49547b..5f4ad7f013 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -657,7 +657,8 @@ handle_options(Opts0) ->  		    server_name_indication = handle_option(server_name_indication, Opts, undefined),  		    honor_cipher_order = handle_option(honor_cipher_order, Opts, false),  		    protocol = proplists:get_value(protocol, Opts, tls), -		    padding_check =  proplists:get_value(padding_check, Opts, true) +		    padding_check =  proplists:get_value(padding_check, Opts, true), +		    fallback =  proplists:get_value(fallback, Opts, false)  		   },      CbInfo  = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}), @@ -670,7 +671,8 @@ handle_options(Opts0) ->  		  cb_info, renegotiate_at, secure_renegotiate, hibernate_after,  		  erl_dist, next_protocols_advertised,  		  client_preferred_next_protocols, log_alert, -		  server_name_indication, honor_cipher_order, padding_check], +		  server_name_indication, honor_cipher_order, padding_check, +		  fallback],      SockOpts = lists:foldl(fun(Key, PropList) ->  				   proplists:delete(Key, PropList) @@ -850,6 +852,8 @@ validate_option(honor_cipher_order, Value) when is_boolean(Value) ->      Value;  validate_option(padding_check, Value) when is_boolean(Value) ->      Value; +validate_option(fallback, Value) when is_boolean(Value) -> +    Value;  validate_option(Opt, Value) ->      throw({error, {options, {Opt, Value}}}). diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index 78dc98bc25..9e372f739a 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2007-2014. All Rights Reserved. +%% Copyright Ericsson AB 2007-2015. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -161,5 +161,7 @@ description_txt(?BAD_CERTIFICATE_HASH_VALUE) ->      "bad certificate hash value";  description_txt(?UNKNOWN_PSK_IDENTITY) ->      "unknown psk identity"; +description_txt(?INAPPROPRIATE_FALLBACK) -> +    "inappropriate fallback";  description_txt(Enum) ->      lists:flatten(io_lib:format("unsupported/unknown alert: ~p", [Enum])). diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl index f4f1d74264..a3619e4a35 100644 --- a/lib/ssl/src/ssl_alert.hrl +++ b/lib/ssl/src/ssl_alert.hrl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 2007-2014. All Rights Reserved. +%% Copyright Ericsson AB 2007-2015. All Rights Reserved.  %%   %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -58,6 +58,7 @@  %%       protocol_version(70),  %%       insufficient_security(71),  %%       internal_error(80), +%%       inappropriate_fallback(86),  %%       user_canceled(90),  %%       no_renegotiation(100),  %% RFC 4366 @@ -93,6 +94,7 @@  -define(PROTOCOL_VERSION, 70).  -define(INSUFFICIENT_SECURITY, 71).  -define(INTERNAL_ERROR, 80). +-define(INAPPROPRIATE_FALLBACK, 86).  -define(USER_CANCELED, 90).  -define(NO_RENEGOTIATION, 100).  -define(UNSUPPORTED_EXTENSION, 110). diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index ff9c618a35..bec0055353 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -36,7 +36,7 @@  	 decipher/6, cipher/5, suite/1, suites/1, all_suites/1,   	 ec_keyed_suites/0, anonymous_suites/0, psk_suites/1, srp_suites/0,  	 openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1, -	 hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2]). +	 hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1]).  -export_type([cipher_suite/0,  	      erl_cipher_suite/0, openssl_cipher_suite/0, @@ -1108,6 +1108,9 @@ is_acceptable_prf(default_prf, _) ->  is_acceptable_prf(Prf, Algos) ->      proplists:get_bool(Prf, Algos). +is_fallback(CipherSuites)-> +    lists:member(?TLS_FALLBACK_SCSV, CipherSuites). +  %%--------------------------------------------------------------------  %%% Internal functions  %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 3ce9c19aa9..3e50563f0a 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2007-2014. All Rights Reserved. +%% Copyright Ericsson AB 2007-2015. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -355,6 +355,10 @@  %% hello extension data as they should.  -define(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, <<?BYTE(16#00), ?BYTE(16#FF)>>). +%% TLS Fallback Signaling Cipher Suite Value (SCSV) for Preventing Protocol +%% Downgrade Attacks +-define(TLS_FALLBACK_SCSV, <<?BYTE(16#56), ?BYTE(16#00)>>). +  %%% PSK Cipher Suites RFC 4279  %%      TLS_PSK_WITH_RC4_128_SHA              = { 0x00, 0x8A }; diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index bb4e732517..88105cac5a 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -118,7 +118,8 @@  	  %% Should the server prefer its own cipher order over the one provided by  	  %% the client?  	  honor_cipher_order = false, -	  padding_check = true +	  padding_check = true, +	  fallback = false  	  }).  -record(socket_options, diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 183cabcfcd..b0b6d5a8e3 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2007-2014. All Rights Reserved. +%% Copyright Ericsson AB 2007-2015. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -28,6 +28,7 @@  -include("tls_record.hrl").  -include("ssl_alert.hrl").  -include("ssl_internal.hrl"). +-include("ssl_cipher.hrl").  -include_lib("public_key/include/public_key.hrl").  -export([client_hello/8, hello/4, @@ -47,22 +48,28 @@  %%--------------------------------------------------------------------  client_hello(Host, Port, ConnectionStates,  	     #ssl_options{versions = Versions, -			  ciphers = UserSuites +			  ciphers = UserSuites, +			  fallback = Fallback  			 } = SslOpts,  	     Cache, CacheCb, Renegotiation, OwnCert) ->      Version = tls_record:highest_protocol_version(Versions),      Pending = ssl_record:pending_connection_state(ConnectionStates, read),      SecParams = Pending#connection_state.security_parameters, -    CipherSuites = ssl_handshake:available_suites(UserSuites, Version), +    AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version),       Extensions = ssl_handshake:client_hello_extensions(Host, Version,  -						       CipherSuites, +						       AvailableCipherSuites,  						       SslOpts, ConnectionStates, Renegotiation), - -    Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), - +    CipherSuites =  +	case Fallback of +	    true -> +	        [?TLS_FALLBACK_SCSV | ssl_handshake:cipher_suites(AvailableCipherSuites, Renegotiation)]; +	    false -> +		ssl_handshake:cipher_suites(AvailableCipherSuites, Renegotiation) +	end, +    Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),          #client_hello{session_id = Id,  		  client_version = Version, -		  cipher_suites = ssl_handshake:cipher_suites(CipherSuites, Renegotiation), +		  cipher_suites = CipherSuites,  		  compression_methods = ssl_record:compressions(),  		  random = SecParams#security_parameters.client_random,  		  extensions = Extensions @@ -96,33 +103,22 @@ hello(#server_hello{server_version = Version, random = Random,      end;  hello(#client_hello{client_version = ClientVersion, -		    session_id = SugesstedId, -		    cipher_suites = CipherSuites, -		    compression_methods = Compressions, -		    random = Random, -		    extensions = #hello_extensions{elliptic_curves = Curves} = HelloExt}, +		    cipher_suites = CipherSuites} = Hello,        #ssl_options{versions = Versions} = SslOpts, -      {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> +      Info, Renegotiation) ->      Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions), -    case tls_record:is_acceptable_version(Version, Versions) of -	true -> -	    ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)), -	    {Type, #session{cipher_suite = CipherSuite} = Session1} -		= ssl_handshake:select_session(SugesstedId, CipherSuites, Compressions, -					       Port, Session0#session{ecc = ECCCurve}, Version, -					       SslOpts, Cache, CacheCb, Cert), -	    case CipherSuite of  -		no_suite -> -		    ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); -		_ -> -		    handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, -						   SslOpts, Session1, ConnectionStates0, -						   Renegotiation) +    case ssl_cipher:is_fallback(CipherSuites) of +	true ->  +	    Highest = tls_record:highest_protocol_version(Versions), +	    case tls_record:is_higher(Highest, Version) of +		true -> +		    ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); +		false ->				      +		    handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)  	    end;  	false -> -	    ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) +	    handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)      end. -  %%--------------------------------------------------------------------  -spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist().  %%      @@ -149,6 +145,32 @@ get_tls_handshake(Version, Data, Buffer) ->  %%--------------------------------------------------------------------  %%% Internal functions  %%-------------------------------------------------------------------- +handle_client_hello(Version, #client_hello{session_id = SugesstedId, +				       cipher_suites = CipherSuites, +				       compression_methods = Compressions, +				       random = Random, +				       extensions = #hello_extensions{elliptic_curves = Curves} = HelloExt}, +		#ssl_options{versions = Versions} = SslOpts, +	 {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> +    case tls_record:is_acceptable_version(Version, Versions) of +	true -> +	    ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)), +	    {Type, #session{cipher_suite = CipherSuite} = Session1} +		= ssl_handshake:select_session(SugesstedId, CipherSuites, Compressions, +					       Port, Session0#session{ecc = ECCCurve}, Version, +					       SslOpts, Cache, CacheCb, Cert), +	    case CipherSuite of  +		no_suite -> +		    ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); +		_ -> +		    handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, +						   SslOpts, Session1, ConnectionStates0, +						   Renegotiation) +	    end; +	false -> +	    ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) +    end. +  get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),  			Body:Length/binary,Rest/binary>>, Acc) ->      Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index ed61da2d62..168b2c8fd3 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -41,7 +41,7 @@  %% Protocol version handling  -export([protocol_version/1, lowest_protocol_version/2, -	 highest_protocol_version/1, supported_protocol_versions/0, +	 highest_protocol_version/1, is_higher/2, supported_protocol_versions/0,  	 is_acceptable_version/1, is_acceptable_version/2]).  -export_type([tls_version/0, tls_atom_version/0]). @@ -236,6 +236,13 @@ highest_protocol_version(Version = {M,_}, [{N,_} | Rest])  when M > N ->  highest_protocol_version(_, [Version | Rest]) ->      highest_protocol_version(Version, Rest). +is_higher({M, N}, {M, O}) when N > O -> +    true; +is_higher({M, _}, {N, _}) when M > N -> +    true;  +is_higher(_, _) -> +    false. +  %%--------------------------------------------------------------------  -spec supported_protocol_versions() -> [tls_version()].					   %% | 
