sonixlabs-net-ssh 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. data/CHANGELOG.rdoc +262 -0
  2. data/Manifest +121 -0
  3. data/README.rdoc +184 -0
  4. data/Rakefile +86 -0
  5. data/Rudyfile +96 -0
  6. data/THANKS.rdoc +19 -0
  7. data/lib/net/ssh.rb +223 -0
  8. data/lib/net/ssh/authentication/agent.rb +179 -0
  9. data/lib/net/ssh/authentication/constants.rb +18 -0
  10. data/lib/net/ssh/authentication/key_manager.rb +253 -0
  11. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  12. data/lib/net/ssh/authentication/methods/hostbased.rb +75 -0
  13. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +70 -0
  14. data/lib/net/ssh/authentication/methods/password.rb +43 -0
  15. data/lib/net/ssh/authentication/methods/publickey.rb +96 -0
  16. data/lib/net/ssh/authentication/pageant.rb +264 -0
  17. data/lib/net/ssh/authentication/session.rb +146 -0
  18. data/lib/net/ssh/buffer.rb +340 -0
  19. data/lib/net/ssh/buffered_io.rb +198 -0
  20. data/lib/net/ssh/config.rb +207 -0
  21. data/lib/net/ssh/connection/channel.rb +630 -0
  22. data/lib/net/ssh/connection/constants.rb +33 -0
  23. data/lib/net/ssh/connection/session.rb +597 -0
  24. data/lib/net/ssh/connection/term.rb +178 -0
  25. data/lib/net/ssh/errors.rb +88 -0
  26. data/lib/net/ssh/key_factory.rb +102 -0
  27. data/lib/net/ssh/known_hosts.rb +129 -0
  28. data/lib/net/ssh/loggable.rb +61 -0
  29. data/lib/net/ssh/packet.rb +102 -0
  30. data/lib/net/ssh/prompt.rb +93 -0
  31. data/lib/net/ssh/proxy/command.rb +75 -0
  32. data/lib/net/ssh/proxy/errors.rb +14 -0
  33. data/lib/net/ssh/proxy/http.rb +94 -0
  34. data/lib/net/ssh/proxy/socks4.rb +70 -0
  35. data/lib/net/ssh/proxy/socks5.rb +142 -0
  36. data/lib/net/ssh/ruby_compat.rb +43 -0
  37. data/lib/net/ssh/service/forward.rb +298 -0
  38. data/lib/net/ssh/test.rb +89 -0
  39. data/lib/net/ssh/test/channel.rb +129 -0
  40. data/lib/net/ssh/test/extensions.rb +152 -0
  41. data/lib/net/ssh/test/kex.rb +44 -0
  42. data/lib/net/ssh/test/local_packet.rb +51 -0
  43. data/lib/net/ssh/test/packet.rb +81 -0
  44. data/lib/net/ssh/test/remote_packet.rb +38 -0
  45. data/lib/net/ssh/test/script.rb +157 -0
  46. data/lib/net/ssh/test/socket.rb +64 -0
  47. data/lib/net/ssh/transport/algorithms.rb +386 -0
  48. data/lib/net/ssh/transport/cipher_factory.rb +79 -0
  49. data/lib/net/ssh/transport/constants.rb +30 -0
  50. data/lib/net/ssh/transport/hmac.rb +42 -0
  51. data/lib/net/ssh/transport/hmac/abstract.rb +79 -0
  52. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  53. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  54. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  55. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  56. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  57. data/lib/net/ssh/transport/hmac/sha2_256.rb +15 -0
  58. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +13 -0
  59. data/lib/net/ssh/transport/hmac/sha2_512.rb +14 -0
  60. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +13 -0
  61. data/lib/net/ssh/transport/identity_cipher.rb +55 -0
  62. data/lib/net/ssh/transport/kex.rb +17 -0
  63. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
  64. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +80 -0
  65. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +15 -0
  66. data/lib/net/ssh/transport/key_expander.rb +26 -0
  67. data/lib/net/ssh/transport/openssl.rb +127 -0
  68. data/lib/net/ssh/transport/packet_stream.rb +235 -0
  69. data/lib/net/ssh/transport/server_version.rb +71 -0
  70. data/lib/net/ssh/transport/session.rb +278 -0
  71. data/lib/net/ssh/transport/state.rb +206 -0
  72. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  73. data/lib/net/ssh/verifiers/null.rb +12 -0
  74. data/lib/net/ssh/verifiers/strict.rb +53 -0
  75. data/lib/net/ssh/version.rb +62 -0
  76. data/lib/sonixlabs-net-ssh.rb +1 -0
  77. data/net-ssh.gemspec +145 -0
  78. data/setup.rb +1585 -0
  79. data/support/arcfour_check.rb +20 -0
  80. data/support/ssh_tunnel_bug.rb +65 -0
  81. data/test/authentication/methods/common.rb +28 -0
  82. data/test/authentication/methods/test_abstract.rb +51 -0
  83. data/test/authentication/methods/test_hostbased.rb +114 -0
  84. data/test/authentication/methods/test_keyboard_interactive.rb +100 -0
  85. data/test/authentication/methods/test_password.rb +52 -0
  86. data/test/authentication/methods/test_publickey.rb +148 -0
  87. data/test/authentication/test_agent.rb +205 -0
  88. data/test/authentication/test_key_manager.rb +171 -0
  89. data/test/authentication/test_session.rb +106 -0
  90. data/test/common.rb +107 -0
  91. data/test/configs/eqsign +3 -0
  92. data/test/configs/exact_match +8 -0
  93. data/test/configs/host_plus +10 -0
  94. data/test/configs/multihost +4 -0
  95. data/test/configs/wild_cards +14 -0
  96. data/test/connection/test_channel.rb +467 -0
  97. data/test/connection/test_session.rb +488 -0
  98. data/test/test_all.rb +9 -0
  99. data/test/test_buffer.rb +336 -0
  100. data/test/test_buffered_io.rb +63 -0
  101. data/test/test_config.rb +120 -0
  102. data/test/test_key_factory.rb +79 -0
  103. data/test/transport/hmac/test_md5.rb +39 -0
  104. data/test/transport/hmac/test_md5_96.rb +25 -0
  105. data/test/transport/hmac/test_none.rb +34 -0
  106. data/test/transport/hmac/test_sha1.rb +34 -0
  107. data/test/transport/hmac/test_sha1_96.rb +25 -0
  108. data/test/transport/hmac/test_sha2_256.rb +35 -0
  109. data/test/transport/hmac/test_sha2_256_96.rb +25 -0
  110. data/test/transport/hmac/test_sha2_512.rb +35 -0
  111. data/test/transport/hmac/test_sha2_512_96.rb +25 -0
  112. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  113. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  114. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +33 -0
  115. data/test/transport/test_algorithms.rb +308 -0
  116. data/test/transport/test_cipher_factory.rb +213 -0
  117. data/test/transport/test_hmac.rb +34 -0
  118. data/test/transport/test_identity_cipher.rb +40 -0
  119. data/test/transport/test_packet_stream.rb +736 -0
  120. data/test/transport/test_server_version.rb +78 -0
  121. data/test/transport/test_session.rb +315 -0
  122. data/test/transport/test_state.rb +179 -0
  123. metadata +178 -0
