From df8da1d56961e999a43531b64a6f312b60da93d9 Mon Sep 17 00:00:00 2001
From: Hans Nilsson <hans@erlang.org>
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 = <<?BYTE(?FALSE),
+							   ?UINT32(ALen), BAlg:ALen/binary,
+							   ?UINT32(KLen), KeyBlob:KLen/binary,
+							   _/binary
+							 >>
+						 }, 
+			_SessionId, 
 			#ssh{opts = Opts,
 			     userauth_supported_methods = Methods} = Ssh) ->
 
-    <<?BYTE(HaveSig),
-      ?UINT32(ALen), BAlg:ALen/binary,
-      Rest/binary>> = Data,
-
-    {KeyBlob,  SigWLen} = 
-	case Rest of 
-	    <<?UINT32(KLen0), KeyBlob0:KLen0/binary, SigWLen0/binary>> ->
-		{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 = <<?BYTE(?TRUE),
+							   ?UINT32(ALen), BAlg:ALen/binary,
+							   ?UINT32(KLen), KeyBlob:KLen/binary,
+							   SigWLen/binary>>
+						 }, 
+			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 = <<?BOOLEAN(?FALSE)>>
+						},
+	    {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),
-	    <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
-	    <<?UINT32(AlgLen), _Alg:AlgLen/binary,
-	      ?UINT32(SigLen), Sig:SigLen/binary>> = 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),
+		<<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
+		<<?UINT32(AlgLen), _Alg:AlgLen/binary,
+		  ?UINT32(SigLen), Sig:SigLen/binary>> = 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