diff options
Diffstat (limited to 'lib/ssh/src')
-rw-r--r-- | lib/ssh/src/ssh.erl | 154 | ||||
-rw-r--r-- | lib/ssh/src/ssh.hrl | 2 | ||||
-rw-r--r-- | lib/ssh/src/ssh_acceptor.erl | 7 | ||||
-rw-r--r-- | lib/ssh/src/ssh_auth.erl | 60 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection.erl | 4 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection_handler.erl | 155 | ||||
-rw-r--r-- | lib/ssh/src/ssh_file.erl | 30 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 675 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.hrl | 35 |
9 files changed, 690 insertions, 432 deletions
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 049018b21c..bb50e436a3 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -33,7 +33,8 @@ default_algorithms/0, stop_listener/1, stop_listener/2, stop_listener/3, stop_daemon/1, stop_daemon/2, stop_daemon/3, - shell/1, shell/2, shell/3]). + shell/1, shell/2, shell/3 + ]). %%-------------------------------------------------------------------- -spec start() -> ok | {error, term()}. @@ -234,10 +235,27 @@ start_daemon(Host, Port, Options, Inet) -> {error, _Reason} = Error -> Error; {SocketOptions, SshOptions}-> - do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions]) + try + do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions]) + catch + throw:bad_fd -> {error,bad_fd}; + _C:_E -> {error,{cannot_start_daemon,_C,_E}} + end end. -do_start_daemon(Host, Port, Options, SocketOptions) -> +do_start_daemon(Host0, Port0, Options, SocketOptions) -> + {Host,Port} = try + case proplists:get_value(fd, SocketOptions) of + undefined -> + {Host0,Port0}; + Fd when Port0==0 -> + find_hostport(Fd); + _ -> + {Host0,Port0} + end + catch + _:_ -> throw(bad_fd) + end, Profile = proplists:get_value(profile, Options, ?DEFAULT_PROFILE), case ssh_system_sup:system_supervisor(Host, Port, Profile) of undefined -> @@ -271,6 +289,22 @@ do_start_daemon(Host, Port, Options, SocketOptions) -> end end. +find_hostport(Fd) -> + %% Using internal functions inet:open/8 and inet:close/0. + %% Don't try this at home unless you know what you are doing! + {ok,S} = inet:open(Fd, {0,0,0,0}, 0, [], tcp, inet, stream, inet_tcp), + {ok, HostPort} = inet:sockname(S), + ok = inet:close(S), + HostPort. + +%% find_port(Fd) -> +%% %% Hack.... +%% {ok,TmpSock} = gen_tcp:listen(0,[{fd,Fd}]), +%% {ok, {_,ThePort}} = inet:sockname(TmpSock), +%% gen_tcp:close(TmpSock), +%% ThePort. + + handle_options(Opts) -> try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of {Inet, Ssh} -> @@ -337,6 +371,8 @@ handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{keyboard_interact_fun, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); %%Backwards compatibility handle_option([{allow_user_interaction, Value} | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option({user_interaction, Value}) | SshOptions]); @@ -420,27 +456,67 @@ handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) -> Opt; handle_ssh_option({preferred_algorithms,[_|_]} = Opt) -> handle_pref_algs(Opt); -handle_ssh_option({dh_gex_groups,L=[{I1,I2,I3}|_]}) when is_integer(I1), I1>0, - is_integer(I2), I2>0, - is_integer(I3), I3>0 -> - {dh_gex_groups, lists:map(fun({N,G,P}) -> {N,{G,P}} end, L)}; -handle_ssh_option({dh_gex_groups,{file,File=[C|_]}}=Opt) when is_integer(C), C>0 -> - %% A string, (file name) - case file:consult(File) of - {ok, List} -> - try handle_ssh_option({dh_gex_groups,List}) of - {dh_gex_groups,_} = NewOpt -> - NewOpt - catch - _:_ -> - throw({error, {{eoptions, Opt}, "Bad format in file"}}) - end; - Error -> - throw({error, {{eoptions, Opt},{"Error reading file",Error}}}) - end; + +handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) -> + {dh_gex_groups, + collect_per_size( + lists:foldl( + fun({N,G,P}, Acc) when is_integer(N),N>0, + is_integer(G),G>0, + is_integer(P),P>0 -> + [{N,{G,P}} | Acc]; + ({N,{G,P}}, Acc) when is_integer(N),N>0, + is_integer(G),G>0, + is_integer(P),P>0 -> + [{N,{G,P}} | Acc]; + ({N,GPs}, Acc) when is_list(GPs) -> + lists:foldr(fun({Gi,Pi}, Acci) when is_integer(Gi),Gi>0, + is_integer(Pi),Pi>0 -> + [{N,{Gi,Pi}} | Acci] + end, Acc, GPs) + end, [], L0))}; + +handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0, + Tag == file ; + Tag == ssh_moduli_file -> + {ok,GroupDefs} = + case Tag of + file -> + file:consult(File); + ssh_moduli_file -> + case file:open(File,[read]) of + {ok,D} -> + try + {ok,Moduli} = read_moduli_file(D, 1, []), + file:close(D), + {ok, Moduli} + catch + _:_ -> + throw({error, {{eoptions, Opt}, "Bad format in file "++File}}) + end; + {error,enoent} -> + throw({error, {{eoptions, Opt}, "File not found:"++File}}); + {error,Error} -> + throw({error, {{eoptions, Opt}, io_lib:format("Error reading file ~s: ~p",[File,Error])}}) + end + end, + + try + handle_ssh_option({dh_gex_groups,GroupDefs}) + catch + _:_ -> + throw({error, {{eoptions, Opt}, "Bad format in file: "++File}}) + 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; @@ -462,10 +538,14 @@ handle_ssh_option({password, Value} = Opt) when is_list(Value) -> Opt; handle_ssh_option({user_passwords, Value} = Opt) when is_list(Value)-> Opt; -handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value) -> +handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value,2) -> + Opt; +handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value,4) -> Opt; handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) -> Opt; +handle_ssh_option({keyboard_interact_fun, Value} = Opt) when is_function(Value,3) -> + Opt; handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> Opt; handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module), @@ -660,3 +740,33 @@ directory_exist_readable(Dir) -> +collect_per_size(L) -> + lists:foldr( + fun({Sz,GP}, [{Sz,GPs}|Acc]) -> [{Sz,[GP|GPs]}|Acc]; + ({Sz,GP}, Acc) -> [{Sz,[GP]}|Acc] + end, [], lists:sort(L)). + +read_moduli_file(D, I, Acc) -> + case io:get_line(D,"") of + {error,Error} -> + {error,Error}; + eof -> + {ok, Acc}; + "#" ++ _ -> read_moduli_file(D, I+1, Acc); + <<"#",_/binary>> -> read_moduli_file(D, I+1, Acc); + Data -> + Line = if is_binary(Data) -> binary_to_list(Data); + is_list(Data) -> Data + end, + try + [_Time,_Type,_Tests,_Tries,Size,G,P] = string:tokens(Line," \r\n"), + M = {list_to_integer(Size), + {list_to_integer(G), list_to_integer(P,16)} + }, + read_moduli_file(D, I+1, [M|Acc]) + catch + _:_ -> + read_moduli_file(D, I+1, Acc) + end + end. + diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index fc9d60c500..8efc743b67 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -29,7 +29,6 @@ -define(SSH_DEFAULT_PORT, 22). -define(SSH_MAX_PACKET_SIZE, (256*1024)). --define(SSH_LENGHT_INDICATOR_SIZE, 4). -define(REKEY_TIMOUT, 3600000). -define(REKEY_DATA_TIMOUT, 60000). -define(DEFAULT_PROFILE, default). @@ -138,6 +137,7 @@ kb_tries_left = 0, % integer(), num tries left for "keyboard-interactive" userauth_preference, available_host_keys, + pwdfun_user_state, authenticated = false }). diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl index c5ad1d7b6c..d94dedf1bf 100644 --- a/lib/ssh/src/ssh_acceptor.erl +++ b/lib/ssh/src/ssh_acceptor.erl @@ -56,7 +56,12 @@ acceptor_init(Parent, Port, Address, SockOpts, Opts, AcceptTimeout) -> error end. -do_socket_listen(Callback, Port, Opts) -> +do_socket_listen(Callback, Port0, Opts) -> + Port = + case proplists:get_value(fd, Opts) of + undefined -> Port0; + _ -> 0 + end, case Callback:listen(Port, Opts) of {error, nxdomain} -> Callback:listen(Port, lists:delete(inet6, Opts)); diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 04749fcf8e..4967a2e4cd 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -174,15 +174,15 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, #ssh{opts = Opts, userauth_supported_methods = Methods} = Ssh) -> Password = unicode:characters_to_list(BinPwd), - case check_password(User, Password, Opts) of - true -> + case check_password(User, Password, Opts, Ssh) of + {true,Ssh1} -> {authorized, User, - ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)}; - false -> + ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh1)}; + {false,Ssh1} -> {not_authorized, {User, {error,"Bad user or password"}}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ authentications = Methods, - partial_success = false}, Ssh)} + partial_success = false}, Ssh1)} end; handle_userauth_request(#ssh_msg_userauth_request{user = User, @@ -335,16 +335,16 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, kb_tries_left = KbTriesLeft, user = User, userauth_supported_methods = Methods} = Ssh) -> - case check_password(User, unicode:characters_to_list(Password), Opts) of - true -> + case check_password(User, unicode:characters_to_list(Password), Opts, Ssh) of + {true,Ssh1} -> {authorized, User, - ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)}; - false -> + ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh1)}; + {false,Ssh1} -> {not_authorized, {User, {error,"Bad user or password"}}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ authentications = Methods, partial_success = false}, - Ssh#ssh{kb_tries_left = max(KbTriesLeft-1, 0)} + Ssh1#ssh{kb_tries_left = max(KbTriesLeft-1, 0)} )} end; @@ -364,6 +364,11 @@ method_preference(Algs) -> [{"publickey", ?MODULE, publickey_msg, [A]} | Acc] end, [{"password", ?MODULE, password_msg, []}, + {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, + {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, + {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, + {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, + {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []} ], Algs). @@ -387,13 +392,34 @@ user_name(Opts) -> {ok, User} end. -check_password(User, Password, Opts) -> +check_password(User, Password, Opts, Ssh) -> case proplists:get_value(pwdfun, Opts) of undefined -> Static = get_password_option(Opts, User), - Password == Static; - Cheker -> - Cheker(User, Password) + {Password == Static, Ssh}; + + Checker when is_function(Checker,2) -> + {Checker(User, Password), Ssh}; + + Checker when is_function(Checker,4) -> + #ssh{pwdfun_user_state = PrivateState, + peer = {_,PeerAddr={_,_}} + } = Ssh, + case Checker(User, Password, PeerAddr, PrivateState) of + true -> + {true,Ssh}; + false -> + {false,Ssh}; + {true,NewState} -> + {true, Ssh#ssh{pwdfun_user_state=NewState}}; + {false,NewState} -> + {false, Ssh#ssh{pwdfun_user_state=NewState}}; + disconnect -> + throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, + description = + "Unable to connect using the available authentication methods", + language = ""}) + end end. get_password_option(Opts, User) -> @@ -451,14 +477,14 @@ keyboard_interact_get_responses(false, undefined, undefined, _, _, _, [Prompt|_] ssh_no_io:read_line(Prompt, Opts); %% Throws error as keyboard interaction is not allowed keyboard_interact_get_responses(true, undefined, _,IoCb, Name, Instr, PromptInfos, Opts, _) -> keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts); -keyboard_interact_get_responses(true, Fun, _, Name, Instr, PromptInfos, _, _, NumPrompts) -> +keyboard_interact_get_responses(true, Fun, _Pwd, _IoCb, Name, Instr, PromptInfos, _Opts, NumPrompts) -> keyboard_interact_fun(Fun, Name, Instr, PromptInfos, NumPrompts). keyboard_interact(IoCb, Name, Instr, Prompts, Opts) -> - if Name /= "" -> IoCb:format("~s", [Name]); + if Name /= "" -> IoCb:format("~s~n", [Name]); true -> ok end, - if Instr /= "" -> IoCb:format("~s", [Instr]); + if Instr /= "" -> IoCb:format("~s~n", [Instr]); true -> ok end, lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt, Opts); diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index 266c64fd4f..a34478732c 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -662,7 +662,7 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, ReplyMsg = {subsystem, ChannelId, WantReply, binary_to_list(SsName)}, try - {ok, Pid} = start_subsytem(SsName, Connection, Channel0, ReplyMsg), + {ok, Pid} = start_subsystem(SsName, Connection, Channel0, ReplyMsg), erlang:monitor(process, Pid), Channel = Channel0#channel{user = Pid}, ssh_channel:cache_update(Cache, Channel), @@ -1017,7 +1017,7 @@ start_cli(#connection{options = Options, sub_system_supervisor = SubSysSup}, ChannelId) -> start_channel(CbModule, ChannelId, Args, SubSysSup, Exec, Options). -start_subsytem(BinName, #connection{options = Options, +start_subsystem(BinName, #connection{options = Options, sub_system_supervisor = SubSysSup}, #channel{local_id = ChannelId}, _ReplyMsg) -> Name = binary_to_list(BinName), diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 7fb86c1108..505c6eb181 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -627,14 +627,24 @@ userauth_keyboard_interactive(#ssh_msg_userauth_info_response{} = Msg, retry_fun(User, Address, Reason, Opts), send_msg(Reply, State), {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} - end. - + end; +userauth_keyboard_interactive(Msg = #ssh_msg_userauth_failure{}, + #state{ssh_params = Ssh0 = + #ssh{role = client, + userauth_preference = Prefs0}} + = State) -> + Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Prefs0, + Method =/= "keyboard-interactive"], + userauth(Msg, State#state{ssh_params = Ssh0#ssh{userauth_preference=Prefs}}). + -userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_failure{}, State) -> +userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_failure{}, + #state{ssh_params = #ssh{role = client}} = State) -> userauth(Msg, State); -userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_success{}, State) -> +userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_success{}, + #state{ssh_params = #ssh{role = client}} = State) -> userauth(Msg, State). %%-------------------------------------------------------------------- @@ -960,57 +970,39 @@ handle_info({Protocol, Socket, Info}, hello, transport_protocol = Protocol} = State) -> event({info_line, Info}, hello, State); -handle_info({Protocol, Socket, Data}, Statename, +handle_info({Protocol, Socket, Data}, StateName, #state{socket = Socket, transport_protocol = Protocol, - ssh_params = #ssh{decrypt_block_size = BlockSize, - recv_mac_size = MacSize} = Ssh0, - decoded_data_buffer = <<>>, - encoded_data_buffer = EncData0} = State0) -> - - %% Implementations SHOULD decrypt the length after receiving the - %% first 8 (or cipher block size, whichever is larger) bytes of a - %% packet. (RFC 4253: Section 6 - Binary Packet Protocol) - case size(EncData0) + size(Data) >= erlang:max(8, BlockSize) of - true -> - {Ssh, SshPacketLen, DecData, EncData} = - - ssh_transport:decrypt_first_block(<<EncData0/binary, - Data/binary>>, Ssh0), - case SshPacketLen > ?SSH_MAX_PACKET_SIZE of - true -> - DisconnectMsg = - #ssh_msg_disconnect{code = - ?SSH_DISCONNECT_PROTOCOL_ERROR, - description = "Bad packet length " - ++ integer_to_list(SshPacketLen), - language = "en"}, - handle_disconnect(DisconnectMsg, State0); - false -> - RemainingSshPacketLen = - (SshPacketLen + ?SSH_LENGHT_INDICATOR_SIZE) - - BlockSize + MacSize, - State = State0#state{ssh_params = Ssh}, - handle_ssh_packet_data(RemainingSshPacketLen, - DecData, EncData, Statename, - State) - end; - false -> - {next_state, Statename, - next_packet(State0#state{encoded_data_buffer = - <<EncData0/binary, Data/binary>>})} + ssh_params = Ssh0, + decoded_data_buffer = DecData0, + encoded_data_buffer = EncData0, + undecoded_packet_length = RemainingSshPacketLen0} = State0) -> + Encoded = <<EncData0/binary, Data/binary>>, + case ssh_transport:handle_packet_part(DecData0, Encoded, RemainingSshPacketLen0, Ssh0) of + {get_more, DecBytes, EncDataRest, RemainingSshPacketLen, Ssh1} -> + {next_state, StateName, + next_packet(State0#state{encoded_data_buffer = EncDataRest, + decoded_data_buffer = DecBytes, + undecoded_packet_length = RemainingSshPacketLen, + ssh_params = Ssh1})}; + {decoded, MsgBytes, EncDataRest, Ssh1} -> + generate_event(MsgBytes, StateName, + State0#state{ssh_params = Ssh1, + %% Important to be set for + %% next_packet +%%% FIXME: the following three seem to always be set in generate_event! + decoded_data_buffer = <<>>, + undecoded_packet_length = undefined, + encoded_data_buffer = EncDataRest}, + EncDataRest); + {bad_mac, Ssh1} -> + DisconnectMsg = + #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR, + description = "Bad mac", + language = ""}, + handle_disconnect(DisconnectMsg, State0#state{ssh_params=Ssh1}) end; - -handle_info({Protocol, Socket, Data}, Statename, - #state{socket = Socket, - transport_protocol = Protocol, - decoded_data_buffer = DecData, - encoded_data_buffer = EncData, - undecoded_packet_length = Len} = - State) when is_integer(Len) -> - handle_ssh_packet_data(Len, DecData, <<EncData/binary, Data/binary>>, - Statename, State); - + handle_info({CloseTag, _Socket}, _StateName, #state{transport_close_tag = CloseTag, ssh_params = #ssh{role = _Role, opts = _Opts}} = State) -> @@ -1088,7 +1080,7 @@ handle_info(UnexpectedMessage, StateName, #state{opts = Opts, terminate(normal, _, #state{transport_cb = Transport, connection_state = Connection, socket = Socket}) -> - terminate_subsytem(Connection), + terminate_subsystem(Connection), (catch Transport:close(Socket)), ok; @@ -1117,7 +1109,7 @@ terminate({shutdown, _}, StateName, State) -> terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid, connection_state = Connection} = State) -> - terminate_subsytem(Connection), + terminate_subsystem(Connection), log_error(Reason), DisconnectMsg = #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, @@ -1128,10 +1120,10 @@ terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid, terminate(normal, StateName, State#state{ssh_params = Ssh}). -terminate_subsytem(#connection{system_supervisor = SysSup, +terminate_subsystem(#connection{system_supervisor = SysSup, sub_system_supervisor = SubSysSup}) when is_pid(SubSysSup) -> ssh_system_sup:stop_subsystem(SysSup, SubSysSup); -terminate_subsytem(_) -> +terminate_subsystem(_) -> ok. format_status(normal, [_, State]) -> @@ -1621,57 +1613,6 @@ after_new_keys_events({connection_reply, _Data} = Reply, {StateName, State}) -> NewState = send_replies([Reply], State), {next_state, StateName, NewState}. -handle_ssh_packet_data(RemainingSshPacketLen, DecData, EncData, StateName, - State) -> - EncSize = size(EncData), - case RemainingSshPacketLen > EncSize of - true -> - {next_state, StateName, - next_packet(State#state{decoded_data_buffer = DecData, - encoded_data_buffer = EncData, - undecoded_packet_length = - RemainingSshPacketLen})}; - false -> - handle_ssh_packet(RemainingSshPacketLen, StateName, - State#state{decoded_data_buffer = DecData, - encoded_data_buffer = EncData}) - - end. - -handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0, - encoded_data_buffer = EncData0, - ssh_params = Ssh0, - transport_protocol = _Protocol, - socket = _Socket} = State0) -> - try - {Ssh1, DecData, EncData, Mac} = - ssh_transport:unpack(EncData0, Length, Ssh0), - SshPacket = <<DecData0/binary, DecData/binary>>, - case ssh_transport:is_valid_mac(Mac, SshPacket, Ssh1) of - true -> - PacketData = ssh_transport:msg_data(SshPacket), - {Ssh1, Msg} = ssh_transport:decompress(Ssh1, PacketData), - generate_event(Msg, StateName, - State0#state{ssh_params = Ssh1, - %% Important to be set for - %% next_packet - decoded_data_buffer = <<>>}, - EncData); - false -> - DisconnectMsg = - #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR, - description = "Bad mac", - language = "en"}, - handle_disconnect(DisconnectMsg, State0) - end - catch _:_ -> - Disconnect = - #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR, - description = "Bad input", - language = "en"}, - handle_disconnect(Disconnect, State0) - end. - handle_disconnect(DisconnectMsg, State) -> handle_disconnect(own, DisconnectMsg, State). diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index c087ce14d7..3e066c453d 100644 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -221,9 +221,11 @@ do_lookup_host_key(KeyToMatch, Host, Alg, Opts) -> {ok, Fd} -> Res = lookup_host_key_fd(Fd, KeyToMatch, Host, Alg), file:close(Fd), - {ok, Res}; - {error, enoent} -> {error, not_found}; - Error -> Error + Res; + {error, enoent} -> + {error, not_found}; + Error -> + Error end. identity_key_filename('ssh-dss' ) -> "id_dsa"; @@ -242,6 +244,9 @@ lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType) -> case io:get_line(Fd, '') of eof -> {error, not_found}; + {error,Error} -> + %% Rare... For example NFS errors + {error,Error}; Line -> case ssh_decode_line(Line, known_hosts) of [{Key, Attributes}] -> @@ -262,7 +267,7 @@ handle_host(Fd, KeyToMatch, Host, HostList, Key, KeyType) -> Host1 = host_name(Host), case lists:member(Host1, HostList) andalso key_match(Key, KeyType) of true when KeyToMatch == Key -> - Key; + {ok,Key}; _ -> lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType) end. @@ -309,6 +314,9 @@ lookup_user_key_fd(Fd, Key) -> case io:get_line(Fd, '') of eof -> {error, not_found}; + {error,Error} -> + %% Rare... For example NFS errors + {error,Error}; Line -> case ssh_decode_line(Line, auth_keys) of [{AuthKey, _}] -> @@ -328,8 +336,18 @@ is_auth_key(Key, Key) -> is_auth_key(_,_) -> false. -default_user_dir()-> - {ok,[[Home|_]]} = init:get_argument(home), + +default_user_dir() -> + try + default_user_dir(os:getenv("HOME")) + catch + _:_ -> + default_user_dir(init:get_argument(home)) + end. + +default_user_dir({ok,[[Home|_]]}) -> + default_user_dir(Home); +default_user_dir(Home) when is_list(Home) -> UserDir = filename:join(Home, ".ssh"), ok = filelib:ensure_dir(filename:join(UserDir, "dummy")), {ok,Info} = file:read_file_info(UserDir), diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 8b65806dc6..041e86bae1 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -31,10 +31,10 @@ -include("ssh.hrl"). -export([versions/2, hello_version_msg/1]). --export([next_seqnum/1, decrypt_first_block/2, decrypt_blocks/3, +-export([next_seqnum/1, supported_algorithms/0, supported_algorithms/1, default_algorithms/0, default_algorithms/1, - is_valid_mac/3, + handle_packet_part/4, handle_hello_version/1, key_exchange_init_msg/1, key_init/3, new_keys_message/1, @@ -45,9 +45,13 @@ handle_kex_ecdh_init/2, handle_kex_ecdh_reply/2, extract_public_key/1, - unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1, + ssh_packet/2, pack/2, sign/3, verify/4]). +%%% For test suites +-export([pack/3]). +-export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove + %%%---------------------------------------------------------------------------- %%% %%% There is a difference between supported and default algorithms. The @@ -66,10 +70,15 @@ default_algorithms() -> [{K,default_algorithms(K)} || K <- algo_classes()]. algo_classes() -> [kex, public_key, cipher, mac, compression]. -%% default_algorithms(kex) -> % Example of how to disable an algorithm -%% supported_algorithms(kex, ['ecdh-sha2-nistp521']); + +default_algorithms(cipher) -> + supported_algorithms(cipher, same(['AEAD_AES_128_GCM', + 'AEAD_AES_256_GCM'])); +default_algorithms(mac) -> + supported_algorithms(mac, same(['AEAD_AES_128_GCM', + 'AEAD_AES_256_GCM'])); default_algorithms(Alg) -> - supported_algorithms(Alg). + supported_algorithms(Alg, []). supported_algorithms() -> [{K,supported_algorithms(K)} || K <- algo_classes()]. @@ -97,19 +106,25 @@ supported_algorithms(public_key) -> supported_algorithms(cipher) -> same( select_crypto_supported( - [{'aes256-ctr', [{ciphers,{aes_ctr,256}}]}, - {'aes192-ctr', [{ciphers,{aes_ctr,192}}]}, - {'aes128-ctr', [{ciphers,{aes_ctr,128}}]}, - {'aes128-cbc', [{ciphers,aes_cbc128}]}, - {'3des-cbc', [{ciphers,des3_cbc}]} + [{'aes256-ctr', [{ciphers,{aes_ctr,256}}]}, + {'aes192-ctr', [{ciphers,{aes_ctr,192}}]}, + {'aes128-ctr', [{ciphers,{aes_ctr,128}}]}, + {'aes128-cbc', [{ciphers,aes_cbc128}]}, + {'[email protected]', [{ciphers,{aes_gcm,128}}]}, + {'[email protected]', [{ciphers,{aes_gcm,256}}]}, + {'AEAD_AES_128_GCM', [{ciphers,{aes_gcm,128}}]}, + {'AEAD_AES_256_GCM', [{ciphers,{aes_gcm,256}}]}, + {'3des-cbc', [{ciphers,des3_cbc}]} ] )); supported_algorithms(mac) -> same( select_crypto_supported( - [{'hmac-sha2-256', [{hashs,sha256}]}, - {'hmac-sha2-512', [{hashs,sha512}]}, - {'hmac-sha1', [{hashs,sha}]} + [{'hmac-sha2-256', [{hashs,sha256}]}, + {'hmac-sha2-512', [{hashs,sha512}]}, + {'hmac-sha1', [{hashs,sha}]}, + {'AEAD_AES_128_GCM', [{ciphers,{aes_gcm,128}}]}, + {'AEAD_AES_256_GCM', [{ciphers,{aes_gcm,256}}]} ] )); supported_algorithms(compression) -> @@ -118,46 +133,6 @@ supported_algorithms(compression) -> 'zlib' ]). -%% Dialyzer complains when not called...supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) -> -%% Dialyzer complains when not called... [{client2server,As1},{server2client,As2}] = supported_algorithms(Key), -%% Dialyzer complains when not called... [{client2server,As1--BL1},{server2client,As2--BL2}]; -%% Dialyzer complains when not called...supported_algorithms(Key, BlackList) -> -%% Dialyzer complains when not called... supported_algorithms(Key) -- BlackList. - -select_crypto_supported(L) -> - Sup = [{ec_curve,crypto_supported_curves()} | crypto:supports()], - [Name || {Name,CryptoRequires} <- L, - crypto_supported(CryptoRequires, Sup)]. - -crypto_supported_curves() -> - try crypto:ec_curves() - catch _:_ -> [] - end. - -crypto_supported(Conditions, Supported) -> - lists:all( fun({Tag,CryptoName}) when is_atom(CryptoName) -> - crypto_name_supported(Tag,CryptoName,Supported); - ({Tag,{Name=aes_ctr,Len}}) when is_integer(Len) -> - crypto_name_supported(Tag,Name,Supported) andalso - ctr_len_supported(Name,Len) - end, Conditions). - -crypto_name_supported(Tag, CryptoName, Supported) -> - lists:member(CryptoName, proplists:get_value(Tag,Supported,[])). - -ctr_len_supported(Name, Len) -> - try - crypto:stream_encrypt(crypto:stream_init(Name, <<0:Len>>, <<0:128>>), <<"">>) - of - {_,X} -> is_binary(X) - catch - _:_ -> false - end. - - -same(Algs) -> [{client2server,Algs}, {server2client,Algs}]. - - %%%---------------------------------------------------------------------------- versions(client, Options)-> Vsn = proplists:get_value(vsn, Options, ?DEFAULT_CLIENT_VERSION), @@ -196,12 +171,6 @@ hello_version_msg(Data) -> next_seqnum(SeqNum) -> (SeqNum + 1) band 16#ffffffff. -decrypt_first_block(Bin, #ssh{decrypt_block_size = BlockSize} = Ssh0) -> - <<EncBlock:BlockSize/binary, EncData/binary>> = Bin, - {Ssh, <<?UINT32(PacketLen), _/binary>> = DecData} = - decrypt(Ssh0, EncBlock), - {Ssh, PacketLen, DecData, EncData}. - decrypt_blocks(Bin, Length, Ssh0) -> <<EncBlocks:Length/binary, EncData/binary>> = Bin, {Ssh, DecData} = decrypt(Ssh0, EncBlocks), @@ -441,19 +410,29 @@ 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 - {G, P} = dh_gex_group(Min, NBits, Max, proplists:get_value(dh_gex_groups,Opts)), - {Public, Private} = generate_key(dh, [P,G]), - {SshPacket, Ssh} = - ssh_packet(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0), - {ok, SshPacket, - Ssh#ssh{keyex_key = {{Private, Public}, {G, P}}, - keyex_info = {Min, Max, NBits} - }}; + {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 + {ok, {_Sz, {G,P}}} -> + {Public, Private} = generate_key(dh, [P,G]), + {SshPacket, Ssh} = + ssh_packet(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0), + {ok, SshPacket, + Ssh#ssh{keyex_key = {{Private, Public}, {G, P}}, + keyex_info = {Min, Max, NBits} + }}; + {error,_} -> + throw(#ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_PROTOCOL_ERROR, + description = "No possible diffie-hellman-group-exchange group found", + language = ""}) + end; + handle_kex_dh_gex_request(_, _) -> throw({{error,bad_ssh_msg_kex_dh_gex_request}, #ssh_msg_disconnect{ @@ -462,6 +441,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]), @@ -563,10 +562,11 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic}, Ssh0 = #ssh{algorithms = #alg{kex=Kex}}) -> %% at server Curve = ecdh_curve(Kex), - case ecdh_validate_public_key(PeerPublic, Curve) of - true -> - {MyPublic, MyPrivate} = generate_key(ecdh, Curve), - K = compute_key(ecdh, PeerPublic, MyPrivate, Curve), + {MyPublic, MyPrivate} = generate_key(ecdh, Curve), + try + compute_key(ecdh, PeerPublic, MyPrivate, Curve) + of + K -> MyPrivHostKey = get_host_key(Ssh0), MyPubHostKey = extract_public_key(MyPrivHostKey), H = kex_h(Ssh0, Curve, MyPubHostKey, PeerPublic, MyPublic, K), @@ -579,9 +579,9 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic}, {ok, SshPacket, Ssh1#ssh{keyex_key = {{MyPublic,MyPrivate},Curve}, shared_secret = K, exchanged_hash = H, - session_id = sid(Ssh1, H)}}; - - false -> + session_id = sid(Ssh1, H)}} + catch + _:_ -> throw({{error,invalid_peer_public_key}, #ssh_msg_disconnect{ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, @@ -596,9 +596,10 @@ handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey, #ssh{keyex_key = {{MyPublic,MyPrivate}, Curve}} = Ssh0 ) -> %% at client - case ecdh_validate_public_key(PeerPublic, Curve) of - true -> - K = compute_key(ecdh, PeerPublic, MyPrivate, Curve), + try + compute_key(ecdh, PeerPublic, MyPrivate, Curve) + of + K -> H = kex_h(Ssh0, Curve, PeerPubHostKey, MyPublic, PeerPublic, K), case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of ok -> @@ -613,9 +614,9 @@ handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey, description = "Key exchange failed", language = ""} }) - end; - - false -> + end + catch + _:_ -> throw({{error,invalid_peer_public_key}, #ssh_msg_disconnect{ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, @@ -626,62 +627,6 @@ handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey, %%%---------------------------------------------------------------- -%%% -%%% Standards for Efficient Cryptography Group, "Elliptic Curve Cryptography", SEC 1 -%%% Section 3.2.2.1 -%%% - -ecdh_validate_public_key(Key, Curve) -> - case key_size(Curve) of - undefined -> - false; - - Sz -> - case dec_key(Key, Sz) of - {ok,Q} -> - case crypto:ec_curve(Curve) of - {{prime_field,P}, {A, B, _Seed}, - _P0Bin, _OrderBin, _CoFactorBin} -> - on_curve(Q, bin2int(A), bin2int(B), bin2int(P)) - end; - - {error,compressed_not_implemented} -> % Be a bit generous... - true; - - _Error -> - false - end - end. - - -on_curve({X,Y}, A, B, P) when 0 =< X,X =< (P-1), - 0 =< Y,Y =< (P-1) -> - %% Section 3.2.2.1, point 2 - (Y*Y) rem P == (X*X*X + A*X + B) rem P; -on_curve(_, _, _, _) -> - false. - - -bin2int(B) -> - Sz = erlang:bit_size(B), - <<I:Sz/big-unsigned-integer>> = B, - I. - -key_size(secp256r1) -> 256; -key_size(secp384r1) -> 384; -key_size(secp521r1) -> 528; % Round 521 up to closest 8-bits. -key_size(_) -> undefined. - - -dec_key(Key, NBits) -> - Size = 8 + 2*NBits, - case <<Key:Size>> of - <<4:8, X:NBits, Y:NBits>> -> {ok,{X,Y}}; - <<4:8, _/binary>> -> {error,bad_format}; - _ -> {error,compressed_not_implemented} - end. - -%%%---------------------------------------------------------------- handle_new_keys(#ssh_msg_newkeys{}, Ssh0) -> try install_alg(Ssh0) of #ssh{} = Ssh -> @@ -781,8 +726,12 @@ known_host_key(#ssh{opts = Opts, key_cb = Mod, peer = Peer} = Ssh, %% The first algorithm in each list MUST be the preferred (guessed) %% algorithm. Each string MUST contain at least one algorithm name. select_algorithm(Role, Client, Server) -> - {Encrypt, Decrypt} = select_encrypt_decrypt(Role, Client, Server), - {SendMac, RecvMac} = select_send_recv_mac(Role, Client, Server), + {Encrypt0, Decrypt0} = select_encrypt_decrypt(Role, Client, Server), + {SendMac0, RecvMac0} = select_send_recv_mac(Role, Client, Server), + + {Encrypt, SendMac} = aead_gcm_simultan(Encrypt0, SendMac0), + {Decrypt, RecvMac} = aead_gcm_simultan(Decrypt0, RecvMac0), + {Compression, Decompression} = select_compression_decompression(Role, Client, Server), @@ -813,6 +762,38 @@ select_algorithm(Role, Client, Server) -> s_lng = S_Lng}, {ok, Alg}. + +%%% It is an agreed problem with RFC 5674 that if the selection is +%%% Cipher = AEAD_AES_x_GCM and +%%% Mac = AEAD_AES_y_GCM (where x =/= y) +%%% then it is undefined what length should be selected. +%%% +%%% If only one of the two lengths (128,256) is available, I claim that +%%% there is no such ambiguity. + +%%% From https://anongit.mindrot.org/openssh.git/plain/PROTOCOL +%%% (read Nov 20, 2015) +%%% 1.6 transport: AES-GCM +%%% +%%% OpenSSH supports the AES-GCM algorithm as specified in RFC 5647. +%%% Because of problems with the specification of the key exchange +%%% the behaviour of OpenSSH differs from the RFC as follows: +%%% +%%% AES-GCM is only negotiated as the cipher algorithms +%%% "[email protected]" or "[email protected]" and never as +%%% an MAC algorithm. Additionally, if AES-GCM is selected as the cipher +%%% the exchanged MAC algorithms are ignored and there doesn't have to be +%%% a matching MAC. + +aead_gcm_simultan('[email protected]', _) -> {'AEAD_AES_128_GCM', 'AEAD_AES_128_GCM'}; +aead_gcm_simultan('[email protected]', _) -> {'AEAD_AES_256_GCM', 'AEAD_AES_256_GCM'}; +aead_gcm_simultan('AEAD_AES_128_GCM', _) -> {'AEAD_AES_128_GCM', 'AEAD_AES_128_GCM'}; +aead_gcm_simultan('AEAD_AES_256_GCM', _) -> {'AEAD_AES_256_GCM', 'AEAD_AES_256_GCM'}; +aead_gcm_simultan(_, 'AEAD_AES_128_GCM') -> {'AEAD_AES_128_GCM', 'AEAD_AES_128_GCM'}; +aead_gcm_simultan(_, 'AEAD_AES_256_GCM') -> {'AEAD_AES_256_GCM', 'AEAD_AES_256_GCM'}; +aead_gcm_simultan(Cipher, Mac) -> {Cipher,Mac}. + + select_encrypt_decrypt(client, Client, Server) -> Encrypt = select(Client#ssh_msg_kexinit.encryption_algorithms_client_to_server, @@ -847,18 +828,18 @@ select_compression_decompression(client, Client, Server) -> Compression = select(Client#ssh_msg_kexinit.compression_algorithms_client_to_server, Server#ssh_msg_kexinit.compression_algorithms_client_to_server), - Decomprssion = + Decompression = select(Client#ssh_msg_kexinit.compression_algorithms_server_to_client, Server#ssh_msg_kexinit.compression_algorithms_server_to_client), - {Compression, Decomprssion}; + {Compression, Decompression}; select_compression_decompression(server, Client, Server) -> - Decomprssion = + Decompression = select(Client#ssh_msg_kexinit.compression_algorithms_client_to_server, Server#ssh_msg_kexinit.compression_algorithms_client_to_server), Compression = select(Client#ssh_msg_kexinit.compression_algorithms_server_to_client, Server#ssh_msg_kexinit.compression_algorithms_server_to_client), - {Compression, Decomprssion}. + {Compression, Decompression}. install_alg(SSH) -> SSH1 = alg_final(SSH), @@ -929,13 +910,45 @@ ssh_packet(Msg, Ssh) -> BinMsg = ssh_message:encode(Msg), pack(BinMsg, Ssh). -pack(Data0, #ssh{encrypt_block_size = BlockSize, - send_sequence = SeqNum, send_mac = MacAlg, - send_mac_key = MacKey, - random_length_padding = RandomLengthPadding} - = Ssh0) when is_binary(Data0) -> - {Ssh1, Data} = compress(Ssh0, Data0), - PL = (BlockSize - ((4 + 1 + size(Data)) rem BlockSize)) rem BlockSize, +pack(Data, Ssh=#ssh{}) -> + pack(Data, Ssh, 0). + +%%% Note: pack/3 is only to be called from tests that wants +%%% to deliberetly send packets with wrong PacketLength! +%%% Use pack/2 for all other purposes! +pack(PlainText, + #ssh{send_sequence = SeqNum, + send_mac = MacAlg, + send_mac_key = MacKey, + encrypt = CryptoAlg} = Ssh0, PacketLenDeviationForTests) when is_binary(PlainText) -> + + {Ssh1, CompressedPlainText} = compress(Ssh0, PlainText), + {EcryptedPacket, MAC, Ssh3} = + case pkt_type(CryptoAlg) of + common -> + PaddingLen = padding_length(4+1+size(CompressedPlainText), Ssh0), + Padding = ssh_bits:random(PaddingLen), + PlainPacketLen = 1 + PaddingLen + size(CompressedPlainText) + PacketLenDeviationForTests, + PlainPacketData = <<?UINT32(PlainPacketLen),?BYTE(PaddingLen), CompressedPlainText/binary, Padding/binary>>, + {Ssh2, EcryptedPacket0} = encrypt(Ssh1, PlainPacketData), + MAC0 = mac(MacAlg, MacKey, SeqNum, PlainPacketData), + {EcryptedPacket0, MAC0, Ssh2}; + aead -> + PaddingLen = padding_length(1+size(CompressedPlainText), Ssh0), + Padding = ssh_bits:random(PaddingLen), + PlainPacketLen = 1 + PaddingLen + size(CompressedPlainText) + PacketLenDeviationForTests, + PlainPacketData = <<?BYTE(PaddingLen), CompressedPlainText/binary, Padding/binary>>, + {Ssh2, {EcryptedPacket0,MAC0}} = encrypt(Ssh1, {<<?UINT32(PlainPacketLen)>>,PlainPacketData}), + {<<?UINT32(PlainPacketLen),EcryptedPacket0/binary>>, MAC0, Ssh2} + end, + FinalPacket = [EcryptedPacket, MAC], + Ssh = Ssh3#ssh{send_sequence = (SeqNum+1) band 16#ffffffff}, + {FinalPacket, Ssh}. + + +padding_length(Size, #ssh{encrypt_block_size = BlockSize, + random_length_padding = RandomLengthPadding}) -> + PL = (BlockSize - (Size rem BlockSize)) rem BlockSize, MinPaddingLen = if PL < 4 -> PL + BlockSize; true -> PL end, @@ -944,45 +957,94 @@ pack(Data0, #ssh{encrypt_block_size = BlockSize, ExtraPaddingLen = try crypto:rand_uniform(0,MaxExtraBlocks)*PadBlockSize catch _:_ -> 0 end, - PaddingLen = MinPaddingLen + ExtraPaddingLen, - Padding = ssh_bits:random(PaddingLen), - PacketLen = 1 + PaddingLen + size(Data), - PacketData = <<?UINT32(PacketLen),?BYTE(PaddingLen), - Data/binary, Padding/binary>>, - {Ssh2, EncPacket} = encrypt(Ssh1, PacketData), - MAC = mac(MacAlg, MacKey, SeqNum, PacketData), - Packet = [EncPacket, MAC], - Ssh = Ssh2#ssh{send_sequence = (SeqNum+1) band 16#ffffffff}, - {Packet, Ssh}. - -unpack(EncodedSoFar, ReminingLenght, #ssh{recv_mac_size = MacSize} = Ssh0) -> - SshLength = ReminingLenght - MacSize, - {NoMac, Mac, Rest} = case MacSize of - 0 -> - <<NoMac0:SshLength/binary, - Rest0/binary>> = EncodedSoFar, - {NoMac0, <<>>, Rest0}; - _ -> - <<NoMac0:SshLength/binary, - Mac0:MacSize/binary, - Rest0/binary>> = EncodedSoFar, - {NoMac0, Mac0, Rest0} - end, - {Ssh1, DecData, <<>>} = - case SshLength of - 0 -> - {Ssh0, <<>>, <<>>}; - _ -> - decrypt_blocks(NoMac, SshLength, Ssh0) - end, - {Ssh1, DecData, Rest, Mac}. + MinPaddingLen + ExtraPaddingLen. -msg_data(PacketData) -> - <<Len:32, PaddingLen:8, _/binary>> = PacketData, - DataLen = Len - PaddingLen - 1, - <<_:32, _:8, Data:DataLen/binary, - _:PaddingLen/binary>> = PacketData, - Data. + + +handle_packet_part(<<>>, Encrypted0, undefined, #ssh{decrypt = CryptoAlg} = Ssh0) -> + %% New ssh packet + case get_length(pkt_type(CryptoAlg), Encrypted0, Ssh0) of + get_more -> + %% too short to get the length + {get_more, <<>>, Encrypted0, undefined, Ssh0}; + + {ok, PacketLen, _, _, _} when PacketLen > ?SSH_MAX_PACKET_SIZE -> + %% far too long message than expected + throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR, + description = "Bad packet length " + ++ integer_to_list(PacketLen), + language = ""}); + + {ok, PacketLen, Decrypted, Encrypted1, + #ssh{recv_mac_size = MacSize} = Ssh1} -> + %% enough bytes so we got the length and can calculate how many + %% more bytes to expect for a full packet + TotalNeeded = (4 + PacketLen + MacSize), + handle_packet_part(Decrypted, Encrypted1, TotalNeeded, Ssh1) + end; + +handle_packet_part(DecryptedPfx, EncryptedBuffer, TotalNeeded, Ssh0) + when (size(DecryptedPfx)+size(EncryptedBuffer)) < TotalNeeded -> + %% need more bytes to finalize the packet + {get_more, DecryptedPfx, EncryptedBuffer, TotalNeeded, Ssh0}; + +handle_packet_part(DecryptedPfx, EncryptedBuffer, TotalNeeded, + #ssh{recv_mac_size = MacSize, + decrypt = CryptoAlg} = Ssh0) -> + %% enough bytes to decode the packet. + DecryptLen = TotalNeeded - size(DecryptedPfx) - MacSize, + <<EncryptedSfx:DecryptLen/binary, Mac:MacSize/binary, NextPacketBytes/binary>> = EncryptedBuffer, + case pkt_type(CryptoAlg) of + common -> + {Ssh1, DecryptedSfx} = decrypt(Ssh0, EncryptedSfx), + DecryptedPacket = <<DecryptedPfx/binary, DecryptedSfx/binary>>, + case is_valid_mac(Mac, DecryptedPacket, Ssh1) of + false -> + {bad_mac, Ssh1}; + true -> + {Ssh, DecompressedPayload} = decompress(Ssh1, payload(DecryptedPacket)), + {decoded, DecompressedPayload, NextPacketBytes, Ssh} + end; + aead -> + PacketLenBin = DecryptedPfx, + case decrypt(Ssh0, {PacketLenBin,EncryptedSfx,Mac}) of + {Ssh1, error} -> + {bad_mac, Ssh1}; + {Ssh1, DecryptedSfx} -> + DecryptedPacket = <<DecryptedPfx/binary, DecryptedSfx/binary>>, + {Ssh, DecompressedPayload} = decompress(Ssh1, payload(DecryptedPacket)), + {decoded, DecompressedPayload, NextPacketBytes, Ssh} + end + end. + + +get_length(common, EncryptedBuffer, #ssh{decrypt_block_size = BlockSize} = Ssh0) -> + case size(EncryptedBuffer) >= erlang:max(8, BlockSize) of + true -> + <<EncBlock:BlockSize/binary, EncryptedRest/binary>> = EncryptedBuffer, + {Ssh, + <<?UINT32(PacketLen),_/binary>> = Decrypted} = decrypt(Ssh0, EncBlock), + {ok, PacketLen, Decrypted, EncryptedRest, Ssh}; + false -> + get_more + end; +get_length(aead, EncryptedBuffer, Ssh) -> + case size(EncryptedBuffer) >= 4 of + true -> + <<?UINT32(PacketLen), EncryptedRest/binary>> = EncryptedBuffer, + {ok, PacketLen, <<?UINT32(PacketLen)>>, EncryptedRest, Ssh}; + false -> + get_more + end. + +pkt_type('AEAD_AES_128_GCM') -> aead; +pkt_type('AEAD_AES_256_GCM') -> aead; +pkt_type(_) -> common. + +payload(<<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>) -> + PayloadLen = PacketLen - PaddingLen - 1, + <<Payload:PayloadLen/binary, _/binary>> = PayloadAndPadding, + Payload. sign(SigData, Hash, #'DSAPrivateKey'{} = Key) -> DerSignature = public_key:sign(SigData, Hash, Key), @@ -1008,6 +1070,7 @@ verify(PlainText, Hash, Sig, {#'ECPoint'{},_} = Key) -> verify(PlainText, Hash, Sig, Key) -> public_key:verify(PlainText, Hash, Sig, Key). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Encryption @@ -1016,6 +1079,30 @@ verify(PlainText, Hash, Sig, Key) -> encrypt_init(#ssh{encrypt = none} = Ssh) -> {ok, Ssh}; +encrypt_init(#ssh{encrypt = 'AEAD_AES_128_GCM', role = client} = Ssh) -> + IV = hash(Ssh, "A", 12*8), + <<K:16/binary>> = hash(Ssh, "C", 128), + {ok, Ssh#ssh{encrypt_keys = K, + encrypt_block_size = 16, + encrypt_ctx = IV}}; +encrypt_init(#ssh{encrypt = 'AEAD_AES_128_GCM', role = server} = Ssh) -> + IV = hash(Ssh, "B", 12*8), + <<K:16/binary>> = hash(Ssh, "D", 128), + {ok, Ssh#ssh{encrypt_keys = K, + encrypt_block_size = 16, + encrypt_ctx = IV}}; +encrypt_init(#ssh{encrypt = 'AEAD_AES_256_GCM', role = client} = Ssh) -> + IV = hash(Ssh, "A", 12*8), + <<K:32/binary>> = hash(Ssh, "C", 256), + {ok, Ssh#ssh{encrypt_keys = K, + encrypt_block_size = 16, + encrypt_ctx = IV}}; +encrypt_init(#ssh{encrypt = 'AEAD_AES_256_GCM', role = server} = Ssh) -> + IV = hash(Ssh, "B", 12*8), + <<K:32/binary>> = hash(Ssh, "D", 256), + {ok, Ssh#ssh{encrypt_keys = K, + encrypt_block_size = 16, + encrypt_ctx = IV}}; encrypt_init(#ssh{encrypt = '3des-cbc', role = client} = Ssh) -> IV = hash(Ssh, "A", 64), <<K1:8/binary, K2:8/binary, K3:8/binary>> = hash(Ssh, "C", 192), @@ -1092,6 +1179,18 @@ encrypt_final(Ssh) -> encrypt(#ssh{encrypt = none} = Ssh, Data) -> {Ssh, Data}; +encrypt(#ssh{encrypt = 'AEAD_AES_128_GCM', + encrypt_keys = K, + encrypt_ctx = IV0} = Ssh, Data={_AAD,_Ptext}) -> + Enc = {_Ctext,_Ctag} = crypto:block_encrypt(aes_gcm, K, IV0, Data), + IV = next_gcm_iv(IV0), + {Ssh#ssh{encrypt_ctx = IV}, Enc}; +encrypt(#ssh{encrypt = 'AEAD_AES_256_GCM', + encrypt_keys = K, + encrypt_ctx = IV0} = Ssh, Data={_AAD,_Ptext}) -> + Enc = {_Ctext,_Ctag} = crypto:block_encrypt(aes_gcm, K, IV0, Data), + IV = next_gcm_iv(IV0), + {Ssh#ssh{encrypt_ctx = IV}, Enc}; encrypt(#ssh{encrypt = '3des-cbc', encrypt_keys = {K1,K2,K3}, encrypt_ctx = IV0} = Ssh, Data) -> @@ -1124,6 +1223,30 @@ encrypt(#ssh{encrypt = 'aes256-ctr', decrypt_init(#ssh{decrypt = none} = Ssh) -> {ok, Ssh}; +decrypt_init(#ssh{decrypt = 'AEAD_AES_128_GCM', role = client} = Ssh) -> + IV = hash(Ssh, "B", 12*8), + <<K:16/binary>> = hash(Ssh, "D", 128), + {ok, Ssh#ssh{decrypt_keys = K, + decrypt_block_size = 16, + decrypt_ctx = IV}}; +decrypt_init(#ssh{decrypt = 'AEAD_AES_128_GCM', role = server} = Ssh) -> + IV = hash(Ssh, "A", 12*8), + <<K:16/binary>> = hash(Ssh, "C", 128), + {ok, Ssh#ssh{decrypt_keys = K, + decrypt_block_size = 16, + decrypt_ctx = IV}}; +decrypt_init(#ssh{decrypt = 'AEAD_AES_256_GCM', role = client} = Ssh) -> + IV = hash(Ssh, "B", 12*8), + <<K:32/binary>> = hash(Ssh, "D", 256), + {ok, Ssh#ssh{decrypt_keys = K, + decrypt_block_size = 16, + decrypt_ctx = IV}}; +decrypt_init(#ssh{decrypt = 'AEAD_AES_256_GCM', role = server} = Ssh) -> + IV = hash(Ssh, "A", 12*8), + <<K:32/binary>> = hash(Ssh, "C", 256), + {ok, Ssh#ssh{decrypt_keys = K, + decrypt_block_size = 16, + decrypt_ctx = IV}}; decrypt_init(#ssh{decrypt = '3des-cbc', role = client} = Ssh) -> {IV, KD} = {hash(Ssh, "B", 64), hash(Ssh, "D", 192)}, @@ -1198,8 +1321,22 @@ decrypt_final(Ssh) -> decrypt_ctx = undefined, decrypt_block_size = 8}}. +decrypt(Ssh, <<>>) -> + {Ssh, <<>>}; decrypt(#ssh{decrypt = none} = Ssh, Data) -> {Ssh, Data}; +decrypt(#ssh{decrypt = 'AEAD_AES_128_GCM', + decrypt_keys = K, + decrypt_ctx = IV0} = Ssh, Data = {_AAD,_Ctext,_Ctag}) -> + Dec = crypto:block_decrypt(aes_gcm, K, IV0, Data), % Dec = PlainText | error + IV = next_gcm_iv(IV0), + {Ssh#ssh{decrypt_ctx = IV}, Dec}; +decrypt(#ssh{decrypt = 'AEAD_AES_256_GCM', + decrypt_keys = K, + decrypt_ctx = IV0} = Ssh, Data = {_AAD,_Ctext,_Ctag}) -> + Dec = crypto:block_decrypt(aes_gcm, K, IV0, Data), % Dec = PlainText | error + IV = next_gcm_iv(IV0), + {Ssh#ssh{decrypt_ctx = IV}, Dec}; decrypt(#ssh{decrypt = '3des-cbc', decrypt_keys = Keys, decrypt_ctx = IV0} = Ssh, Data) -> {K1, K2, K3} = Keys, @@ -1224,6 +1361,10 @@ decrypt(#ssh{decrypt = 'aes256-ctr', {State, Enc} = crypto:stream_decrypt(State0,Data), {Ssh#ssh{decrypt_ctx = State}, Enc}. + +next_gcm_iv(<<Fixed:32, InvCtr:64>>) -> <<Fixed:32, (InvCtr+1):64>>. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Compression %% @@ -1312,28 +1453,42 @@ decompress(#ssh{decompress = '[email protected]', decompress_ctx = Context, authe %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% send_mac_init(SSH) -> - case SSH#ssh.role of - client -> - KeySize =mac_key_size(SSH#ssh.send_mac), - Key = hash(SSH, "E", KeySize), - {ok, SSH#ssh { send_mac_key = Key }}; - server -> - KeySize = mac_key_size(SSH#ssh.send_mac), - Key = hash(SSH, "F", KeySize), - {ok, SSH#ssh { send_mac_key = Key }} + case pkt_type(SSH#ssh.send_mac) of + common -> + case SSH#ssh.role of + client -> + KeySize = mac_key_size(SSH#ssh.send_mac), + Key = hash(SSH, "E", KeySize), + {ok, SSH#ssh { send_mac_key = Key }}; + server -> + KeySize = mac_key_size(SSH#ssh.send_mac), + Key = hash(SSH, "F", KeySize), + {ok, SSH#ssh { send_mac_key = Key }} + end; + aead -> + %% Not applicable + {ok, SSH} end. send_mac_final(SSH) -> - {ok, SSH#ssh { send_mac = none, send_mac_key = undefined }}. + {ok, SSH#ssh {send_mac = none, + send_mac_key = undefined }}. + recv_mac_init(SSH) -> - case SSH#ssh.role of - client -> - Key = hash(SSH, "F", mac_key_size(SSH#ssh.recv_mac)), - {ok, SSH#ssh { recv_mac_key = Key }}; - server -> - Key = hash(SSH, "E", mac_key_size(SSH#ssh.recv_mac)), - {ok, SSH#ssh { recv_mac_key = Key }} + case pkt_type(SSH#ssh.recv_mac) of + common -> + case SSH#ssh.role of + client -> + Key = hash(SSH, "F", mac_key_size(SSH#ssh.recv_mac)), + {ok, SSH#ssh { recv_mac_key = Key }}; + server -> + Key = hash(SSH, "E", mac_key_size(SSH#ssh.recv_mac)), + {ok, SSH#ssh { recv_mac_key = Key }} + end; + aead -> + %% Not applicable + {ok, SSH} end. recv_mac_final(SSH) -> @@ -1464,6 +1619,8 @@ mac_digest_size('hmac-md5') -> 20; mac_digest_size('hmac-md5-96') -> 12; mac_digest_size('hmac-sha2-256') -> 32; mac_digest_size('hmac-sha2-512') -> 64; +mac_digest_size('AEAD_AES_128_GCM') -> 16; +mac_digest_size('AEAD_AES_256_GCM') -> 16; mac_digest_size(none) -> 0. peer_name({Host, _}) -> @@ -1475,44 +1632,10 @@ peer_name({Host, _}) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -dh_group('diffie-hellman-group1-sha1') -> element(2, ?dh_group1); -dh_group('diffie-hellman-group14-sha1') -> element(2, ?dh_group14). - -dh_gex_default_groups() -> ?dh_default_groups. - - -dh_gex_group(Min, N, Max, undefined) -> - dh_gex_group(Min, N, Max, dh_gex_default_groups()); -dh_gex_group(Min, N, Max, Groups) -> - %% First try to find an exact match. If not an exact match, select the largest possible. - {_Size,Group} = - lists:foldl( - fun(_, {I,G}) when I==N -> - %% If we have an exact match already: use that one - {I,G}; - ({I,G}, _) when I==N -> - %% If we now found an exact match: use that very one - {I,G}; - ({I,G}, {Imax,_Gmax}) when Min=<I,I=<Max, % a) {I,G} fullfills the requirements - I>Imax -> % b) {I,G} is larger than current max - %% A group within the limits and better than the one we have - {I,G}; - (_, IGmax) -> - %% Keep the one we have - IGmax - end, {-1,undefined}, Groups), - - case Group of - undefined -> - throw(#ssh_msg_disconnect{ - code = ?SSH_DISCONNECT_PROTOCOL_ERROR, - description = "No possible diffie-hellman-group-exchange group found", - language = ""}); - _ -> - Group - end. - +dh_group('diffie-hellman-group1-sha1') -> ?dh_group1; +dh_group('diffie-hellman-group14-sha1') -> ?dh_group14. +%%%---------------------------------------------------------------- generate_key(Algorithm, Args) -> {Public,Private} = crypto:generate_key(Algorithm, Args), {crypto:bytes_to_integer(Public), crypto:bytes_to_integer(Private)}. @@ -1527,6 +1650,68 @@ ecdh_curve('ecdh-sha2-nistp256') -> secp256r1; ecdh_curve('ecdh-sha2-nistp384') -> secp384r1; ecdh_curve('ecdh-sha2-nistp521') -> secp521r1. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Utils for default_algorithms/1 and supported_algorithms/1 +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) -> + [{client2server,As1},{server2client,As2}] = supported_algorithms(Key), + [{client2server,As1--BL1},{server2client,As2--BL2}]; +supported_algorithms(Key, BlackList) -> + supported_algorithms(Key) -- BlackList. + + +select_crypto_supported(L) -> + Sup = [{ec_curve,crypto_supported_curves()} | crypto:supports()], + [Name || {Name,CryptoRequires} <- L, + crypto_supported(CryptoRequires, Sup)]. + +crypto_supported_curves() -> + try crypto:ec_curves() + catch _:_ -> [] + end. + +crypto_supported(Conditions, Supported) -> + lists:all( fun({Tag,CryptoName}) when is_atom(CryptoName) -> + crypto_name_supported(Tag,CryptoName,Supported); + ({Tag,{Name,Len}}) when is_integer(Len) -> + crypto_name_supported(Tag,Name,Supported) andalso + len_supported(Name,Len) + end, Conditions). + +crypto_name_supported(Tag, CryptoName, Supported) -> + lists:member(CryptoName, proplists:get_value(Tag,Supported,[])). + +len_supported(Name, Len) -> + try + case Name of + aes_ctr -> + {_, <<_/binary>>} = + %% Test encryption + crypto:stream_encrypt(crypto:stream_init(Name, <<0:Len>>, <<0:128>>), <<"">>); + aes_gcm -> + {<<_/binary>>, <<_/binary>>} = + crypto:block_encrypt(Name, + _Key = <<0:Len>>, + _IV = <<0:12/unsigned-unit:8>>, + {<<"AAD">>,"PT"}) + end + of + _ -> true + catch + _:_ -> false + end. + + +same(Algs) -> [{client2server,Algs}, {server2client,Algs}]. + + +%% default_algorithms(kex) -> % Example of how to disable an algorithm +%% supported_algorithms(kex, ['ecdh-sha2-nistp521']); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Other utils diff --git a/lib/ssh/src/ssh_transport.hrl b/lib/ssh/src/ssh_transport.hrl index 337f455279..fd43326f0d 100644 --- a/lib/ssh/src/ssh_transport.hrl +++ b/lib/ssh/src/ssh_transport.hrl @@ -229,40 +229,13 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% rfc 2489, ch 6.2 +%%% Size 1024 -define(dh_group1, - {1024, - {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF}}). + {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF}). %%% rfc 3526, ch3 +%%% Size 2048 -define(dh_group14, - {2048, - {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF}}). - -%%% rfc 3526, ch4 --define(dh_group15, - {3072, - {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF}}). - -%%% rfc 3526, ch5 --define(dh_group16, - {4096, - {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF}}). - -%%% rfc 3526, ch6 --define(dh_group17, - {6144, - {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF}}). - -%%% rfc 3526, ch7 --define(dh_group18, - {8192, - {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF}}). - --define(dh_default_groups, [?dh_group1, - ?dh_group14, - ?dh_group15, - ?dh_group16, - ?dh_group17, - ?dh_group18] ). + {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF}). -endif. % -ifdef(ssh_transport). |