summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDelano Mandelbaum <delano@solutious.com>2014-09-30 17:18:39 -0700
committerDelano Mandelbaum <delano@solutious.com>2014-09-30 17:18:39 -0700
commit25ac6987c922c462af0611f2e570f2c971879143 (patch)
tree745cc27f9f23d2d1498c695fb7eb25c46df357e0
parent2c10a94e81d9f6f0cbf38c8e8c023d5ece9e334b (diff)
parent85c91e87dd5a1c34cf34f381274e26df02b91c79 (diff)
downloadnet-ssh-25ac6987c922c462af0611f2e570f2c971879143.tar.gz
Merge pull request #187 from mfazekas/password_auth_ask
Password auth will ask for password up to number_of_password_prompts tim...
-rw-r--r--lib/net/ssh/authentication/methods/password.rb45
-rw-r--r--lib/net/ssh/config.rb3
-rw-r--r--test/authentication/methods/test_password.rb43
-rw-r--r--test/test_config.rb4
4 files changed, 84 insertions, 11 deletions
diff --git a/lib/net/ssh/authentication/methods/password.rb b/lib/net/ssh/authentication/methods/password.rb
index ad1eed7..6c4f174 100644
--- a/lib/net/ssh/authentication/methods/password.rb
+++ b/lib/net/ssh/authentication/methods/password.rb
@@ -1,4 +1,5 @@
require 'net/ssh/errors'
+require 'net/ssh/prompt'
require 'net/ssh/authentication/methods/abstract'
module Net
@@ -8,25 +9,36 @@ module Net
# Implements the "password" SSH authentication method.
class Password < Abstract
+ include Prompt
+
# Attempt to authenticate the given user for the given service. If
- # the password parameter is nil, this will never do anything except
- # return false.
+ # the password parameter is nil, this will ask for password
def authenticate(next_service, username, password=nil)
- return false unless password
+ retries = 0
+ max_retries = get_max_retries
+ return false if !password && max_retries == 0
- send_message(userauth_request(username, next_service, "password", false, password))
- message = session.next_message
+ begin
+ password_to_send = password || ask_password(username)
- case message.type
- when USERAUTH_SUCCESS
- debug { "password succeeded" }
- return true
- when USERAUTH_FAILURE
+ send_message(userauth_request(username, next_service, "password", false, password_to_send))
+ message = session.next_message
+ retries += 1
+
+ if message.type == USERAUTH_FAILURE
debug { "password failed" }
raise Net::SSH::Authentication::DisallowedMethod unless
message[:authentications].split(/,/).include? 'password'
+ password = nil
+ end
+ end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
+ case message.type
+ when USERAUTH_SUCCESS
+ debug { "password succeeded" }
+ return true
+ when USERAUTH_FAILURE
return false
when USERAUTH_PASSWD_CHANGEREQ
debug { "password change request received, failing" }
@@ -35,6 +47,19 @@ module Net
raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
end
end
+
+ private
+
+ NUMBER_OF_PASSWORD_PROMPTS = 3
+
+ def ask_password(username)
+ echo = false
+ prompt("#{username}@#{session.transport.host}'s password:", echo)
+ end
+
+ def get_max_retries
+ (session.transport.options||{})[:number_of_password_prompts] || NUMBER_OF_PASSWORD_PROMPTS
+ end
end
end
diff --git a/lib/net/ssh/config.rb b/lib/net/ssh/config.rb
index e2590a9..0622472 100644
--- a/lib/net/ssh/config.rb
+++ b/lib/net/ssh/config.rb
@@ -31,6 +31,7 @@ module Net; module SSH
# * RekeyLimit => :rekey_limit
# * User => :user
# * UserKnownHostsFile => :user_known_hosts_file
+ # * NumberOfPasswordPrompts => :number_of_password_prompts
#
# Note that you will never need to use this class directly--you can control
# whether the OpenSSH configuration files are read by passing the :config
@@ -219,6 +220,8 @@ module Net; module SSH
when 'sendenv'
multi_send_env = value.to_s.split(/\s+/)
hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
+ when 'numberofpasswordprompts'
+ hash[:number_of_password_prompts] = value.to_i
end
hash
end
diff --git a/test/authentication/methods/test_password.rb b/test/authentication/methods/test_password.rb
index 4ba8207..3448b5a 100644
--- a/test/authentication/methods/test_password.rb
+++ b/test/authentication/methods/test_password.rb
@@ -1,7 +1,9 @@
require 'common'
require 'net/ssh/authentication/methods/password'
+require 'net/ssh/authentication/session'
require 'authentication/methods/common'
+
module Authentication; module Methods
class TestPassword < Test::Unit::TestCase
@@ -24,6 +26,47 @@ module Authentication; module Methods
end
end
+ def test_authenticate_ask_for_password_for_second_time_when_password_is_incorrect
+ transport.expect do |t,packet|
+ assert_equal USERAUTH_REQUEST, packet.type
+ assert_equal "jamis", packet.read_string
+ assert_equal "ssh-connection", packet.read_string
+ assert_equal "password", packet.read_string
+ assert_equal false, packet.read_bool
+ assert_equal "the-password", packet.read_string
+ t.return(USERAUTH_FAILURE, :string, "publickey,password")
+
+ t.expect do |t2, packet2|
+ assert_equal USERAUTH_REQUEST, packet2.type
+ assert_equal "jamis", packet2.read_string
+ assert_equal "ssh-connection", packet2.read_string
+ assert_equal "password", packet2.read_string
+ assert_equal false, packet2.read_bool
+ assert_equal "the-password-2", packet2.read_string
+ t.return(USERAUTH_SUCCESS)
+ end
+ end
+
+ subject.expects(:prompt).with("jamis@'s password:", false).returns("the-password-2")
+ subject.authenticate("ssh-connection", "jamis", "the-password")
+ end
+
+ def test_authenticate_ask_for_password_if_not_given
+ transport.expect do |t,packet|
+ assert_equal USERAUTH_REQUEST, packet.type
+ assert_equal "bill", packet.read_string
+ assert_equal "ssh-connection", packet.read_string
+ assert_equal "password", packet.read_string
+ assert_equal false, packet.read_bool
+ assert_equal "good-password", packet.read_string
+ t.return(USERAUTH_SUCCESS)
+ end
+
+ transport.instance_eval { @host='testhost' }
+ subject.expects(:prompt).with("bill@testhost's password:", false).returns("good-password")
+ subject.authenticate("ssh-connection", "bill", nil)
+ end
+
def test_authenticate_when_password_is_acceptible_should_return_true
transport.expect do |t,packet|
assert_equal USERAUTH_REQUEST, packet.type
diff --git a/test/test_config.rb b/test/test_config.rb
index 623281e..9b3eeb8 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -93,7 +93,8 @@ class TestConfig < Test::Unit::TestCase
'port' => 1234,
'pubkeyauthentication' => true,
'rekeylimit' => 1024,
- 'sendenv' => "LC_*"
+ 'sendenv' => "LC_*",
+ 'numberofpasswordprompts' => '123'
}
net_ssh = Net::SSH::Config.translate(open_ssh)
@@ -111,6 +112,7 @@ class TestConfig < Test::Unit::TestCase
assert_equal 1024, net_ssh[:rekey_limit]
assert_equal "127.0.0.1", net_ssh[:bind_address]
assert_equal [/^LC_.*$/], net_ssh[:send_env]
+ assert_equal 123, net_ssh[:number_of_password_prompts]
end
def test_translate_should_turn_off_authentication_methods