From 7fa1f7e973369dedd56f1be17d3b075d72ec0b60 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Thu, 1 Sep 2016 18:20:22 +0200 Subject: ssh: fix no detect of tcp close --- lib/ssh/src/ssh_connection_handler.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 2eb29c9b32..00bf1a3885 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -1238,9 +1238,12 @@ handle_event(internal, prepare_next_packet, _, D) -> handle_event(info, {CloseTag,Socket}, StateName, D = #data{socket = Socket, transport_close_tag = CloseTag}) -> - disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, - description = "Connection closed"}, - StateName, D); + %% Simulate a disconnect from the peer + handle_event(info, + #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, + description = "Connection closed"}, + StateName, + D); handle_event(info, {timeout, {_, From} = Request}, _, #data{connection_state = #connection{requests = Requests} = C0} = D) -> -- cgit v1.2.3 From c4e9732c040966366e0719a62550f30e45fc01a3 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Thu, 1 Sep 2016 22:38:13 +0200 Subject: ssh: separate clauses for first and second pk auth msg SSH sends the public key and user name twice. If we do not check the validity of that pair at the first time, Codenomicon Defensics will complain. --- lib/ssh/src/ssh_auth.erl | 66 +++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 1dcf5d0708..7793d77f36 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -260,43 +260,45 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", method = "publickey", - data = Data}, + data = <> + }, SessionId, #ssh{opts = Opts, userauth_supported_methods = Methods} = Ssh) -> - <> = Data, - - {KeyBlob, SigWLen} = - case Rest of - <> -> - {KeyBlob0, SigWLen0}; - <<>> -> - {<<>>, <<>>} - end, + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet( + #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg), + key_blob = KeyBlob}, Ssh)}; - case HaveSig of - ?TRUE -> - case verify_sig(SessionId, User, "ssh-connection", - binary_to_list(BAlg), - KeyBlob, SigWLen, Opts) of - true -> - {authorized, User, - ssh_transport:ssh_packet( - #ssh_msg_userauth_success{}, Ssh)}; - false -> - {not_authorized, {User, undefined}, - ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ - authentications = Methods, - partial_success = false}, Ssh)} - end; - ?FALSE -> - {not_authorized, {User, undefined}, +handle_userauth_request(#ssh_msg_userauth_request{user = User, + service = "ssh-connection", + method = "publickey", + data = <> + }, + SessionId, + #ssh{opts = Opts, + userauth_supported_methods = Methods} = Ssh) -> + + case verify_sig(SessionId, User, "ssh-connection", + binary_to_list(BAlg), + KeyBlob, SigWLen, Opts) of + true -> + {authorized, User, ssh_transport:ssh_packet( - #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg), - key_blob = KeyBlob}, Ssh)} + #ssh_msg_userauth_success{}, Ssh)}; + false -> + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ + authentications = Methods, + partial_success = false}, Ssh)} end; handle_userauth_request(#ssh_msg_userauth_request{user = User, @@ -484,6 +486,8 @@ get_password_option(Opts, User) -> false -> proplists:get_value(password, Opts, false) end. +%%pre_verify_sig(SessionId, User, Service, Alg, KeyBlob, Opts) -> + verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> {ok, Key} = decode_public_key_v2(KeyBlob, Alg), KeyCb = proplists:get_value(key_cb, Opts, ssh_file), -- cgit v1.2.3 From 9b988fa6edd9db2396ade2141e14f0fc7b68cfd2 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Thu, 1 Sep 2016 21:31:54 +0200 Subject: ssh: make ecdsa sha dependent on curve Bug fix. --- lib/ssh/src/ssh_auth.erl | 4 ++-- lib/ssh/src/ssh_transport.erl | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 1dcf5d0708..afc6ec5a56 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -140,7 +140,7 @@ publickey_msg([Alg, #ssh{user = User, session_id = SessionId, service = Service, opts = Opts} = Ssh]) -> - Hash = sha, %% Maybe option?! + Hash = ssh_transport:sha(Alg), KeyCb = proplists:get_value(key_cb, Opts, ssh_file), case KeyCb:user_key(Alg, Opts) of {ok, PrivKey} -> @@ -495,7 +495,7 @@ verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> <> = SigWLen, <> = AlgSig, - ssh_transport:verify(PlainText, sha, Sig, Key); + ssh_transport:verify(PlainText, ssh_transport:sha(list_to_atom(Alg)), Sig, Key); false -> false end. diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 7cb3b75ac0..15b80de30a 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -46,7 +46,7 @@ handle_kex_ecdh_reply/2, extract_public_key/1, ssh_packet/2, pack/2, - sign/3, verify/4]). + sha/1, sign/3, verify/4]). %%% For test suites -export([pack/3]). @@ -1619,6 +1619,11 @@ kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) -> crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L). +sha('ssh-rsa') -> sha; +sha('ssh-dss') -> sha; +sha('ecdsa-sha2-nistp256') -> sha(secp256r1); +sha('ecdsa-sha2-nistp384') -> sha(secp384r1); +sha('ecdsa-sha2-nistp521') -> sha(secp521r1); sha(secp256r1) -> sha256; sha(secp384r1) -> sha384; sha(secp521r1) -> sha512; -- cgit v1.2.3 From c0dfb5487a5ca79c35506905090b14e4abe06e3a Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Thu, 1 Sep 2016 22:52:04 +0200 Subject: ssh: pre-verify key Conflicts: lib/ssh/src/ssh_auth.erl --- lib/ssh/src/ssh_auth.erl | 58 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 7793d77f36..0c1aaa2ede 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -266,14 +266,23 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, _/binary >> }, - SessionId, + _SessionId, #ssh{opts = Opts, userauth_supported_methods = Methods} = Ssh) -> - {not_authorized, {User, undefined}, - ssh_transport:ssh_packet( - #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg), - key_blob = KeyBlob}, Ssh)}; + case pre_verify_sig(User, binary_to_list(BAlg), + KeyBlob, Opts) of + true -> + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet( + #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg), + key_blob = KeyBlob}, Ssh)}; + false -> + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ + authentications = Methods, + partial_success = false}, Ssh)} + end; handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", @@ -486,21 +495,34 @@ get_password_option(Opts, User) -> false -> proplists:get_value(password, Opts, false) end. -%%pre_verify_sig(SessionId, User, Service, Alg, KeyBlob, Opts) -> +pre_verify_sig(User, Alg, KeyBlob, Opts) -> + try + {ok, Key} = decode_public_key_v2(KeyBlob, Alg), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + KeyCb:is_auth_key(Key, User, Opts) + catch + _:_ -> + false + end. verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> - {ok, Key} = decode_public_key_v2(KeyBlob, Alg), - KeyCb = proplists:get_value(key_cb, Opts, ssh_file), - - case KeyCb:is_auth_key(Key, User, Opts) of - true -> - PlainText = build_sig_data(SessionId, User, - Service, KeyBlob, Alg), - <> = SigWLen, - <> = AlgSig, - ssh_transport:verify(PlainText, sha, Sig, Key); - false -> + try + {ok, Key} = decode_public_key_v2(KeyBlob, Alg), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + + case KeyCb:is_auth_key(Key, User, Opts) of + true -> + PlainText = build_sig_data(SessionId, User, + Service, KeyBlob, Alg), + <> = SigWLen, + <> = AlgSig, + ssh_transport:verify(PlainText, ssh_transport:sha(list_to_atom(Alg)), Sig, Key); + false -> + false + end + catch + _:_ -> false end. -- cgit v1.2.3 From df8da1d56961e999a43531b64a6f312b60da93d9 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Fri, 2 Sep 2016 14:55:58 +0200 Subject: ssh: add tstflg value one_empty to force daemon send empty ssh_msg_userauth_info_request This behavour is assumed by Codenomicon Defensics. --- lib/ssh/src/ssh_auth.erl | 131 ++++++++++++++++++++++----------- lib/ssh/src/ssh_connection_handler.erl | 14 +++- lib/ssh/src/ssh_transport.erl | 7 +- 3 files changed, 106 insertions(+), 46 deletions(-) diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 1dcf5d0708..ac35b70209 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -140,7 +140,7 @@ publickey_msg([Alg, #ssh{user = User, session_id = SessionId, service = Service, opts = Opts} = Ssh]) -> - Hash = sha, %% Maybe option?! + Hash = ssh_transport:sha(Alg), KeyCb = proplists:get_value(key_cb, Opts, ssh_file), case KeyCb:user_key(Alg, Opts) of {ok, PrivKey} -> @@ -260,43 +260,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", method = "publickey", - data = Data}, - SessionId, + data = <> + }, + _SessionId, #ssh{opts = Opts, userauth_supported_methods = Methods} = Ssh) -> - <> = Data, - - {KeyBlob, SigWLen} = - case Rest of - <> -> - {KeyBlob0, SigWLen0}; - <<>> -> - {<<>>, <<>>} - end, - - case HaveSig of - ?TRUE -> - case verify_sig(SessionId, User, "ssh-connection", - binary_to_list(BAlg), - KeyBlob, SigWLen, Opts) of - true -> - {authorized, User, - ssh_transport:ssh_packet( - #ssh_msg_userauth_success{}, Ssh)}; - false -> - {not_authorized, {User, undefined}, - ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ - authentications = Methods, - partial_success = false}, Ssh)} - end; - ?FALSE -> + case pre_verify_sig(User, binary_to_list(BAlg), + KeyBlob, Opts) of + true -> {not_authorized, {User, undefined}, ssh_transport:ssh_packet( #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg), - key_blob = KeyBlob}, Ssh)} + key_blob = KeyBlob}, Ssh)}; + false -> + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ + authentications = Methods, + partial_success = false}, Ssh)} + end; + +handle_userauth_request(#ssh_msg_userauth_request{user = User, + service = "ssh-connection", + method = "publickey", + data = <> + }, + SessionId, + #ssh{opts = Opts, + userauth_supported_methods = Methods} = Ssh) -> + + case verify_sig(SessionId, User, "ssh-connection", + binary_to_list(BAlg), + KeyBlob, SigWLen, Opts) of + true -> + {authorized, User, + ssh_transport:ssh_packet( + #ssh_msg_userauth_success{}, Ssh)}; + false -> + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ + authentications = Methods, + partial_success = false}, Ssh)} end; handle_userauth_request(#ssh_msg_userauth_request{user = User, @@ -395,10 +406,22 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, kb_tries_left = KbTriesLeft, user = User, userauth_supported_methods = Methods} = Ssh) -> + SendOneEmpty = proplists:get_value(tstflg, Opts) == one_empty, case check_password(User, unicode:characters_to_list(Password), Opts, Ssh) of + {true,Ssh1} when SendOneEmpty==true -> + Msg = #ssh_msg_userauth_info_request{name = "", + instruction = "", + language_tag = "", + num_prompts = 0, + data = <> + }, + {authorized_but_one_more, User, + ssh_transport:ssh_packet(Msg, Ssh1)}; + {true,Ssh1} -> {authorized, User, 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{ @@ -408,6 +431,11 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, )} end; +handle_userauth_info_response({extra,#ssh_msg_userauth_info_response{}}, + #ssh{user = User} = Ssh) -> + {authorized, User, + ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)}; + handle_userauth_info_response(#ssh_msg_userauth_info_response{}, _Auth) -> ssh_connection_handler:disconnect( @@ -484,19 +512,34 @@ get_password_option(Opts, User) -> false -> proplists:get_value(password, Opts, false) end. -verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> - {ok, Key} = decode_public_key_v2(KeyBlob, Alg), - KeyCb = proplists:get_value(key_cb, Opts, ssh_file), +pre_verify_sig(User, Alg, KeyBlob, Opts) -> + try + {ok, Key} = decode_public_key_v2(KeyBlob, Alg), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + KeyCb:is_auth_key(Key, User, Opts) + catch + _:_ -> + false + end. - case KeyCb:is_auth_key(Key, User, Opts) of - true -> - PlainText = build_sig_data(SessionId, User, - Service, KeyBlob, Alg), - <> = SigWLen, - <> = AlgSig, - ssh_transport:verify(PlainText, sha, Sig, Key); - false -> +verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> + try + {ok, Key} = decode_public_key_v2(KeyBlob, Alg), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + + case KeyCb:is_auth_key(Key, User, Opts) of + true -> + PlainText = build_sig_data(SessionId, User, + Service, KeyBlob, Alg), + <> = SigWLen, + <> = AlgSig, + ssh_transport:verify(PlainText, ssh_transport:sha(list_to_atom(Alg)), Sig, Key); + false -> + false + end + catch + _:_ -> false end. diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 2eb29c9b32..8ed638cec9 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -822,9 +822,21 @@ handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_inte {not_authorized, {User, Reason}, {Reply, Ssh}} -> retry_fun(User, Reason, D), send_bytes(Reply, D), - {next_state, {userauth,server}, D#data{ssh_params = Ssh}} + {next_state, {userauth,server}, D#data{ssh_params = Ssh}}; + + {authorized_but_one_more, _User, {Reply, Ssh}} -> + send_bytes(Reply, D), + {next_state, {userauth_keyboard_interactive_extra,server}, D#data{ssh_params = Ssh}} end; +handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive_extra, server}, D) -> + {authorized, User, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response({extra,Msg}, D#data.ssh_params), + send_bytes(Reply, D), + D#data.starter ! ssh_connected, + connected_fun(User, "keyboard-interactive", D), + {next_state, {connected,server}, D#data{auth_user = User, + ssh_params = Ssh#ssh{authenticated = true}}}; + handle_event(_, Msg = #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive, client}, #data{ssh_params = Ssh0} = D0) -> Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Ssh0#ssh.userauth_preference, diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 7cb3b75ac0..15b80de30a 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -46,7 +46,7 @@ handle_kex_ecdh_reply/2, extract_public_key/1, ssh_packet/2, pack/2, - sign/3, verify/4]). + sha/1, sign/3, verify/4]). %%% For test suites -export([pack/3]). @@ -1619,6 +1619,11 @@ kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) -> crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L). +sha('ssh-rsa') -> sha; +sha('ssh-dss') -> sha; +sha('ecdsa-sha2-nistp256') -> sha(secp256r1); +sha('ecdsa-sha2-nistp384') -> sha(secp384r1); +sha('ecdsa-sha2-nistp521') -> sha(secp521r1); sha(secp256r1) -> sha256; sha(secp384r1) -> sha384; sha(secp521r1) -> sha512; -- cgit v1.2.3