diff options
| author | Delano Mandelbaum <delano@blamestella.com> | 2012-02-14 04:15:04 -0800 |
|---|---|---|
| committer | Delano Mandelbaum <delano@blamestella.com> | 2012-02-14 04:15:04 -0800 |
| commit | a54775848a33aa42f97618a6c05e3121962c4e15 (patch) | |
| tree | a37d469aadae6ba287db7b96108084e235dd21bc | |
| parent | 770a08a3767f3f47e2fcd45fe05f46993bedeebc (diff) | |
| parent | 28e539663f4e6bbc163005babe1cf7df9fce831f (diff) | |
| download | net-ssh-a54775848a33aa42f97618a6c05e3121962c4e15.tar.gz | |
Merge pull request #34 from rliebling/master
Fix using pageant on windows in ruby 19
| -rw-r--r-- | lib/net/ssh/authentication/agent.rb | 6 | ||||
| -rw-r--r-- | lib/net/ssh/authentication/pageant.rb | 107 | ||||
| -rw-r--r-- | net-ssh.gemspec | 2 | ||||
| -rw-r--r-- | test/authentication/test_key_manager.rb | 29 | ||||
| -rw-r--r-- | test/test_config.rb | 5 | ||||
| -rw-r--r-- | test/test_key_factory.rb | 46 |
6 files changed, 144 insertions, 51 deletions
diff --git a/lib/net/ssh/authentication/agent.rb b/lib/net/ssh/authentication/agent.rb index 1ff40aa..2208872 100644 --- a/lib/net/ssh/authentication/agent.rb +++ b/lib/net/ssh/authentication/agent.rb @@ -3,8 +3,8 @@ require 'net/ssh/errors' require 'net/ssh/loggable' require 'net/ssh/transport/server_version' -# Only load pageant on Windows, Ruby 1.8.x -if File::ALT_SEPARATOR && !(RUBY_PLATFORM =~ /java/) && RUBY_VERSION < "1.9" +# Only load pageant on Windows +if File::ALT_SEPARATOR && !(RUBY_PLATFORM =~ /java/) require 'net/ssh/authentication/pageant' end @@ -135,7 +135,7 @@ module Net; module SSH; module Authentication # Returns the agent socket factory to use. def agent_socket_factory if File::ALT_SEPARATOR - Pageant::Socket + Pageant::socket_factory else UNIXSocket end diff --git a/lib/net/ssh/authentication/pageant.rb b/lib/net/ssh/authentication/pageant.rb index 871e86b..0159d82 100644 --- a/lib/net/ssh/authentication/pageant.rb +++ b/lib/net/ssh/authentication/pageant.rb @@ -1,5 +1,13 @@ require 'dl/import' -require 'dl/struct' + +if RUBY_VERSION < "1.9" + require 'dl/struct' +end + +if RUBY_VERSION =~ /^1.9/ + require 'dl/types' + require 'dl' +end require 'net/ssh/errors' @@ -17,15 +25,23 @@ module Net; module SSH; module Authentication # From Putty pageant.c AGENT_MAX_MSGLEN = 8192 AGENT_COPYDATA_ID = 0x804e50ba - + # The definition of the Windows methods and data structures used in # communicating with the pageant process. module Win - extend DL::Importable - - dlload 'user32' - dlload 'kernel32' - + if RUBY_VERSION < "1.9" + extend DL::Importable + + dlload 'user32' + dlload 'kernel32' + end + + if RUBY_VERSION =~ /^1.9/ + extend DL::Importer + dlload 'user32','kernel32' + include DL::Win32Types + end + typealias("LPCTSTR", "char *") # From winnt.h typealias("LPVOID", "void *") # From winnt.h typealias("LPCVOID", "const void *") # From windef.h @@ -52,7 +68,7 @@ module Net; module SSH; module Authentication # args: hFile, (ignored), flProtect, dwMaximumSizeHigh, # dwMaximumSizeLow, lpName extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, DWORD, ' + - 'DWORD, LPCTSTR)' + 'DWORD, LPCTSTR)' # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, # dwfileOffsetLow, dwNumberOfBytesToMap @@ -66,7 +82,11 @@ module Net; module SSH; module Authentication # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' + - 'UINT, UINT, PDWORD_PTR)' + 'UINT, UINT, PDWORD_PTR)' + if RUBY_VERSION < "1.9" + alias_method :FindWindow,:findWindow + module_function :FindWindow + end end # This is the pseudo-socket implementation that mimics the interface of @@ -87,7 +107,7 @@ module Net; module SSH; module Authentication # Create a new instance that communicates with the running pageant # instance. If no such instance is running, this will cause an error. def initialize - @win = Win.findWindow("Pageant", "Pageant") + @win = Win.FindWindow("Pageant", "Pageant") if @win == 0 raise Net::SSH::Exception, @@ -97,7 +117,7 @@ module Net; module SSH; module Authentication @res = nil @pos = 0 end - + # Forwards the data to #send_query, ignoring any arguments after # the first. Returns 0. def send(data, *args) @@ -132,11 +152,11 @@ module Net; module SSH; module Authentication end ptr[0] = query - + cds = [AGENT_COPYDATA_ID, mapname.size + 1, mapname]. pack("LLp").to_ptr succ = Win.sendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL, - cds, Win::SMTO_NORMAL, 5000, id) + cds, Win::SMTO_NORMAL, 5000, id) if succ > 0 retlen = 4 + ptr.to_s(4).unpack("N")[0] @@ -178,6 +198,67 @@ module Net; module SSH; module Authentication end + # Socket changes for Ruby 1.9 + # Functionality is the same as Ruby 1.8 but it includes the new calls to + # the DL module as well as other pointer transformations + class Socket19 < Socket + # Packages the given query string and sends it to the pageant + # process via the Windows messaging subsystem. The result is + # cached, to be returned piece-wise when #read is called. + def send_query(query) + res = nil + filemap = 0 + ptr = nil + id = DL.malloc(DL::SIZEOF_LONG) + + mapname = "PageantRequest%08x\000" % Win.GetCurrentThreadId() + + filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE, + Win::NULL, + Win::PAGE_READWRITE, 0, + AGENT_MAX_MSGLEN, mapname) + + if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE + raise Net::SSH::Exception, + "Creation of file mapping failed" + end + + ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0, + 0) + + if ptr.nil? || ptr.null? + raise Net::SSH::Exception, "Mapping of file failed" + end + + DL::CPtr.new(ptr)[0,query.size]=query + + cds = DL::CPtr.to_ptr [AGENT_COPYDATA_ID, mapname.size + 1, mapname]. + pack("LLp") + succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL, + cds, Win::SMTO_NORMAL, 5000, id) + + if succ > 0 + retlen = 4 + ptr.to_s(4).unpack("N")[0] + res = ptr.to_s(retlen) + end + + return res + ensure + Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null? + Win.CloseHandle(filemap) if filemap != 0 + end + end + + # Selects which socket to use depending on the ruby version + # This is needed due changes in the DL module. + def self.socket_factory + if RUBY_VERSION < "1.9" + Socket + else + Socket19 + end + end + end end; end; end diff --git a/net-ssh.gemspec b/net-ssh.gemspec index 1df8c8d..959f8a5 100644 --- a/net-ssh.gemspec +++ b/net-ssh.gemspec @@ -3,7 +3,7 @@ s.rubyforge_project = 'net-ssh' s.version = "2.3.0" s.summary = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol." - s.description = s.summary + s.description = s.summary + " It allows you to write programs that invoke and interact with processes on remote servers, via SSH2." s.authors = ["Jamis Buck", "Delano Mandelbaum"] s.email = ["net-ssh@solutious.com"] s.homepage = "http://github.com/net-ssh/net-ssh" diff --git a/test/authentication/test_key_manager.rb b/test/authentication/test_key_manager.rb index 0c6f585..6078447 100644 --- a/test/authentication/test_key_manager.rb +++ b/test/authentication/test_key_manager.rb @@ -18,7 +18,9 @@ module Authentication manager.add "/second" manager.add "/third" manager.add "/second" - assert_equal %w(/first /second /third), manager.key_files + assert_true manager.key_files.length == 3 + final_files = manager.key_files.map {|item| item.split('/').last} + assert_equal %w(first second third), final_files end def test_use_agent_should_be_set_to_false_if_agent_could_not_be_found @@ -30,9 +32,10 @@ module Authentication def test_each_identity_should_load_from_key_files manager.stubs(:agent).returns(nil) - - stub_file_private_key "/first", rsa - stub_file_private_key "/second", dsa + first = File.expand_path("/first") + second = File.expand_path("/second") + stub_file_private_key first, rsa + stub_file_private_key second, dsa identities = [] manager.each_identity { |identity| identities << identity } @@ -40,9 +43,9 @@ module Authentication assert_equal 2, identities.length assert_equal rsa.to_blob, identities.first.to_blob assert_equal dsa.to_blob, identities.last.to_blob - - assert_equal({:from => :file, :file => "/first", :key => rsa}, manager.known_identities[rsa]) - assert_equal({:from => :file, :file => "/second", :key => dsa}, manager.known_identities[dsa]) + + assert_equal({:from => :file, :file => first, :key => rsa}, manager.known_identities[rsa]) + assert_equal({:from => :file, :file => second, :key => dsa}, manager.known_identities[dsa]) end def test_identities_should_load_from_agent @@ -62,7 +65,8 @@ module Authentication def test_only_identities_with_key_files_should_load_from_agent_of_keys_only_set manager(:keys_only => true).stubs(:agent).returns(agent) - stub_file_private_key "/first", rsa + first = File.expand_path("/first") + stub_file_private_key first, rsa identities = [] manager.each_identity { |identity| identities << identity } @@ -76,8 +80,10 @@ module Authentication def test_identities_without_public_key_files_should_not_be_touched_if_identity_loaded_from_agent manager.stubs(:agent).returns(agent) - stub_file_public_key "/first", rsa - stub_file_private_key "/second", dsa, :passphrase => :should_not_be_asked + first = File.expand_path("/first") + stub_file_public_key first, rsa + second = File.expand_path("/second") + stub_file_private_key second, dsa, :passphrase => :should_not_be_asked identities = [] manager.each_identity do |identity| @@ -98,7 +104,8 @@ module Authentication def test_sign_with_file_originated_key_should_load_private_key_and_sign_with_it manager.stubs(:agent).returns(nil) - stub_file_private_key "/first", rsa(512) + first = File.expand_path("/first") + stub_file_private_key first, rsa(512) rsa.expects(:ssh_do_sign).with("hello, world").returns("abcxyz123") manager.each_identity { |identity| } # preload the known_identities assert_equal "\0\0\0\assh-rsa\0\0\0\011abcxyz123", manager.sign(rsa, "hello, world") diff --git a/test/test_config.rb b/test/test_config.rb index 5fc9795..548072f 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -3,8 +3,9 @@ require 'net/ssh/config' class TestConfig < Test::Unit::TestCase def test_load_for_non_existant_file_should_return_empty_hash - File.expects(:readable?).with("/bogus/file").returns(false) - assert_equal({}, Net::SSH::Config.load("/bogus/file", "host.name")) + bogus_file = File.expand_path("/bogus/file") + File.expects(:readable?).with(bogus_file).returns(false) + assert_equal({}, Net::SSH::Config.load(bogus_file, "host.name")) end def test_load_should_expand_path diff --git a/test/test_key_factory.rb b/test/test_key_factory.rb index caaa899..8ce07c8 100644 --- a/test/test_key_factory.rb +++ b/test/test_key_factory.rb @@ -2,53 +2,57 @@ require 'common' require 'net/ssh/key_factory' class TestKeyFactory < Test::Unit::TestCase + def setup + @key_file = File.expand_path("/key-file") + end + def test_load_unencrypted_private_RSA_key_should_return_key - File.expects(:read).with("/key-file").returns(rsa_key.export) - assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der + File.expects(:read).with(@key_file).returns(rsa_key.export) + assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file).to_der end def test_load_unencrypted_private_DSA_key_should_return_key - File.expects(:read).with("/key-file").returns(dsa_key.export) - assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der + File.expects(:read).with(@key_file).returns(dsa_key.export) + assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file).to_der end def test_load_encrypted_private_RSA_key_should_prompt_for_password_and_return_key - File.expects(:read).with("/key-file").returns(encrypted(rsa_key, "password")) - Net::SSH::KeyFactory.expects(:prompt).with("Enter passphrase for /key-file:", false).returns("password") - assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der + File.expects(:read).with(@key_file).returns(encrypted(rsa_key, "password")) + Net::SSH::KeyFactory.expects(:prompt).with("Enter passphrase for #{@key_file}:", false).returns("password") + assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file).to_der end def test_load_encrypted_private_RSA_key_with_password_should_not_prompt_and_return_key - File.expects(:read).with("/key-file").returns(encrypted(rsa_key, "password")) - assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file", "password").to_der + File.expects(:read).with(@key_file).returns(encrypted(rsa_key, "password")) + assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file, "password").to_der end def test_load_encrypted_private_DSA_key_should_prompt_for_password_and_return_key - File.expects(:read).with("/key-file").returns(encrypted(dsa_key, "password")) - Net::SSH::KeyFactory.expects(:prompt).with("Enter passphrase for /key-file:", false).returns("password") - assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der + File.expects(:read).with(@key_file).returns(encrypted(dsa_key, "password")) + Net::SSH::KeyFactory.expects(:prompt).with("Enter passphrase for #{@key_file}:", false).returns("password") + assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file).to_der end def test_load_encrypted_private_DSA_key_with_password_should_not_prompt_and_return_key - File.expects(:read).with("/key-file").returns(encrypted(dsa_key, "password")) - assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file", "password").to_der + File.expects(:read).with(@key_file).returns(encrypted(dsa_key, "password")) + assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file, "password").to_der end def test_load_encrypted_private_key_should_give_three_tries_for_the_password_and_then_raise_exception - File.expects(:read).with("/key-file").returns(encrypted(rsa_key, "password")) - Net::SSH::KeyFactory.expects(:prompt).times(3).with("Enter passphrase for /key-file:", false).returns("passwod","passphrase","passwd") - assert_raises(OpenSSL::PKey::RSAError) { Net::SSH::KeyFactory.load_private_key("/key-file") } + File.expects(:read).with(@key_file).returns(encrypted(rsa_key, "password")) + Net::SSH::KeyFactory.expects(:prompt).times(3).with("Enter passphrase for #{@key_file}:", false).returns("passwod","passphrase","passwd") + assert_raises(OpenSSL::PKey::RSAError) { Net::SSH::KeyFactory.load_private_key(@key_file) } end def test_load_encrypted_private_key_should_raise_exception_without_asking_passphrase - File.expects(:read).with("/key-file").returns(encrypted(rsa_key, "password")) + File.expects(:read).with(@key_file).returns(encrypted(rsa_key, "password")) Net::SSH::KeyFactory.expects(:prompt).never - assert_raises(OpenSSL::PKey::RSAError) { Net::SSH::KeyFactory.load_private_key("/key-file", nil, false) } + assert_raises(OpenSSL::PKey::RSAError) { Net::SSH::KeyFactory.load_private_key(@key_file, nil, false) } end def test_load_public_rsa_key_should_return_key - File.expects(:read).with("/key-file").returns(public(rsa_key)) - assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key("/key-file").to_blob + File.expects(:read).with(@key_file).returns(public(rsa_key)) + assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob end private |
