Skip to content

Commit

Permalink
ssh: Option minimal_remote_max_packet_size and test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
HansN authored and IngelaAndin committed Apr 7, 2015
1 parent 7345063 commit 3cf856f
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 43 deletions.
6 changes: 6 additions & 0 deletions lib/ssh/doc/src/ssh.xml
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@
</warning>
</item>

<tag><c><![CDATA[{minimal_remote_max_packet_size, non_negative_integer()}]]></c></tag>
<item>
<p>The least maximum packet size that the daemon will accept in channel open requests from the client. The default value is 0.
</p>
</item>

<tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
<item>
<p>Module implementing the behaviour <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
Expand Down
5 changes: 5 additions & 0 deletions lib/ssh/src/ssh.erl
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,14 @@ handle_option([{parallel_login, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
handle_option([{minimal_remote_max_packet_size, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).


handle_ssh_option({minimal_remote_max_packet_size, Value} = Opt) when is_integer(Value), Value >=0 ->
Opt;
handle_ssh_option({system_dir, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({user_dir, Value} = Opt) when is_list(Value) ->
Expand Down
114 changes: 71 additions & 43 deletions lib/ssh/src/ssh_connection.erl
Original file line number Diff line number Diff line change
Expand Up @@ -468,18 +468,31 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
sender_channel = RemoteId,
initial_window_size = WindowSz,
maximum_packet_size = PacketSz}, Connection0, server) ->

try setup_session(Connection0, RemoteId,
Type, WindowSz, PacketSz) of
Result ->
Result
catch _:_ ->
maximum_packet_size = PacketSz},
#connection{options = SSHopts} = Connection0,
server) ->
MinAcceptedPackSz = proplists:get_value(minimal_remote_max_packet_size, SSHopts, 0),

if
MinAcceptedPackSz =< PacketSz ->
try setup_session(Connection0, RemoteId,
Type, WindowSz, PacketSz) of
Result ->
Result
catch _:_ ->
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies, [{connection_reply, FailMsg}]},
Connection0}
end;

MinAcceptedPackSz > PacketSz ->
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies, [{connection_reply, FailMsg}]},
Connection0}
?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
lists:concat(["Maximum packet size below ",MinAcceptedPackSz,
" not supported"]), "en"),
{{replies, [{connection_reply, FailMsg}]}, Connection0}
end;

handle_msg(#ssh_msg_channel_open{channel_type = "session",
Expand All @@ -499,43 +512,57 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip" = Type,
initial_window_size = RWindowSz,
maximum_packet_size = RPacketSz,
data = Data},
#connection{channel_cache = Cache} = Connection0, server) ->
#connection{channel_cache = Cache,
options = SSHopts} = Connection0, server) ->
<<?UINT32(ALen), Address:ALen/binary, ?UINT32(Port),
?UINT32(OLen), Orig:OLen/binary, ?UINT32(OrigPort)>> = Data,

case bound_channel(Address, Port, Connection0) of
undefined ->
MinAcceptedPackSz = proplists:get_value(minimal_remote_max_packet_size, SSHopts, 0),

if
MinAcceptedPackSz =< RPacketSz ->
case bound_channel(Address, Port, Connection0) of
undefined ->
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies,
[{connection_reply, FailMsg}]}, Connection0};
ChannelPid ->
{ChannelId, Connection1} = new_channel_id(Connection0),
LWindowSz = ?DEFAULT_WINDOW_SIZE,
LPacketSz = ?DEFAULT_PACKET_SIZE,
Channel = #channel{type = Type,
sys = "none",
user = ChannelPid,
local_id = ChannelId,
recv_window_size = LWindowSz,
recv_packet_size = LPacketSz,
send_window_size = RWindowSz,
send_packet_size = RPacketSz,
send_buf = queue:new()
},
ssh_channel:cache_update(Cache, Channel),
OpenConfMsg = channel_open_confirmation_msg(RemoteId, ChannelId,
LWindowSz, LPacketSz),
{OpenMsg, Connection} =
reply_msg(Channel, Connection1,
{open, Channel, {forwarded_tcpip,
decode_ip(Address), Port,
decode_ip(Orig), OrigPort}}),
{{replies, [{connection_reply, OpenConfMsg},
OpenMsg]}, Connection}
end;