data/Rakefile ADDED
@@ -0,0 +1,86 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'fileutils'
5
+ include FileUtils
6
+
7
+ begin
8
+ require 'hanna/rdoctask'
9
+ rescue LoadError
10
+ require 'rdoc/task'
11
+ end
12
+
13
+
14
+ task :default => :package
15
+
16
+ # CONFIG =============================================================
17
+
18
+ # Change the following according to your needs
19
+ README = "README.rdoc"
20
+ CHANGES = "CHANGELOG.rdoc"
21
+ THANKS = 'THANKS.rdoc'
22
+
23
+ # Files and directories to be deleted when you run "rake clean"
24
+ CLEAN.include [ 'pkg', '*.gem', '.config', 'doc']
25
+
26
+ # Virginia assumes your project and gemspec have the same name
27
+ name = 'net-ssh'
28
+ load "#{name}.gemspec"
29
+ version = @spec.version
30
+
31
+ # That's it! The following defaults should allow you to get started
32
+ # on other things.
33
+
34
+
35
+ # TESTS/SPECS =========================================================
36
+
37
+
38
+
39
+ # INSTALL =============================================================
40
+
41
+ Rake::GemPackageTask.new(@spec) do |p|
42
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
43
+ end
44
+
45
+ task :build => [ :package ]
46
+ task :release => [ :rdoc, :package ]
47
+ task :install => [ :rdoc, :package ] do
48
+ sh %{sudo gem install pkg/#{name}-#{version}.gem}
49
+ end
50
+ task :uninstall => [ :clean ] do
51
+ sh %{sudo gem uninstall #{name}}
52
+ end
53
+
54
+
55
+ # RUBYFORGE RELEASE / PUBLISH TASKS ==================================
56
+
57
+ if @spec.rubyforge_project
58
+ desc 'Publish website to rubyforge'
59
+ task 'publish:rdoc' => 'doc/index.html' do
60
+ sh "scp -r doc/* rubyforge.org:/var/www/gforge-projects/#{name}/ssh/v2/api/"
61
+ end
62
+
63
+ desc 'Public release to rubyforge'
64
+ task 'publish:gem' => [:package] do |t|
65
+ sh <<-end
66
+ rubyforge add_release -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem &&
67
+ rubyforge add_file -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+
74
+ # RUBY DOCS TASK ==================================
75
+
76
+ Rake::RDocTask.new do |t|
77
+ t.rdoc_dir = 'doc'
78
+ t.title = @spec.summary
79
+ t.options << '--line-numbers' << '-A cattr_accessor=object'
80
+ t.options << '--charset' << 'utf-8'
81
+ t.rdoc_files.include(README)
82
+ t.rdoc_files.include(CHANGES)
83
+ t.rdoc_files.include(THANKS)
84
+ t.rdoc_files.include('lib/**/*.rb')
85
+ end
86
+
data/Rudyfile ADDED
@@ -0,0 +1,96 @@
1
+ # Rudyfile
2
+ #
3
+ # This configuration is used to test installing
4
+ # and running net-ssh on a clean machine.
5
+ #
6
+ # Usage:
7
+ #
8
+ # $ rudy -vv startup
9
+ # $ rudy -vv testsuite
10
+ # $ rudy -vv shutdown
11
+ #
12
+ # Requires: Rudy 0.9 (http://code.google.com/p/rudy/)
13
+ #
14
+
15
+ defaults do
16
+ color true
17
+ environment :test
18
+ role :netssh
19
+ end
20
+
21
+ machines do
22
+ region :'us-east-1' do
23
+ ami 'ami-e348af8a' # Alestic Debian 5.0, 32-bit (US)
24
+ end
25
+ env :test do
26
+ role :netssh do
27
+ user :root
28
+ end
29
+ end
30
+ end
31
+
32
+ commands do
33
+ allow :apt_get, "apt-get", :y, :q
34
+ allow :gem_install, "/usr/bin/gem", "install", :n, '/usr/bin', :y, :V, "--no-rdoc", "--no-ri"
35
+ allow :gem_sources, "/usr/bin/gem", "sources"
36
+ allow :gem_uninstall, "/usr/bin/gem", "uninstall", :V
37
+ allow :update_rubygems
38
+ allow :rm
39
+ end
40
+
41
+ routines do
42
+
43
+ testsuite do
44
+ before :sysupdate, :installdeps, :install_gem
45
+
46
+ remote :root do
47
+ directory_upload 'test', '/tmp/'
48
+ cd '/tmp'
49
+ ruby :I, 'lib/', :I, 'test/', :r, 'rubygems', 'test/test_all.rb'
50
+ end
51
+
52
+ after :install_rubyforge, :install_github
53
+ end
54
+
55
+ install_gem do
56
+ before :package_gem
57
+ remote :root do
58
+ disable_safe_mode
59
+ file_upload "pkg/net-ssh-*.gem", "/tmp/"
60
+ gem_install "/tmp/net-ssh-*.gem"
61
+ end
62
+ end
63
+
64
+ package_gem do
65
+ local do
66
+ rm :r, :f, 'pkg'
67
+ rake 'package'
68
+ end
69
+ end
70
+
71
+ remove do
72
+ remote :root do
73
+ gem_uninstall 'net-ssh'
74
+ end
75
+ end
76
+
77
+ installdeps do
78
+ remote :root do
79
+ gem_install "rye", "test-unit", "mocha"
80
+ rye 'authorize-local'
81
+ end
82
+ end
83
+
84
+ sysupdate do
85
+ remote :root do
86
+ apt_get "update"
87
+ apt_get "install", "build-essential", "git-core"
88
+ apt_get "install", "ruby1.8-dev", "rdoc", "libzlib-ruby", "rubygems"
89
+ mkdir :p, "/var/lib/gems/1.8/bin" # Doesn't get created, but causes Rubygems to fail
90
+ gem_install "builder", "session"
91
+ gem_install 'rubygems-update', "-v=1.3.4" # circular issue with 1.3.5 and hoe
92
+ update_rubygems
93
+ end
94
+ end
95
+ end
96
+
data/THANKS.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ Net::SSH was originally written by Jamis Buck <jamis@37signals.com>. In
2
+ addition, the following individuals are gratefully acknowledged for their
3
+ contributions:
4
+
5
+ GOTOU Yuuzou <gotoyuzo@notwork.org>
6
+ * help and code related to OpenSSL
7
+
8
+ Guillaume Marçais <guillaume.marcais@free.fr>
9
+ * support for communicating with the the PuTTY "pageant" process
10
+
11
+ Daniel Berger <djberg96@yahoo.com>
12
+ * help getting unit tests in earlier Net::SSH versions to pass in Windows
13
+ * initial version of Net::SSH::Config provided inspiration and encouragement
14
+
15
+ Chris Andrews <chris@nodnol.org> and Lee Jensen <lee@outerim.com>
16
+ * support for ssh agent forwarding
17
+
18
+ Hiroshi Nakamura
19
+ * fixed errors with JRuby tests
data/lib/net/ssh.rb ADDED
@@ -0,0 +1,223 @@
1
+ # Make sure HOME is set, regardless of OS, so that File.expand_path works
2
+ # as expected with tilde characters.
3
+ ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : "."
4
+
5
+ require 'logger'
6
+
7
+ require 'net/ssh/config'
8
+ require 'net/ssh/errors'
9
+ require 'net/ssh/loggable'
10
+ require 'net/ssh/transport/session'
11
+ require 'net/ssh/authentication/session'
12
+ require 'net/ssh/connection/session'
13
+
14
+ module Net
15
+
16
+ # Net::SSH is a library for interacting, programmatically, with remote
17
+ # processes via the SSH2 protocol. Sessions are always initiated via
18
+ # Net::SSH.start. From there, a program interacts with the new SSH session
19
+ # via the convenience methods on Net::SSH::Connection::Session, by opening
20
+ # and interacting with new channels (Net::SSH::Connection:Session#open_channel
21
+ # and Net::SSH::Connection::Channel), or by forwarding local and/or
22
+ # remote ports through the connection (Net::SSH::Service::Forward).
23
+ #
24
+ # The SSH protocol is very event-oriented. Requests are sent from the client
25
+ # to the server, and are answered asynchronously. This gives great flexibility
26
+ # (since clients can have multiple requests pending at a time), but it also
27
+ # adds complexity. Net::SSH tries to manage this complexity by providing
28
+ # some simpler methods of synchronous communication (see Net::SSH::Connection::Session#exec!).
29
+ #
30
+ # In general, though, and if you want to do anything more complicated than
31
+ # simply executing commands and capturing their output, you'll need to use
32
+ # channels (Net::SSH::Connection::Channel) to build state machines that are
33
+ # executed while the event loop runs (Net::SSH::Connection::Session#loop).
34
+ #
35
+ # Net::SSH::Connection::Session and Net::SSH::Connection::Channel have more
36
+ # information about this technique.
37
+ #
38
+ # = "Um, all I want to do is X, just show me how!"
39
+ #
40
+ # == X == "execute a command and capture the output"
41
+ #
42
+ # Net::SSH.start("host", "user", :password => "password") do |ssh|
43
+ # result = ssh.exec!("ls -l")
44
+ # puts result
45
+ # end
46
+ #
47
+ # == X == "forward connections on a local port to a remote host"
48
+ #
49
+ # Net::SSH.start("host", "user", :password => "password") do |ssh|
50
+ # ssh.forward.local(1234, "www.google.com", 80)
51
+ # ssh.loop { true }
52
+ # end
53
+ #
54
+ # == X == "forward connections on a remote port to the local host"
55
+ #
56
+ # Net::SSH.start("host", "user", :password => "password") do |ssh|
57
+ # ssh.forward.remote(80, "www.google.com", 1234)
58
+ # ssh.loop { true }
59
+ # end
60
+ module SSH
61
+ # This is the set of options that Net::SSH.start recognizes. See
62
+ # Net::SSH.start for a description of each option.
63
+ VALID_OPTIONS = [
64
+ :auth_methods, :bind_address, :compression, :compression_level, :config,
65
+ :encryption, :forward_agent, :hmac, :host_key, :kex, :keys, :key_data,
66
+ :languages, :logger, :paranoid, :password, :port, :proxy,
67
+ :rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
68
+ :global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
69
+ :host_name, :user, :properties, :passphrase, :keys_only
70
+ ]
71
+
72
+ # The standard means of starting a new SSH connection. When used with a
73
+ # block, the connection will be closed when the block terminates, otherwise
74
+ # the connection will just be returned. The yielded (or returned) value
75
+ # will be an instance of Net::SSH::Connection::Session (q.v.). (See also
76
+ # Net::SSH::Connection::Channel and Net::SSH::Service::Forward.)
77
+ #
78
+ # Net::SSH.start("host", "user") do |ssh|
79
+ # ssh.exec! "cp /some/file /another/location"
80
+ # hostname = ssh.exec!("hostname")
81
+ #
82
+ # ssh.open_channel do |ch|
83
+ # ch.exec "sudo -p 'sudo password: ' ls" do |ch, success|
84
+ # abort "could not execute sudo ls" unless success
85
+ #
86
+ # ch.on_data do |ch, data|
87
+ # print data
88
+ # if data =~ /sudo password: /
89
+ # ch.send_data("password\n")
90
+ # end
91
+ # end
92
+ # end
93
+ # end
94
+ #
95
+ # ssh.loop
96
+ # end
97
+ #
98
+ # This method accepts the following options (all are optional):
99
+ #
100
+ # * :auth_methods => an array of authentication methods to try
101
+ # * :bind_address => the IP address on the connecting machine to use in
102
+ # establishing connection. (:bind_address is discarded if :proxy
103
+ # is set.)
104
+ # * :compression => the compression algorithm to use, or +true+ to use
105
+ # whatever is supported.
106
+ # * :compression_level => the compression level to use when sending data
107
+ # * :config => set to +true+ to load the default OpenSSH config files
108
+ # (~/.ssh/config, /etc/ssh_config), or to +false+ to not load them, or to
109
+ # a file-name (or array of file-names) to load those specific configuration
110
+ # files. Defaults to +true+.
111
+ # * :encryption => the encryption cipher (or ciphers) to use
112
+ # * :forward_agent => set to true if you want the SSH agent connection to
113
+ # be forwarded
114
+ # * :global_known_hosts_file => the location of the global known hosts
115
+ # file. Set to an array if you want to specify multiple global known
116
+ # hosts files. Defaults to %w(/etc/ssh/known_hosts /etc/ssh/known_hosts2).
117
+ # * :hmac => the hmac algorithm (or algorithms) to use
118
+ # * :host_key => the host key algorithm (or algorithms) to use
119
+ # * :host_key_alias => the host name to use when looking up or adding a
120
+ # host to a known_hosts dictionary file
121
+ # * :host_name => the real host name or IP to log into. This is used
122
+ # instead of the +host+ parameter, and is primarily only useful when
123
+ # specified in an SSH configuration file. It lets you specify an
124
+ # "alias", similarly to adding an entry in /etc/hosts but without needing
125
+ # to modify /etc/hosts.
126
+ # * :kex => the key exchange algorithm (or algorithms) to use
127
+ # * :keys => an array of file names of private keys to use for publickey
128
+ # and hostbased authentication
129
+ # * :key_data => an array of strings, with each element of the array being
130
+ # a raw private key in PEM format.
131
+ # * :keys_only => set to +true+ to use only private keys from +keys+ and
132
+ # +key_data+ parameters, even if ssh-agent offers more identities. This
133
+ # option is intended for situations where ssh-agent offers many different
134
+ # identites.
135
+ # * :logger => the logger instance to use when logging
136
+ # * :paranoid => either true, false, or :very, specifying how strict
137
+ # host-key verification should be
138
+ # * :passphrase => the passphrase to use when loading a private key (default
139
+ # is +nil+, for no passphrase)
140
+ # * :password => the password to use to login
141
+ # * :port => the port to use when connecting to the remote host
142
+ # * :properties => a hash of key/value pairs to add to the new connection's
143
+ # properties (see Net::SSH::Connection::Session#properties)
144
+ # * :proxy => a proxy instance (see Proxy) to use when connecting
145
+ # * :rekey_blocks_limit => the max number of blocks to process before rekeying
146
+ # * :rekey_limit => the max number of bytes to process before rekeying
147
+ # * :rekey_packet_limit => the max number of packets to process before rekeying
148
+ # * :timeout => how long to wait for the initial connection to be made
149
+ # * :user => the user name to log in as; this overrides the +user+
150
+ # parameter, and is primarily only useful when provided via an SSH
151
+ # configuration file.
152
+ # * :user_known_hosts_file => the location of the user known hosts file.
153
+ # Set to an array to specify multiple user known hosts files.
154
+ # Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2).
155
+ # * :verbose => how verbose to be (Logger verbosity constants, Logger::DEBUG
156
+ # is very verbose, Logger::FATAL is all but silent). Logger::FATAL is the
157
+ # default. The symbols :debug, :info, :warn, :error, and :fatal are also
158
+ # supported and are translated to the corresponding Logger constant.
159
+ def self.start(host, user, options={}, &block)
160
+ invalid_options = options.keys - VALID_OPTIONS
161
+ if invalid_options.any?
162
+ raise ArgumentError, "invalid option(s): #{invalid_options.join(', ')}"
163
+ end
164
+
165
+ options[:user] = user if user
166
+ options = configuration_for(host, options.fetch(:config, true)).merge(options)
167
+ host = options.fetch(:host_name, host)
168
+
169
+ if !options.key?(:logger)
170
+ options[:logger] = Logger.new(STDERR)
171
+ options[:logger].level = Logger::FATAL
172
+ end
173
+
174
+ if options[:verbose]
175
+ options[:logger].level = case options[:verbose]
176
+ when Fixnum then options[:verbose]
177
+ when :debug then Logger::DEBUG
178
+ when :info then Logger::INFO
179
+ when :warn then Logger::WARN
180
+ when :error then Logger::ERROR
181
+ when :fatal then Logger::FATAL
182
+ else raise ArgumentError, "can't convert #{options[:verbose].inspect} to any of the Logger level constants"
183
+ end
184
+ end
185
+
186
+ transport = Transport::Session.new(host, options)
187
+ auth = Authentication::Session.new(transport, options)
188
+
189
+ user = options.fetch(:user, user)
190
+ if auth.authenticate("ssh-connection", user, options[:password])
191
+ connection = Connection::Session.new(transport, options)
192
+ if block_given?
193
+ yield connection
194
+ connection.close
195
+ else
196
+ return connection
197
+ end
198
+ else
199
+ transport.close
200
+ raise AuthenticationFailed, user
201
+ end
202
+ end
203
+
204
+ # Returns a hash of the configuration options for the given host, as read
205
+ # from the SSH configuration file(s). If +use_ssh_config+ is true (the
206
+ # default), this will load configuration from both ~/.ssh/config and
207
+ # /etc/ssh_config. If +use_ssh_config+ is nil or false, nothing will be
208
+ # loaded (and an empty hash returned). Otherwise, +use_ssh_config+ may
209
+ # be a file name (or array of file names) of SSH configuration file(s)
210
+ # to read.
211
+ #
212
+ # See Net::SSH::Config for the full description of all supported options.
213
+ def self.configuration_for(host, use_ssh_config=true)
214
+ files = case use_ssh_config
215
+ when true then Net::SSH::Config.default_files
216
+ when false, nil then return {}
217
+ else Array(use_ssh_config)
218
+ end
219
+
220
+ Net::SSH::Config.for(host, files)
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,179 @@
1
+ require 'net/ssh/buffer'
2
+ require 'net/ssh/errors'
3
+ require 'net/ssh/loggable'
4
+ require 'net/ssh/transport/server_version'
5
+
6
+ # Only load pageant on Windows
7
+ if File::ALT_SEPARATOR && !(RUBY_PLATFORM =~ /java/)
8
+ require 'net/ssh/authentication/pageant'
9
+ end
10
+
11
+ module Net; module SSH; module Authentication
12
+
13
+ # A trivial exception class for representing agent-specific errors.
14
+ class AgentError < Net::SSH::Exception; end
15
+
16
+ # An exception for indicating that the SSH agent is not available.
17
+ class AgentNotAvailable < AgentError; end
18
+
19
+ # This class implements a simple client for the ssh-agent protocol. It
20
+ # does not implement any specific protocol, but instead copies the
21
+ # behavior of the ssh-agent functions in the OpenSSH library (3.8).
22
+ #
23
+ # This means that although it behaves like a SSH1 client, it also has
24
+ # some SSH2 functionality (like signing data).
25
+ class Agent
26
+ include Loggable
27
+
28
+ # A simple module for extending keys, to allow comments to be specified
29
+ # for them.
30
+ module Comment
31
+ attr_accessor :comment
32
+ end
33
+
34
+ SSH2_AGENT_REQUEST_VERSION = 1
35
+ SSH2_AGENT_REQUEST_IDENTITIES = 11
36
+ SSH2_AGENT_IDENTITIES_ANSWER = 12
37
+ SSH2_AGENT_SIGN_REQUEST = 13
38
+ SSH2_AGENT_SIGN_RESPONSE = 14
39
+ SSH2_AGENT_FAILURE = 30
40
+ SSH2_AGENT_VERSION_RESPONSE = 103
41
+
42
+ SSH_COM_AGENT2_FAILURE = 102
43
+
44
+ SSH_AGENT_REQUEST_RSA_IDENTITIES = 1
45
+ SSH_AGENT_RSA_IDENTITIES_ANSWER1 = 2
46
+ SSH_AGENT_RSA_IDENTITIES_ANSWER2 = 5
47
+ SSH_AGENT_FAILURE = 5
48
+
49
+ # The underlying socket being used to communicate with the SSH agent.
50
+ attr_reader :socket
51
+
52
+ # Instantiates a new agent object, connects to a running SSH agent,
53
+ # negotiates the agent protocol version, and returns the agent object.
54
+ def self.connect(logger=nil)
55
+ agent = new(logger)
56
+ agent.connect!
57
+ agent.negotiate!
58
+ agent
59
+ end
60
+
61
+ # Creates a new Agent object, using the optional logger instance to
62
+ # report status.
63
+ def initialize(logger=nil)
64
+ self.logger = logger
65
+ end
66
+
67
+ # Connect to the agent process using the socket factory and socket name
68
+ # given by the attribute writers. If the agent on the other end of the
69
+ # socket reports that it is an SSH2-compatible agent, this will fail
70
+ # (it only supports the ssh-agent distributed by OpenSSH).
71
+ def connect!
72
+ begin
73
+ debug { "connecting to ssh-agent" }
74
+ @socket = agent_socket_factory.open(ENV['SSH_AUTH_SOCK'])
75
+ rescue
76
+ error { "could not connect to ssh-agent" }
77
+ raise AgentNotAvailable, $!.message
78
+ end
79
+ end
80
+
81
+ # Attempts to negotiate the SSH agent protocol version. Raises an error
82
+ # if the version could not be negotiated successfully.
83
+ def negotiate!
84
+ # determine what type of agent we're communicating with
85
+ type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
86
+
87
+ if type == SSH2_AGENT_VERSION_RESPONSE
88
+ raise NotImplementedError, "SSH2 agents are not yet supported"
89
+ elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
90
+ raise AgentError, "unknown response from agent: #{type}, #{body.to_s.inspect}"
91
+ end
92
+ end
93
+
94
+ # Return an array of all identities (public keys) known to the agent.
95
+ # Each key returned is augmented with a +comment+ property which is set
96
+ # to the comment returned by the agent for that key.
97
+ def identities
98
+ type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES)
99
+ raise AgentError, "could not get identity count" if agent_failed(type)
100
+ raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER
101
+
102
+ identities = []
103
+ body.read_long.times do
104
+ key = Buffer.new(body.read_string).read_key
105
+ key.extend(Comment)
106
+ key.comment = body.read_string
107
+ identities.push key
108
+ end
109
+
110
+ return identities
111
+ end
112
+
113
+ # Closes this socket. This agent reference is no longer able to
114
+ # query the agent.
115
+ def close
116
+ @socket.close
117
+ end
118
+
119
+ # Using the agent and the given public key, sign the given data. The
120
+ # signature is returned in SSH2 format.
121
+ def sign(key, data)
122
+ type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, 0)
123
+
124
+ if agent_failed(type)
125
+ raise AgentError, "agent could not sign data with requested identity"
126
+ elsif type != SSH2_AGENT_SIGN_RESPONSE
127
+ raise AgentError, "bad authentication response #{type}"
128
+ end
129
+
130
+ return reply.read_string
131
+ end
132
+
133
+ private
134
+
135
+ # Returns the agent socket factory to use.
136
+ def agent_socket_factory
137
+ if File::ALT_SEPARATOR
138
+ Pageant::socket_factory
139
+ else
140
+ UNIXSocket
141
+ end
142
+ end
143
+
144
+ # Send a new packet of the given type, with the associated data.
145
+ def send_packet(type, *args)
146
+ buffer = Buffer.from(*args)
147
+ data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*")
148
+ debug { "sending agent request #{type} len #{buffer.length}" }
149
+ @socket.send data, 0
150
+ end
151
+
152
+ # Read the next packet from the agent. This will return a two-part
153
+ # tuple consisting of the packet type, and the packet's body (which
154
+ # is returned as a Net::SSH::Buffer).
155
+ def read_packet
156
+ buffer = Net::SSH::Buffer.new(@socket.read(4))
157
+ buffer.append(@socket.read(buffer.read_long))
158
+ type = buffer.read_byte
159
+ debug { "received agent packet #{type} len #{buffer.length-4}" }
160
+ return type, buffer
161
+ end
162
+
163
+ # Send the given packet and return the subsequent reply from the agent.
164
+ # (See #send_packet and #read_packet).
165
+ def send_and_wait(type, *args)
166
+ send_packet(type, *args)
167
+ read_packet
168
+ end
169
+
170
+ # Returns +true+ if the parameter indicates a "failure" response from
171
+ # the agent, and +false+ otherwise.
172
+ def agent_failed(type)
173
+ type == SSH_AGENT_FAILURE ||
174
+ type == SSH2_AGENT_FAILURE ||
175
+ type == SSH_COM_AGENT2_FAILURE
176
+ end
177
+ end
178
+
179
+ end; end; end