aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorHans Nilsson <[email protected]>2015-10-26 15:56:13 +0100
committerHans Nilsson <[email protected]>2015-11-04 12:10:06 +0100
commit18b9fc4c61f487007c8bff3bbb52f9466f3454ce (patch)
treed42ff3d1ce25b36fa1ac1f4563205af786f2b5e1 /lib
parent961bb51d1b73c6ff2ad5f08e0a4c13fbfc2fbb98 (diff)
downloadotp-18b9fc4c61f487007c8bff3bbb52f9466f3454ce.tar.gz
otp-18b9fc4c61f487007c8bff3bbb52f9466f3454ce.tar.bz2
otp-18b9fc4c61f487007c8bff3bbb52f9466f3454ce.zip
ssh: extend 'dh_gex_limits' to server side
OTP-13066
Diffstat (limited to 'lib')
-rw-r--r--lib/ssh/doc/src/ssh.xml11
-rw-r--r--lib/ssh/src/ssh.erl5
-rw-r--r--lib/ssh/src/ssh_transport.erl29
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl19
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.</p>
</p>
</item>
+ <tag><c><![CDATA[{dh_gex_limits,{Min=integer(),Max=integer()}}]]></c></tag>
+ <item>
+ <p>Limits what a client can ask for in diffie-hellman-group-exchange. The effective value will be
+ <c>MaxUsed = min(MaxClient,Max), MinUsed = max(MinClient,Min)</c>.
+ </p>
+ <p>If <c>MaxUsed &lt; MinUses</c> in a key exchange, it will fail with a disconnect.
+ </p>
+ <p>See RFC 4419 for the function of the max an min values.
+ </p>
+ </item>
+
<tag><c><![CDATA[{pwdfun, fun(User::string(), password::string()) -> boolean()}]]></c></tag>
<item>
<p>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=<NBits, NBits=<Max ->
+ max = Max0},
+ Ssh0=#ssh{opts=Opts}) when Min0=<NBits, NBits=<Max0 ->
%% 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(