MinAcceptedPackSz > RPacketSz ->
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies,
[{connection_reply, FailMsg}]}, Connection0};
ChannelPid ->
{ChannelId, Connection1} = new_channel_id(Connection0),
LWindowSz = ?DEFAULT_WINDOW_SIZE,
LPacketSz = ?DEFAULT_PACKET_SIZE,
Channel = #channel{type = Type,
sys = "none",
user = ChannelPid,
local_id = ChannelId,
recv_window_size = LWindowSz,
recv_packet_size = LPacketSz,
send_window_size = RWindowSz,
send_packet_size = RPacketSz,
send_buf = queue:new()
},
ssh_channel:cache_update(Cache, Channel),
OpenConfMsg = channel_open_confirmation_msg(RemoteId, ChannelId,
LWindowSz, LPacketSz),
{OpenMsg, Connection} =
reply_msg(Channel, Connection1,
{open, Channel, {forwarded_tcpip,
decode_ip(Address), Port,
decode_ip(Orig), OrigPort}}),
{{replies, [{connection_reply, OpenConfMsg},
OpenMsg]}, Connection}
?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
lists:concat(["Maximum packet size below ",MinAcceptedPackSz,
" not supported"]), "en"),
{{replies, [{connection_reply, FailMsg}]}, Connection0}
end;


handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
sender_channel = RemoteId},
Connection, client) ->
Expand Down Expand Up @@ -917,7 +944,8 @@ start_channel(Cb, Id, Args, SubSysSup, Exec) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
setup_session(#connection{channel_cache = Cache} = Connection0,
setup_session(#connection{channel_cache = Cache
} = Connection0,
RemoteId,
Type, WindowSize, PacketSize) ->
{ChannelId, Connection} = new_channel_id(Connection0),
Expand Down
60 changes: 60 additions & 0 deletions lib/ssh/test/ssh_basic_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ all() ->
double_close,
ssh_connect_timeout,
ssh_connect_arg4_timeout,
packet_size_zero,
ssh_daemon_minimal_remote_max_packet_size_option,
{group, hardening_tests}
].

Expand Down Expand Up @@ -756,6 +758,64 @@ ms_passed(N1={_,_,M1}, N2={_,_,M2}) ->
calendar:now_to_local_time(N2)),
1000 * (Min*60 + Sec + (M2-M1)/1000000).

%%--------------------------------------------------------------------
packet_size_zero(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
file:make_dir(UserDir),

{Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
{user_passwords, [{"vego", "morot"}]}]),
Conn =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
{user_interaction, false},
{user, "vego"},
{password, "morot"}]),

{ok,Chan} = ssh_connection:session_channel(Conn, 1000, _MaxPacketSize=0, 60000),
ok = ssh_connection:shell(Conn, Chan),

ssh:close(Conn),
ssh:stop_daemon(Server),

receive
{ssh_cm,Conn,{data,Chan,_Type,_Msg1}} = M ->
ct:pal("Got ~p",[M]),
ct:fail(doesnt_obey_max_packet_size_0)
after 5000 ->
ok
end.

%%--------------------------------------------------------------------
ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
file:make_dir(UserDir),

{Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
{user_passwords, [{"vego", "morot"}]},
{failfun, fun ssh_test_lib:failfun/2},
{minimal_remote_max_packet_size, 14}]),
Conn =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
{user_interaction, false},
{user, "vego"},
{password, "morot"}]),

%% Try the limits of the minimal_remote_max_packet_size:
{ok, _ChannelId} = ssh_connection:session_channel(Conn, 100, 14, infinity),
{open_error,_,"Maximum packet size below 14 not supported",_} =
ssh_connection:session_channel(Conn, 100, 13, infinity),

ssh:close(Conn),
ssh:stop_daemon(Server).

%%--------------------------------------------------------------------
ssh_connect_negtimeout_parallel(Config) -> ssh_connect_negtimeout(Config,true).
ssh_connect_negtimeout_sequential(Config) -> ssh_connect_negtimeout(Config,false).
Expand Down

0 comments on commit 3cf856f

Please sign in to comment.