From 18b9fc4c61f487007c8bff3bbb52f9466f3454ce Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Mon, 26 Oct 2015 15:56:13 +0100 Subject: ssh: extend 'dh_gex_limits' to server side OTP-13066 --- lib/ssh/doc/src/ssh.xml | 11 +++++++++++ lib/ssh/src/ssh.erl | 5 +++++ lib/ssh/src/ssh_transport.erl | 29 +++++++++++++++++++++++++---- lib/ssh/test/ssh_protocol_SUITE.erl | 19 ++++++++++++++++--- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 1e4dd91eb6..7c1b9ea0dc 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -475,6 +475,17 @@ kex is implicit but public_key is set explicitly.

+ + +

Limits what a client can ask for in diffie-hellman-group-exchange. The effective value will be + MaxUsed = min(MaxClient,Max), MinUsed = max(MinClient,Min). +

+

If MaxUsed < MinUses in a key exchange, it will fail with a disconnect. +

+

See RFC 4419 for the function of the max an min values. +

+
+ boolean()}]]>

Provides a function for password validation. This function is called diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 9befceb51b..39cf441090 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -474,9 +474,14 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0, end; +handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0, + is_integer(Max), Max>=Min -> + %% Server + Opt; handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0, is_integer(I), I>=Min, is_integer(Max), Max>=I -> + %% Client Opt; handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity -> Opt; diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 35bfadb1df..d61fc76c0a 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -441,13 +441,14 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey, %%% %%% diffie-hellman-group-exchange-sha1 %%% -handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = Min, +handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = Min0, n = NBits, - max = Max}, - Ssh0=#ssh{opts=Opts}) when Min= + max = Max0}, + Ssh0=#ssh{opts=Opts}) when Min0= %% server + {Min, Max} = adjust_gex_min_max(Min0, Max0, Opts), case public_key:dh_gex_group(Min, NBits, Max, - proplists:get_value(dh_gex_groups,Opts)) of + proplists:get_value(dh_gex_groups,Opts)) of {ok, {_Sz, {G,P}}} -> {Public, Private} = generate_key(dh, [P,G]), {SshPacket, Ssh} = @@ -471,6 +472,26 @@ handle_kex_dh_gex_request(_, _) -> language = ""} }). + +adjust_gex_min_max(Min0, Max0, Opts) -> + case proplists:get_value(dh_gex_limits, Opts) of + undefined -> + {Min0, Max0}; + {Min1, Max1} -> + Min2 = max(Min0, Min1), + Max2 = min(Max0, Max1), + if + Min2 =< Max2 -> + {Min2, Max2}; + Max2 < Min2 -> + throw(#ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_PROTOCOL_ERROR, + description = "No possible diffie-hellman-group-exchange group possible", + language = ""}) + end + end. + + handle_kex_dh_gex_group(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0) -> %% client {Public, Private} = generate_key(dh, [P,G]), diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl index 0292c8d149..3a7f47c2dd 100644 --- a/lib/ssh/test/ssh_protocol_SUITE.erl +++ b/lib/ssh/test/ssh_protocol_SUITE.erl @@ -67,6 +67,7 @@ groups() -> {kex, [], [no_common_alg_server_disconnects, no_common_alg_client_disconnects, gex_client_init_option_groups, + gex_server_gex_limit, gex_client_init_option_groups_moduli_file, gex_client_init_option_groups_file ]}, @@ -92,7 +93,8 @@ init_per_testcase(no_common_alg_server_disconnects, Config) -> init_per_testcase(TC, Config) when TC == gex_client_init_option_groups ; TC == gex_client_init_option_groups_moduli_file ; - TC == gex_client_init_option_groups_file -> + TC == gex_client_init_option_groups_file ; + TC == gex_server_gex_limit -> Opts = case TC of gex_client_init_option_groups -> [{dh_gex_groups, [{2345, 3, 41}]}]; @@ -104,6 +106,12 @@ init_per_testcase(TC, Config) when TC == gex_client_init_option_groups ; DataDir = ?config(data_dir, Config), F = filename:join(DataDir, "dh_group_test.moduli"), [{dh_gex_groups, {ssh_moduli_file,F}}]; + gex_server_gex_limit -> + [{dh_gex_groups, [{ 500, 3, 18}, + {1000, 7, 91}, + {3000, 5, 61}]}, + {dh_gex_limits,{500,1500}} + ]; _ -> [] end, @@ -117,7 +125,8 @@ end_per_testcase(no_common_alg_server_disconnects, Config) -> stop_std_daemon(Config); end_per_testcase(TC, Config) when TC == gex_client_init_option_groups ; TC == gex_client_init_option_groups_moduli_file ; - TC == gex_client_init_option_groups_file -> + TC == gex_client_init_option_groups_file ; + TC == gex_server_gex_limit -> stop_std_daemon(Config); end_per_testcase(_TestCase, Config) -> check_std_daemon_works(Config, ?LINE). @@ -337,7 +346,6 @@ gex_client_init_option_groups(Config) -> do_gex_client_init(Config, {2000, 2048, 4000}, {3,41}). - gex_client_init_option_groups_file(Config) -> do_gex_client_init(Config, {2000, 2048, 4000}, {5,61}). @@ -346,6 +354,11 @@ gex_client_init_option_groups_moduli_file(Config) -> do_gex_client_init(Config, {2000, 2048, 4000}, {5,16#B7}). +gex_server_gex_limit(Config) -> + do_gex_client_init(Config, {1000, 3000, 4000}, + {7,91}). + + do_gex_client_init(Config, {Min,N,Max}, {G,P}) -> {ok,_} = ssh_trpt_test_lib:exec( -- cgit v1.2.3