@@ -235,72 +235,68 @@ handle_userauth_request(#ssh_msg_service_request{name = Name = "ssh-userauth"},
235235
236236handle_userauth_request (# ssh_msg_userauth_request {user = User ,
237237 service = " ssh-connection" ,
238- method = " password" ,
238+ method = " password" = Method ,
239239 data = <<? FALSE , ? UINT32 (Sz ), Password :Sz /binary >>}, _ ,
240- # ssh { userauth_supported_methods = Methods } = Ssh ) ->
240+ Ssh ) ->
241241 case check_password (User , Password , Ssh ) of
242242 {true ,Ssh1 } ->
243243 {authorized , User ,
244244 {# ssh_msg_userauth_success {}, Ssh1 }
245245 };
246246 {false ,Ssh1 } ->
247- {not_authorized , {User , {error ," Bad user or password" }},
248- {# ssh_msg_userauth_failure {authentications = Methods ,
249- partial_success = false }, Ssh1 }
250- }
247+ userauth_failure (User , Method , {error ," Bad user or password" }, Ssh1 )
251248 end ;
252249
253250handle_userauth_request (# ssh_msg_userauth_request {user = User ,
254251 service = " ssh-connection" ,
255- method = " password" ,
252+ method = " password" = Method ,
256253 data = <<? TRUE ,
257254 _ /binary
258255 %% ?UINT32(Sz1), OldBinPwd :Sz1 /binary ,
259256 % % ?UINT32(Sz2), NewBinPwd:Sz2/binary
260257 >>
261- }, _ ,
262- # ssh {userauth_supported_methods = Methods } = Ssh ) ->
258+ }, _ , Ssh ) ->
263259 % % Password change without us having sent SSH_MSG_USERAUTH_PASSWD_CHANGEREQ (because we never do)
264260 % % RFC 4252 says:
265261 % % SSH_MSG_USERAUTH_FAILURE without partial success - The password
266262 % % has not been changed. Either password changing was not supported,
267263 % % or the old password was bad.
268264
269- {not_authorized , {User , {error ," Password change not supported" }},
270- {# ssh_msg_userauth_failure {authentications = Methods ,
271- partial_success = false }, Ssh }
272- };
265+ userauth_failure (User , Method , {error , " Password change not supported" }, Ssh );
273266
274267handle_userauth_request (# ssh_msg_userauth_request {user = User ,
275268 service = " ssh-connection" ,
276269 method = " none" }, _ ,
277270 # ssh {userauth_supported_methods = Methods ,
271+ auth_attempts = Attempts ,
278272 opts = Opts } = Ssh ) ->
279273 case ? GET_OPT (no_auth_needed , Opts ) of
280- false ->
281- % % The normal case
282- {not_authorized , {User , undefined },
283- {# ssh_msg_userauth_failure {authentications = Methods ,
284- partial_success = false }, Ssh }
285- };
286274 true ->
287275 % % RFC 4252 5.2
288276 {authorized , User ,
289277 {# ssh_msg_userauth_success {}, Ssh }
290- }
278+ };
279+ false when Attempts =< 1 ->
280+ % % RFC 4252 5.4: the client's first "none" request is used to query
281+ % % the available methods; like OpenSSH this first attempt is free.
282+ {not_authorized , {User , undefined },
283+ {# ssh_msg_userauth_failure {authentications = Methods ,
284+ partial_success = false }, Ssh }
285+ };
286+ false ->
287+ % % Any subsequent "none" counts as a failed attempt.
288+ userauth_failure (User , " none" , undefined , Ssh )
291289 end ;
292290
293291handle_userauth_request (# ssh_msg_userauth_request {user = User ,
294292 service = " ssh-connection" ,
295- method = " publickey" ,
293+ method = " publickey" = Method ,
296294 data = <<? BYTE (? FALSE ),
297295 ? UINT32 (ALen ), BAlg :ALen /binary ,
298296 ? UINT32 (KLen ), KeyBlob :KLen /binary ,
299297 _ /binary
300298 >>
301- },
302- _SessionId ,
303- # ssh {userauth_supported_methods = Methods } = Ssh0 ) ->
299+ }, _SessionId , Ssh0 ) ->
304300 Ssh =
305301 case check_user (User , Ssh0 ) of
306302 {true ,Ssh01 } -> Ssh01 # ssh {user = User };
@@ -313,26 +309,21 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
313309 true ->
314310 {not_authorized , {User , undefined },
315311 {# ssh_msg_userauth_pk_ok {algorithm_name = binary_to_list (BAlg ),
316- key_blob = KeyBlob }, Ssh }
312+ key_blob = KeyBlob }, Ssh }
317313 };
318314 false ->
319- {not_authorized , {User , undefined },
320- {# ssh_msg_userauth_failure {authentications = Methods ,
321- partial_success = false }, Ssh }
322- }
315+ userauth_failure (User , Method , undefined , Ssh )
323316 end ;
324317
325318handle_userauth_request (# ssh_msg_userauth_request {user = User ,
326319 service = " ssh-connection" ,
327- method = " publickey" ,
320+ method = " publickey" = Method ,
328321 data = <<? BYTE (? TRUE ),
329322 ? UINT32 (ALen ), BAlg :ALen /binary ,
330323 ? UINT32 (KLen ), KeyBlob :KLen /binary ,
331324 SigWLen /binary >>
332325 },
333- SessionId ,
334- # ssh {user = PreVerifyUser ,
335- userauth_supported_methods = Methods } = Ssh0 ) ->
326+ SessionId , # ssh {user = PreVerifyUser } = Ssh0 ) ->
336327
337328 {UserOk ,Ssh } = check_user (User , Ssh0 ),
338329 case
@@ -345,81 +336,63 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
345336 {# ssh_msg_userauth_success {}, Ssh }
346337 };
347338 false ->
348- {not_authorized , {User , undefined },
349- {# ssh_msg_userauth_failure {authentications = Methods ,
350- partial_success = false }, Ssh }
351- }
339+ userauth_failure (User , Method , undefined , Ssh )
352340 end ;
353341
354342handle_userauth_request (# ssh_msg_userauth_request {user = User ,
355343 service = " ssh-connection" ,
356344 method = " keyboard-interactive" ,
357345 data = _ },
358- _ , # ssh {opts = Opts ,
359- kb_tries_left = KbTriesLeft ,
360- userauth_supported_methods = Methods } = Ssh ) ->
361- case KbTriesLeft of
362- N when N < 1 ->
363- {not_authorized , {User , {authmethod , " keyboard-interactive" }},
364- {# ssh_msg_userauth_failure {authentications = Methods ,
365- partial_success = false }, Ssh }
366- };
367-
368- _ ->
369- % % RFC4256
370- % % The data field contains:
371- % % - language tag (deprecated). If =/=[] SHOULD use it however. We skip
372- % % it for simplicity.
373- % % - submethods. "... the user can give a hint of which actual methods
374- % % he wants to use. ...". It's a "MAY use" so we skip
375- % % it. It also needs an understanding between the client
376- % % and the server.
377- % %
378- % % "The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS,
379- % % SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message."
380- Default = {" SSH server" ,
381- " Enter password for \" " ++ User ++ " \" " ,
382- " password: " ,
383- false },
384-
385- {Name , Instruction , Prompt , Echo } =
386- case ? GET_OPT (auth_method_kb_interactive_data , Opts ) of
387- undefined ->
388- Default ;
389- {_ ,_ ,_ ,_ }= V ->
390- V ;
391- F when is_function (F , 4 ) ->
392- {_ ,PeerName } = Ssh # ssh .peer ,
393- F (PeerName , User , " ssh-connection" , Ssh # ssh .pwdfun_user_state );
394- F when is_function (F ) ->
395- {_ ,PeerName } = Ssh # ssh .peer ,
396- F (PeerName , User , " ssh-connection" )
397- end ,
398- EchoEnc = case Echo of
399- true -> <<? TRUE >>;
400- false -> <<? FALSE >>
401- end ,
402- Msg = # ssh_msg_userauth_info_request {name = unicode :characters_to_list (Name ),
403- instruction = unicode :characters_to_list (Instruction ),
404- language_tag = " " ,
405- num_prompts = 1 ,
406- data = <<? STRING (unicode :characters_to_binary (Prompt )),
407- EchoEnc /binary
408- >>
409- },
410- {not_authorized , {User , undefined },
411- {Msg , Ssh # ssh {user = User }}
412- }
413- end ;
346+ _ , # ssh {opts = Opts } = Ssh ) ->
347+ % % RFC4256
348+ % % The data field contains:
349+ % % - language tag (deprecated). If =/=[] SHOULD use it however. We skip
350+ % % it for simplicity.
351+ % % - submethods. "... the user can give a hint of which actual methods
352+ % % he wants to use. ...". It's a "MAY use" so we skip
353+ % % it. It also needs an understanding between the client
354+ % % and the server.
355+ % %
356+ % % "The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS,
357+ % % SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message."
358+ Default = {" SSH server" ,
359+ " Enter password for \" " ++ User ++ " \" " ,
360+ " password: " ,
361+ false },
362+
363+ {Name , Instruction , Prompt , Echo } =
364+ case ? GET_OPT (auth_method_kb_interactive_data , Opts ) of
365+ undefined ->
366+ Default ;
367+ {_ ,_ ,_ ,_ }= V ->
368+ V ;
369+ F when is_function (F , 4 ) ->
370+ {_ ,PeerName } = Ssh # ssh .peer ,
371+ F (PeerName , User , " ssh-connection" , Ssh # ssh .pwdfun_user_state );
372+ F when is_function (F ) ->
373+ {_ ,PeerName } = Ssh # ssh .peer ,
374+ F (PeerName , User , " ssh-connection" )
375+ end ,
376+ EchoEnc = case Echo of
377+ true -> <<? TRUE >>;
378+ false -> <<? FALSE >>
379+ end ,
380+ Msg = # ssh_msg_userauth_info_request {name = unicode :characters_to_list (Name ),
381+ instruction = unicode :characters_to_list (Instruction ),
382+ language_tag = " " ,
383+ num_prompts = 1 ,
384+ data = <<? STRING (unicode :characters_to_binary (Prompt )),
385+ EchoEnc /binary
386+ >>
387+ },
388+ {not_authorized , {User , undefined },
389+ {Msg , Ssh # ssh {user = User }}
390+ };
414391
415392handle_userauth_request (# ssh_msg_userauth_request {user = User ,
416393 service = " ssh-connection" ,
417- method = Other }, _ ,
418- # ssh {userauth_supported_methods = Methods } = Ssh ) ->
419- {not_authorized , {User , {authmethod , Other }},
420- {# ssh_msg_userauth_failure {authentications = Methods ,
421- partial_success = false }, Ssh }
422- }.
394+ method = Other }, _ , Ssh ) ->
395+ userauth_failure (User , Other , {authmethod , Other }, Ssh ).
423396
424397
425398% %%----------------------------------------------------------------
@@ -445,9 +418,7 @@ handle_userauth_info_request(#ssh_msg_userauth_info_request{name = Name,
445418handle_userauth_info_response (# ssh_msg_userauth_info_response {num_responses = 1 ,
446419 data = <<? UINT32 (Sz ), Password :Sz /binary >>},
447420 # ssh {opts = Opts ,
448- kb_tries_left = KbTriesLeft ,
449- user = User ,
450- userauth_supported_methods = Methods } = Ssh ) ->
421+ user = User } = Ssh ) ->
451422 SendOneEmpty =
452423 (? GET_OPT (tstflg ,Opts ) == one_empty )
453424 orelse
@@ -469,10 +440,8 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
469440 {# ssh_msg_userauth_success {}, Ssh1 }};
470441
471442 {false ,Ssh1 } ->
472- {not_authorized , {User , {error ," Bad user or password" }},
473- {# ssh_msg_userauth_failure {authentications = Methods ,
474- partial_success = false },
475- Ssh1 # ssh {kb_tries_left = max (KbTriesLeft - 1 , 0 )}}}
443+ Method = " keyboard-interactive" ,
444+ userauth_failure (User , Method , {error ," Bad user or password" }, Ssh1 )
476445 end ;
477446
478447handle_userauth_info_response ({extra ,# ssh_msg_userauth_info_response {}},
@@ -668,6 +637,20 @@ write_if_nonempty(_, "") -> ok;
668637write_if_nonempty (_ , <<>>) -> ok ;
669638write_if_nonempty (IoCb , Text ) -> IoCb :format (" ~s~n " ,[Text ]).
670639
640+ userauth_failure (User , _Method , Error , # ssh {auth_tries_left = Tries } = Ssh )
641+ when Tries =/= infinity , Tries =< 1 ->
642+ {auth_tries_exceeded , {User , Error }, Ssh };
643+ userauth_failure (User , _Method , Error ,
644+ # ssh {auth_tries_left = Tries , userauth_supported_methods = Methods } = Ssh ) ->
645+ AuthTriesLeft = reduce_tries_count (Tries ),
646+ {not_authorized , {User , Error },
647+ {# ssh_msg_userauth_failure {authentications = Methods ,
648+ partial_success = false },
649+ Ssh # ssh {auth_tries_left = AuthTriesLeft }}}.
650+
651+ reduce_tries_count (infinity ) -> infinity ;
652+ reduce_tries_count (N ) -> N - 1 .
653+
671654% %%----------------------------------------------------------------
672655% %% Called just for the tracer ssh_dbg
673656ssh_msg_userauth_result (_R ) -> ok .
@@ -832,7 +815,7 @@ fmt_req(#ssh_msg_userauth_request{user = User,
832815 service = " ssh-connection" ,
833816 method = Method ,
834817 data = Data },
835- # ssh {kb_tries_left = KbTriesLeft ,
818+ # ssh {auth_tries_left = AuthTriesLeft ,
836819 userauth_supported_methods = Methods }) ->
837820 [io_lib :format (" req user = ~p~n "
838821 " req method = ~p~n "
@@ -841,18 +824,21 @@ fmt_req(#ssh_msg_userauth_request{user = User,
841824 case Method of
842825 " none" -> " " ;
843826 " password" -> fmt_bool (Data );
844- " keyboard-interactive" -> fmt_kb_tries_left ( KbTriesLeft ) ;
845- " publickey" -> [ case Data of
846- <<? BYTE (_ ), ? UINT32 (ALen ), Alg :ALen /binary , _ /binary >> ->
847- io_lib :format (" ~n key-type = ~p " , [Alg ]);
848- _ ->
849- " "
850- end ] ;
827+ " keyboard-interactive" -> " " ;
828+ " publickey" -> case Data of
829+ <<? BYTE (_ ), ? UINT32 (ALen ), Alg :ALen /binary , _ /binary >> ->
830+ io_lib :format (" ~n key-type = ~p " , [Alg ]);
831+ _ ->
832+ " "
833+ end ;
851834 _ -> " "
852- end ].
835+ end ,
836+ fmt_auth_tries_left (AuthTriesLeft )].
853837
854838
855- fmt_kb_tries_left (N ) when is_integer (N )->
839+ fmt_auth_tries_left (infinity ) ->
840+ " " ;
841+ fmt_auth_tries_left (N ) when is_integer (N ) ->
856842 io_lib :format (" ~n tries left = ~p " , [N - 1 ]).
857843
858844
@@ -867,4 +853,3 @@ fmt_bool(<<>>) ->
867853 " " .
868854
869855
870-
0 commit comments