require 'net/ssh/prompt' require 'net/ssh/authentication/methods/abstract' module Net module SSH module Authentication module Methods # Implements the "keyboard-interactive" SSH authentication method. class KeyboardInteractive < Abstract USERAUTH_INFO_REQUEST = 60 USERAUTH_INFO_RESPONSE = 61 # Attempt to authenticate the given user for the given service. def authenticate(next_service, username, password = nil) debug { "trying keyboard-interactive" } send_message(userauth_request(username, next_service, "keyboard-interactive", "", "")) prompter = nil loop do message = session.next_message case message.type when USERAUTH_SUCCESS debug { "keyboard-interactive succeeded" } prompter.success if prompter return true when USERAUTH_FAILURE debug { "keyboard-interactive failed" } raise Net::SSH::Authentication::DisallowedMethod unless message[:authentications].split(/,/).include? 'keyboard-interactive' return false unless interactive? password = nil debug { "retrying keyboard-interactive" } send_message(userauth_request(username, next_service, "keyboard-interactive", "", "")) when USERAUTH_INFO_REQUEST name = message.read_string instruction = message.read_string debug { "keyboard-interactive info request" } if password.nil? && interactive? && prompter.nil? prompter = prompt.start(type: 'keyboard-interactive', name: name, instruction: instruction) end _ = message.read_string # lang_tag responses = [] message.read_long.times do text = message.read_string echo = message.read_bool password_to_send = password || (prompter && prompter.ask(text, echo)) responses << password_to_send end # if the password failed the first time around, don't try # and use it on subsequent requests. password = nil msg = Buffer.from(:byte, USERAUTH_INFO_RESPONSE, :long, responses.length, :string, responses) send_message(msg) else raise Net::SSH::Exception, "unexpected reply in keyboard interactive: #{message.type} (#{message.inspect})" end end end def interactive? options = session.transport.options || {} !options[:non_interactive] end end end end end end