net-ssh 0.5.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.
- data/doc/LICENSE-BSD +27 -0
- data/doc/LICENSE-GPL +280 -0
- data/doc/LICENSE-RUBY +56 -0
- data/doc/README +13 -0
- data/doc/manual-html/chapter-1.html +333 -0
- data/doc/manual-html/chapter-2.html +455 -0
- data/doc/manual-html/chapter-3.html +413 -0
- data/doc/manual-html/chapter-4.html +353 -0
- data/doc/manual-html/chapter-5.html +393 -0
- data/doc/manual-html/chapter-6.html +296 -0
- data/doc/manual-html/index.html +217 -0
- data/doc/manual-html/manual.css +192 -0
- data/doc/manual/chapter.erb +18 -0
- data/doc/manual/example.erb +18 -0
- data/doc/manual/index.erb +29 -0
- data/doc/manual/manual.css +192 -0
- data/doc/manual/manual.rb +240 -0
- data/doc/manual/manual.yml +67 -0
- data/doc/manual/page.erb +87 -0
- data/doc/manual/parts/channels_callbacks.txt +32 -0
- data/doc/manual/parts/channels_loop.txt +14 -0
- data/doc/manual/parts/channels_open.txt +20 -0
- data/doc/manual/parts/channels_operations.txt +15 -0
- data/doc/manual/parts/channels_types.txt +3 -0
- data/doc/manual/parts/channels_what_are.txt +7 -0
- data/doc/manual/parts/exec_channels.txt +28 -0
- data/doc/manual/parts/exec_open.txt +51 -0
- data/doc/manual/parts/exec_popen3.txt +35 -0
- data/doc/manual/parts/forward_direct.txt +37 -0
- data/doc/manual/parts/forward_handlers.txt +16 -0
- data/doc/manual/parts/forward_intro.txt +18 -0
- data/doc/manual/parts/forward_local.txt +18 -0
- data/doc/manual/parts/forward_remote.txt +14 -0
- data/doc/manual/parts/intro_author.txt +1 -0
- data/doc/manual/parts/intro_getting.txt +39 -0
- data/doc/manual/parts/intro_license.txt +6 -0
- data/doc/manual/parts/intro_support.txt +7 -0
- data/doc/manual/parts/intro_what_is.txt +7 -0
- data/doc/manual/parts/intro_what_is_not.txt +3 -0
- data/doc/manual/parts/proxy_http.txt +52 -0
- data/doc/manual/parts/proxy_intro.txt +1 -0
- data/doc/manual/parts/proxy_socks.txt +23 -0
- data/doc/manual/parts/session_key.txt +66 -0
- data/doc/manual/parts/session_options.txt +42 -0
- data/doc/manual/parts/session_session.txt +14 -0
- data/doc/manual/parts/session_start.txt +49 -0
- data/doc/manual/tutorial.erb +30 -0
- data/examples/channel-demo.rb +81 -0
- data/examples/port-forward.rb +51 -0
- data/examples/process-demo.rb +91 -0
- data/examples/remote-net-port-forward.rb +45 -0
- data/examples/remote-port-forward.rb +80 -0
- data/examples/tail-demo.rb +49 -0
- data/lib/net/ssh.rb +52 -0
- data/lib/net/ssh/connection/channel.rb +411 -0
- data/lib/net/ssh/connection/constants.rb +47 -0
- data/lib/net/ssh/connection/driver.rb +343 -0
- data/lib/net/ssh/connection/services.rb +72 -0
- data/lib/net/ssh/connection/term.rb +90 -0
- data/lib/net/ssh/errors.rb +27 -0
- data/lib/net/ssh/proxy/errors.rb +34 -0
- data/lib/net/ssh/proxy/http.rb +126 -0
- data/lib/net/ssh/proxy/socks4.rb +83 -0
- data/lib/net/ssh/proxy/socks5.rb +160 -0
- data/lib/net/ssh/service/forward/driver.rb +319 -0
- data/lib/net/ssh/service/forward/local-network-handler.rb +74 -0
- data/lib/net/ssh/service/forward/remote-network-handler.rb +81 -0
- data/lib/net/ssh/service/forward/services.rb +76 -0
- data/lib/net/ssh/service/process/driver.rb +153 -0
- data/lib/net/ssh/service/process/open.rb +193 -0
- data/lib/net/ssh/service/process/popen3.rb +160 -0
- data/lib/net/ssh/service/process/services.rb +66 -0
- data/lib/net/ssh/service/services.rb +44 -0
- data/lib/net/ssh/session.rb +242 -0
- data/lib/net/ssh/transport/algorithm-negotiator.rb +267 -0
- data/lib/net/ssh/transport/compress/compressor.rb +53 -0
- data/lib/net/ssh/transport/compress/decompressor.rb +53 -0
- data/lib/net/ssh/transport/compress/none-compressor.rb +39 -0
- data/lib/net/ssh/transport/compress/none-decompressor.rb +39 -0
- data/lib/net/ssh/transport/compress/services.rb +68 -0
- data/lib/net/ssh/transport/compress/zlib-compressor.rb +60 -0
- data/lib/net/ssh/transport/compress/zlib-decompressor.rb +52 -0
- data/lib/net/ssh/transport/constants.rb +66 -0
- data/lib/net/ssh/transport/errors.rb +47 -0
- data/lib/net/ssh/transport/identity-cipher.rb +61 -0
- data/lib/net/ssh/transport/kex/dh-gex.rb +106 -0
- data/lib/net/ssh/transport/kex/dh.rb +231 -0
- data/lib/net/ssh/transport/kex/services.rb +60 -0
- data/lib/net/ssh/transport/ossl/buffer-factory.rb +52 -0
- data/lib/net/ssh/transport/ossl/buffer.rb +87 -0
- data/lib/net/ssh/transport/ossl/cipher-factory.rb +98 -0
- data/lib/net/ssh/transport/ossl/digest-factory.rb +51 -0
- data/lib/net/ssh/transport/ossl/hmac-factory.rb +71 -0
- data/lib/net/ssh/transport/ossl/hmac/hmac.rb +62 -0
- data/lib/net/ssh/transport/ossl/hmac/md5-96.rb +44 -0
- data/lib/net/ssh/transport/ossl/hmac/md5.rb +46 -0
- data/lib/net/ssh/transport/ossl/hmac/none.rb +46 -0
- data/lib/net/ssh/transport/ossl/hmac/services.rb +68 -0
- data/lib/net/ssh/transport/ossl/hmac/sha1-96.rb +44 -0
- data/lib/net/ssh/transport/ossl/hmac/sha1.rb +45 -0
- data/lib/net/ssh/transport/ossl/key-factory.rb +113 -0
- data/lib/net/ssh/transport/ossl/services.rb +149 -0
- data/lib/net/ssh/transport/packet-stream.rb +210 -0
- data/lib/net/ssh/transport/services.rb +146 -0
- data/lib/net/ssh/transport/session.rb +296 -0
- data/lib/net/ssh/transport/version-negotiator.rb +73 -0
- data/lib/net/ssh/userauth/agent.rb +218 -0
- data/lib/net/ssh/userauth/constants.rb +35 -0
- data/lib/net/ssh/userauth/driver.rb +176 -0
- data/lib/net/ssh/userauth/methods/hostbased.rb +119 -0
- data/lib/net/ssh/userauth/methods/password.rb +70 -0
- data/lib/net/ssh/userauth/methods/publickey.rb +137 -0
- data/lib/net/ssh/userauth/methods/services.rb +63 -0
- data/lib/net/ssh/userauth/services.rb +126 -0
- data/lib/net/ssh/userauth/userkeys.rb +258 -0
- data/lib/net/ssh/util/buffer.rb +274 -0
- data/lib/net/ssh/util/openssl.rb +146 -0
- data/lib/net/ssh/util/prompter.rb +73 -0
- data/lib/net/ssh/version.rb +29 -0
- data/test/ALL-TESTS.rb +21 -0
- data/test/connection/tc_channel.rb +136 -0
- data/test/connection/tc_driver.rb +287 -0
- data/test/connection/tc_integration.rb +85 -0
- data/test/proxy/tc_http.rb +209 -0
- data/test/proxy/tc_socks4.rb +148 -0
- data/test/proxy/tc_socks5.rb +214 -0
- data/test/service/forward/tc_driver.rb +289 -0
- data/test/service/forward/tc_local_network_handler.rb +123 -0
- data/test/service/forward/tc_remote_network_handler.rb +108 -0
- data/test/service/process/tc_driver.rb +79 -0
- data/test/service/process/tc_integration.rb +117 -0
- data/test/service/process/tc_open.rb +179 -0
- data/test/service/process/tc_popen3.rb +164 -0
- data/test/tc_integration.rb +79 -0
- data/test/transport/compress/tc_none_compress.rb +41 -0
- data/test/transport/compress/tc_none_decompress.rb +45 -0
- data/test/transport/compress/tc_zlib_compress.rb +61 -0
- data/test/transport/compress/tc_zlib_decompress.rb +48 -0
- data/test/transport/kex/tc_dh.rb +304 -0
- data/test/transport/kex/tc_dh_gex.rb +70 -0
- data/test/transport/ossl/fixtures/dsa-encrypted +15 -0
- data/test/transport/ossl/fixtures/dsa-encrypted-bad +15 -0
- data/test/transport/ossl/fixtures/dsa-unencrypted +12 -0
- data/test/transport/ossl/fixtures/dsa-unencrypted-bad +12 -0
- data/test/transport/ossl/fixtures/dsa-unencrypted.pub +1 -0
- data/test/transport/ossl/fixtures/not-a-private-key +4 -0
- data/test/transport/ossl/fixtures/not-supported +2 -0
- data/test/transport/ossl/fixtures/rsa-encrypted +18 -0
- data/test/transport/ossl/fixtures/rsa-encrypted-bad +18 -0
- data/test/transport/ossl/fixtures/rsa-unencrypted +15 -0
- data/test/transport/ossl/fixtures/rsa-unencrypted-bad +15 -0
- data/test/transport/ossl/fixtures/rsa-unencrypted.pub +1 -0
- data/test/transport/ossl/hmac/tc_hmac.rb +58 -0
- data/test/transport/ossl/hmac/tc_md5.rb +50 -0
- data/test/transport/ossl/hmac/tc_md5_96.rb +50 -0
- data/test/transport/ossl/hmac/tc_none.rb +50 -0
- data/test/transport/ossl/hmac/tc_sha1.rb +50 -0
- data/test/transport/ossl/hmac/tc_sha1_96.rb +50 -0
- data/test/transport/ossl/tc_buffer.rb +97 -0
- data/test/transport/ossl/tc_buffer_factory.rb +67 -0
- data/test/transport/ossl/tc_cipher_factory.rb +84 -0
- data/test/transport/ossl/tc_digest_factory.rb +39 -0
- data/test/transport/ossl/tc_hmac_factory.rb +72 -0
- data/test/transport/ossl/tc_key_factory.rb +199 -0
- data/test/transport/tc_algorithm_negotiator.rb +169 -0
- data/test/transport/tc_identity_cipher.rb +52 -0
- data/test/transport/tc_integration.rb +110 -0
- data/test/transport/tc_packet_stream.rb +183 -0
- data/test/transport/tc_session.rb +283 -0
- data/test/transport/tc_version_negotiator.rb +86 -0
- data/test/userauth/methods/tc_hostbased.rb +136 -0
- data/test/userauth/methods/tc_password.rb +89 -0
- data/test/userauth/methods/tc_publickey.rb +167 -0
- data/test/userauth/tc_agent.rb +223 -0
- data/test/userauth/tc_driver.rb +190 -0
- data/test/userauth/tc_integration.rb +81 -0
- data/test/userauth/tc_userkeys.rb +265 -0
- data/test/util/tc_buffer.rb +217 -0
- metadata +256 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This source file is distributed as part of the Net::SSH Secure Shell Client
|
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
|
|
10
|
+
# distribution for the texts of these licenses.
|
|
11
|
+
# -----------------------------------------------------------------------------
|
|
12
|
+
# net-ssh website : http://net-ssh.rubyforge.org
|
|
13
|
+
# project website: http://rubyforge.org/projects/net-ssh
|
|
14
|
+
# =============================================================================
|
|
15
|
+
#++
|
|
16
|
+
|
|
17
|
+
module Net
|
|
18
|
+
module SSH
|
|
19
|
+
module Service
|
|
20
|
+
module Process
|
|
21
|
+
|
|
22
|
+
# Register all services pertaining to the management of remote
|
|
23
|
+
# processes.
|
|
24
|
+
def register_services( container )
|
|
25
|
+
|
|
26
|
+
# All process management services are registered in their own
|
|
27
|
+
# namespace.
|
|
28
|
+
container.namespace_define :process do |ns|
|
|
29
|
+
|
|
30
|
+
# The :open_manager service returns a proc object that can be used
|
|
31
|
+
# to create new OpenManager instances for a given command.
|
|
32
|
+
ns.open_manager do |c,p|
|
|
33
|
+
require 'net/ssh/service/process/open'
|
|
34
|
+
connection = c[:connection][:driver]
|
|
35
|
+
log = c[:log_for, p]
|
|
36
|
+
lambda { |cmd| OpenManager.new( connection, log, cmd ) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The :popen3_manager service returns a new POpen3Manager instance
|
|
40
|
+
# for managing the execution of commands with a popen3-type
|
|
41
|
+
# interface.
|
|
42
|
+
ns.popen3_manager do |c,p|
|
|
43
|
+
require 'net/ssh/service/process/popen3'
|
|
44
|
+
connection = c[:connection][:driver]
|
|
45
|
+
log = c[:log_for, p]
|
|
46
|
+
POpen3Manager.new( connection, log )
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The :driver controls access to all remote process management
|
|
50
|
+
# services.
|
|
51
|
+
ns.driver do |c,p|
|
|
52
|
+
require 'net/ssh/service/process/driver'
|
|
53
|
+
Driver.new( c[:connection][:driver],
|
|
54
|
+
c[:log_for, p],
|
|
55
|
+
:open => c[:open_manager],
|
|
56
|
+
:popen3 => c[:popen3_manager] )
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
module_function :register_services
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This source file is distributed as part of the Net::SSH Secure Shell Client
|
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
|
|
10
|
+
# distribution for the texts of these licenses.
|
|
11
|
+
# -----------------------------------------------------------------------------
|
|
12
|
+
# net-ssh website : http://net-ssh.rubyforge.org
|
|
13
|
+
# project website: http://rubyforge.org/projects/net-ssh
|
|
14
|
+
# =============================================================================
|
|
15
|
+
#++
|
|
16
|
+
|
|
17
|
+
module Net
|
|
18
|
+
module SSH
|
|
19
|
+
module Service
|
|
20
|
+
|
|
21
|
+
# Register all standard SSH services.
|
|
22
|
+
def register_services( container )
|
|
23
|
+
|
|
24
|
+
# Define the hash that will be used to record the registered services.
|
|
25
|
+
# If the hash already exists, don't redefine it.
|
|
26
|
+
unless container.knows_key?( :services )
|
|
27
|
+
container.define.services { Hash.new }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Register the services in their own namespace.
|
|
31
|
+
container.namespace_define :service do |ns|
|
|
32
|
+
ns.require "net/ssh/service/forward/services", "#{self}::Forward"
|
|
33
|
+
ns.require "net/ssh/service/process/services", "#{self}::Process"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Add the services to the services hash.
|
|
37
|
+
container.services[ :forward ] = container.service.forward.driver
|
|
38
|
+
container.services[ :process ] = container.service.process.driver
|
|
39
|
+
end
|
|
40
|
+
module_function :register_services
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This source file is distributed as part of the Net::SSH Secure Shell Client
|
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
|
|
10
|
+
# distribution for the texts of these licenses.
|
|
11
|
+
# -----------------------------------------------------------------------------
|
|
12
|
+
# net-ssh website : http://net-ssh.rubyforge.org
|
|
13
|
+
# project website: http://rubyforge.org/projects/net-ssh
|
|
14
|
+
# =============================================================================
|
|
15
|
+
#++
|
|
16
|
+
|
|
17
|
+
require 'needle'
|
|
18
|
+
require 'net/ssh/errors'
|
|
19
|
+
|
|
20
|
+
module Net
|
|
21
|
+
module SSH
|
|
22
|
+
|
|
23
|
+
# Encapsulates a single session (connection) to a server via SSH.
|
|
24
|
+
class Session
|
|
25
|
+
|
|
26
|
+
# The dependency-injection registry used by this session.
|
|
27
|
+
attr_reader :registry
|
|
28
|
+
|
|
29
|
+
# Create a new SSH session. This method polymorphically accepts a
|
|
30
|
+
# variable number of parameters, as follows:
|
|
31
|
+
#
|
|
32
|
+
# * 1 parameter: must be the hostname to connect to.
|
|
33
|
+
# * 2 parameters: must be the hostname, and either the port (as an
|
|
34
|
+
# integer) or the username to connect as.
|
|
35
|
+
# * 3 parameters: must be the hostname, and either the port (as an
|
|
36
|
+
# integer) and username, or the username and the password.
|
|
37
|
+
# * 4 parameters: must be the hostname, port, username, and password.
|
|
38
|
+
#
|
|
39
|
+
# Any scenario above that omits the username assumes that the USER
|
|
40
|
+
# environment variable is set to the user's name. Any scenario above that
|
|
41
|
+
# omits the password assumes that the user will log in without a password
|
|
42
|
+
# (ie, using a public key). Any scenario above that omits the port number
|
|
43
|
+
# assumes a port number of 22 (the default for SSH).
|
|
44
|
+
#
|
|
45
|
+
# Any of the above scenarios may also accept a Hash as the last
|
|
46
|
+
# parameter, specifying a list of additional options to be used to
|
|
47
|
+
# initialize the session. (See Net::SSH::Session.add_options).
|
|
48
|
+
#
|
|
49
|
+
# Alternatively, named parameters may be used, in which case the first
|
|
50
|
+
# parameter is positional and is always the host to connect to, following
|
|
51
|
+
# which you may specify any of the following named parameters (as
|
|
52
|
+
# symbols):
|
|
53
|
+
#
|
|
54
|
+
# * :port
|
|
55
|
+
# * :username
|
|
56
|
+
# * :password
|
|
57
|
+
#
|
|
58
|
+
# Any additional parameters are treated as options that configure how the
|
|
59
|
+
# connection behaves.
|
|
60
|
+
#
|
|
61
|
+
# Allowed options are:
|
|
62
|
+
#
|
|
63
|
+
# * :keys (the list of filenames identifying the user's keys)
|
|
64
|
+
# * :host_keys (the list of filenames identifying the host's keys)
|
|
65
|
+
# * :auth_methods (a list of authentication methods to use)
|
|
66
|
+
# * :crypto_backend (defaults to :ossl, and specifies the cryptography
|
|
67
|
+
# backend to use)
|
|
68
|
+
# * :registry_options (a hash of options to use when creating the
|
|
69
|
+
# registry)
|
|
70
|
+
# * :container (the registry to use. If not specified, a new registry
|
|
71
|
+
# will be created)
|
|
72
|
+
# * :verbose (how verbose the logging output should be. Defaults to
|
|
73
|
+
# :warn).
|
|
74
|
+
# * :log (the name of the file, or the IO object, to which messages will
|
|
75
|
+
# be logged. Defaults to STDERR.)
|
|
76
|
+
#
|
|
77
|
+
# Also, any options recognized by Net::SSH::Transport::Session may be
|
|
78
|
+
# given, and will be passed through to initialize the transport session.
|
|
79
|
+
#
|
|
80
|
+
# If a block is given to this method, then it is called with the new
|
|
81
|
+
# session object. The session object is then closed when the block
|
|
82
|
+
# terminates. If a block is not given, then the session object is
|
|
83
|
+
# returned (and must be closed explicitly).
|
|
84
|
+
def initialize( *args )
|
|
85
|
+
@open = false
|
|
86
|
+
process_arguments( *args )
|
|
87
|
+
|
|
88
|
+
@registry.define do |b|
|
|
89
|
+
b.crypto_backend { @crypto_backend }
|
|
90
|
+
b.transport_host { @host }
|
|
91
|
+
b.transport_options { @options }
|
|
92
|
+
|
|
93
|
+
b.userauth_keys { @keys }
|
|
94
|
+
b.userauth_host_keys { @host_keys }
|
|
95
|
+
b.userauth_method_order { @auth_methods }
|
|
96
|
+
|
|
97
|
+
b.prompter do
|
|
98
|
+
require 'net/ssh/util/prompter'
|
|
99
|
+
Net::SSH::Util::Prompter.new
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
b.require 'net/ssh/transport/services', "Net::SSH::Transport"
|
|
103
|
+
b.require 'net/ssh/connection/services', "Net::SSH::Connection"
|
|
104
|
+
b.require 'net/ssh/userauth/services', "Net::SSH::UserAuth"
|
|
105
|
+
|
|
106
|
+
b.require 'net/ssh/service/services', "Net::SSH::Service"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
userauth = @registry[:userauth][:driver]
|
|
110
|
+
if userauth.authenticate( "ssh-connection", @username, @password )
|
|
111
|
+
@open = true
|
|
112
|
+
@connection = @registry[:connection][:driver]
|
|
113
|
+
if block_given?
|
|
114
|
+
yield self
|
|
115
|
+
close
|
|
116
|
+
end
|
|
117
|
+
else
|
|
118
|
+
@registry[:transport][:session].close
|
|
119
|
+
raise AuthenticationFailed, @username
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Closes the session, if it is open. If it is not open, this does
|
|
124
|
+
# nothing.
|
|
125
|
+
def close
|
|
126
|
+
@registry[:transport][:session].close if @open
|
|
127
|
+
@open = false
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Returns +true+ if the session is currently open.
|
|
131
|
+
def open?
|
|
132
|
+
@open
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Opens a new communication channel over the current connection. This
|
|
136
|
+
# returns immediately. The block will be invoked when then the channel
|
|
137
|
+
# has been opened. (See Net::SSH::Connection::Driver#open_channel).
|
|
138
|
+
def open_channel( type="session", data=nil, &block )
|
|
139
|
+
sanity_check
|
|
140
|
+
channel = @connection.open_channel( type, data )
|
|
141
|
+
channel.on_confirm_open &block
|
|
142
|
+
channel
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Enters the main communication loop. This processes events occuring over
|
|
146
|
+
# the channel. If a block is given, the loop will continue for as long
|
|
147
|
+
# as the block returns +true+. Otherwise, the loop continues until there
|
|
148
|
+
# are no more open channels. (See Net::SSH::Connection::Driver#loop).
|
|
149
|
+
def loop( &block )
|
|
150
|
+
sanity_check
|
|
151
|
+
@connection.loop &block
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Provides convenient access to services that have been registered with
|
|
155
|
+
# the session, such as "process" and "forward".
|
|
156
|
+
#
|
|
157
|
+
# Usage:
|
|
158
|
+
#
|
|
159
|
+
# session.forward.local(...)
|
|
160
|
+
def method_missing( sym, *args, &block )
|
|
161
|
+
if args.empty? && block.nil? && @registry[:services].has_key?( sym )
|
|
162
|
+
return @registry[:services][ sym ]
|
|
163
|
+
else
|
|
164
|
+
super
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Processes the argument list, determining the meaning of each argument
|
|
169
|
+
# and allowing polymorphic argument lists. (See #initialize).
|
|
170
|
+
def process_arguments( *args )
|
|
171
|
+
@options = {}
|
|
172
|
+
@username = ENV['USER']
|
|
173
|
+
|
|
174
|
+
raise ArgumentError,
|
|
175
|
+
"you must specify the host to connect to" if args.length < 1
|
|
176
|
+
|
|
177
|
+
@host = args.shift
|
|
178
|
+
|
|
179
|
+
# support for both named arguments, and positional arguments...
|
|
180
|
+
if args.length == 1 && args[0].is_a?( Hash ) &&
|
|
181
|
+
( args[0][:username] || args[0][:password] ||
|
|
182
|
+
args[0][:port] || args[0][:options] )
|
|
183
|
+
# then
|
|
184
|
+
@username = args[0][:username] || username
|
|
185
|
+
@password = args[0][:password]
|
|
186
|
+
|
|
187
|
+
@options.update args.shift
|
|
188
|
+
else
|
|
189
|
+
@options[ :port ] = args.shift if args.first.is_a? Numeric
|
|
190
|
+
if args.first.nil? || args.first.is_a?( String )
|
|
191
|
+
@username = args.shift || @username
|
|
192
|
+
end
|
|
193
|
+
if args.first.nil? || args.first.is_a?( String )
|
|
194
|
+
@password = args.shift
|
|
195
|
+
end
|
|
196
|
+
@options.update args.shift if args.first.is_a?( Hash )
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
unless args.empty?
|
|
200
|
+
raise ArgumentError, "extra parameters detected: #{args.inspect}"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
@keys = @options[ :keys ]
|
|
204
|
+
@host_keys = @options[ :host_keys ]
|
|
205
|
+
@auth_methods = @options[ :auth_methods ]
|
|
206
|
+
@crypto_backend = @options.fetch( :crypto_backend, :ossl )
|
|
207
|
+
|
|
208
|
+
verbose = @options.fetch( :verbose, :warn )
|
|
209
|
+
log = @options.fetch( :log, STDERR )
|
|
210
|
+
|
|
211
|
+
@registry_options = @options.fetch( :registry_options, {} )
|
|
212
|
+
|
|
213
|
+
@registry_options[ :logs ] ||= {}
|
|
214
|
+
@registry_options[ :logs ][ :default_level ] = verbose
|
|
215
|
+
|
|
216
|
+
if log.is_a? IO
|
|
217
|
+
@registry_options[ :logs ][ :device ] ||= log
|
|
218
|
+
else
|
|
219
|
+
@registry_options[ :logs ][ :filename ] ||= log
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
@registry = @options[ :container ] ||
|
|
223
|
+
Needle::Registry.new( @registry_options )
|
|
224
|
+
|
|
225
|
+
[ :keys, :host_keys, :auth_methods, :username, :password,
|
|
226
|
+
:crypto_backend, :registry_options, :container, :log, :verbose
|
|
227
|
+
].each do |i|
|
|
228
|
+
@options.delete i
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
private :process_arguments
|
|
232
|
+
|
|
233
|
+
# Make sure we're in an acceptible state.
|
|
234
|
+
def sanity_check
|
|
235
|
+
raise Net::SSH::Exception, "session not open" unless @open
|
|
236
|
+
end
|
|
237
|
+
private :sanity_check
|
|
238
|
+
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
end
|
|
242
|
+
end
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This source file is distributed as part of the Net::SSH Secure Shell Client
|
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
|
|
10
|
+
# distribution for the texts of these licenses.
|
|
11
|
+
# -----------------------------------------------------------------------------
|
|
12
|
+
# net-ssh website : http://net-ssh.rubyforge.org
|
|
13
|
+
# project website: http://rubyforge.org/projects/net-ssh
|
|
14
|
+
# =============================================================================
|
|
15
|
+
#++
|
|
16
|
+
|
|
17
|
+
require 'net/ssh/errors'
|
|
18
|
+
require 'net/ssh/transport/constants'
|
|
19
|
+
|
|
20
|
+
module Net
|
|
21
|
+
module SSH
|
|
22
|
+
module Transport
|
|
23
|
+
|
|
24
|
+
# The AlgorithmNegotiator is used for negotiating the algorithms to be
|
|
25
|
+
# employed for a specific SSH session.
|
|
26
|
+
class AlgorithmNegotiator
|
|
27
|
+
include Constants
|
|
28
|
+
|
|
29
|
+
Algorithms = Struct.new( :server_packet,
|
|
30
|
+
:client_packet,
|
|
31
|
+
:kex,
|
|
32
|
+
:host_key,
|
|
33
|
+
:encryption_c2s,
|
|
34
|
+
:encryption_s2c,
|
|
35
|
+
:mac_c2s,
|
|
36
|
+
:mac_s2c,
|
|
37
|
+
:compression_c2s,
|
|
38
|
+
:compression_s2c,
|
|
39
|
+
:language_c2s,
|
|
40
|
+
:language_s2c,
|
|
41
|
+
:compression_level )
|
|
42
|
+
|
|
43
|
+
# Create a new AlgorithmNegotiator instance, using the given logger,
|
|
44
|
+
# set of default algorithms, and buffer factory.
|
|
45
|
+
def initialize( logger, algorithms, buffers )
|
|
46
|
+
@default_algorithms = algorithms
|
|
47
|
+
@buffers = buffers
|
|
48
|
+
@logger = logger
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Adds the algorithms of the specified type from +options+ to the
|
|
52
|
+
# @algorithms hash. Also verifies that the specified algorithms are
|
|
53
|
+
# supported.
|
|
54
|
+
def prepare_preferred_algorithm( options, algorithm )
|
|
55
|
+
@algorithms[ algorithm ] = @default_algorithms[ algorithm ].dup
|
|
56
|
+
if options[algorithm]
|
|
57
|
+
algos = [ *options[algorithm] ]
|
|
58
|
+
algos.each do |algo|
|
|
59
|
+
unless @algorithms[algorithm].include?(algo)
|
|
60
|
+
raise NotImplementedError,
|
|
61
|
+
"unsupported algorithm for #{algorithm.inspect}: #{algo}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
@algorithms[ algorithm ].unshift( *algos ).uniq!
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
private :prepare_preferred_algorithm
|
|
68
|
+
|
|
69
|
+
# Builds the @algorithms hash from the values specified in the
|
|
70
|
+
# +options+ hash.
|
|
71
|
+
def prepare_preferred_algorithms( options )
|
|
72
|
+
@algorithms = Hash.new
|
|
73
|
+
|
|
74
|
+
prepare_preferred_algorithm options, :host_key
|
|
75
|
+
prepare_preferred_algorithm options, :kex
|
|
76
|
+
prepare_preferred_algorithm options, :encryption
|
|
77
|
+
prepare_preferred_algorithm options, :hmac
|
|
78
|
+
prepare_preferred_algorithm options, :compression
|
|
79
|
+
prepare_preferred_algorithm options, :languages
|
|
80
|
+
|
|
81
|
+
@compression_level = options[ :compression_level ]
|
|
82
|
+
end
|
|
83
|
+
private :prepare_preferred_algorithms
|
|
84
|
+
|
|
85
|
+
# looks for the first element in list1 that is also in list2
|
|
86
|
+
def first_matching_element( list1, list2 )
|
|
87
|
+
list1 = list1.split( /,/ ) if list1.respond_to? :split
|
|
88
|
+
list2 = list2.split( /,/ ) if list2.respond_to? :split
|
|
89
|
+
|
|
90
|
+
list1.each do |item|
|
|
91
|
+
return item if list2.include? item
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
return nil
|
|
95
|
+
end
|
|
96
|
+
private :first_matching_element
|
|
97
|
+
|
|
98
|
+
# Negotiate the supported algorithms with the server. If a compromise
|
|
99
|
+
# cannot be reached between what the client wants and what the server
|
|
100
|
+
# can provide, this will fail.
|
|
101
|
+
def negotiate( session, options )
|
|
102
|
+
prepare_preferred_algorithms options
|
|
103
|
+
|
|
104
|
+
# first, discover what the server can do
|
|
105
|
+
type, buffer = session.wait_for_message
|
|
106
|
+
raise Net::SSH::Exception, "expected KEXINIT" unless type == KEXINIT
|
|
107
|
+
|
|
108
|
+
server_algorithm_packet = buffer.content
|
|
109
|
+
|
|
110
|
+
cookie = buffer.read( 16 )
|
|
111
|
+
kex_algorithms = buffer.read_string
|
|
112
|
+
server_host_key_algorithms = buffer.read_string
|
|
113
|
+
encryption_algorithms_client_to_server = buffer.read_string
|
|
114
|
+
encryption_algorithms_server_to_client = buffer.read_string
|
|
115
|
+
mac_algorithms_client_to_server = buffer.read_string
|
|
116
|
+
mac_algorithms_server_to_client = buffer.read_string
|
|
117
|
+
compression_algorithms_client_to_server = buffer.read_string
|
|
118
|
+
compression_algorithms_server_to_client = buffer.read_string
|
|
119
|
+
languages_client_to_server = buffer.read_string
|
|
120
|
+
languages_server_to_client = buffer.read_string
|
|
121
|
+
first_kex_packet_follows = buffer.read_bool
|
|
122
|
+
zero = buffer.read_long
|
|
123
|
+
|
|
124
|
+
# TODO: if first_kex_packet_follows, we need to try to skip the
|
|
125
|
+
# actual kexinit stuff and try to guess what the server is doing...
|
|
126
|
+
# need to read more about this scenario.
|
|
127
|
+
|
|
128
|
+
# next, tell the server what we can do
|
|
129
|
+
|
|
130
|
+
my_kex = @algorithms[ :kex ].join( "," )
|
|
131
|
+
my_server_host_key_algorithms = @algorithms[ :host_key ].join( "," )
|
|
132
|
+
my_encryption_algorithms = @algorithms[ :encryption ].join( "," )
|
|
133
|
+
my_mac_algorithms = @algorithms[ :hmac ].join( "," )
|
|
134
|
+
my_compression_algorithms = @algorithms[ :compression ].join( "," )
|
|
135
|
+
my_languages = @algorithms[ :languages ].join( "," )
|
|
136
|
+
|
|
137
|
+
msg = @buffers.writer
|
|
138
|
+
msg.write_byte KEXINIT
|
|
139
|
+
msg.write_long rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF),
|
|
140
|
+
rand(0xFFFFFFFF)
|
|
141
|
+
msg.write_string my_kex, my_server_host_key_algorithms
|
|
142
|
+
msg.write_string my_encryption_algorithms, my_encryption_algorithms
|
|
143
|
+
msg.write_string my_mac_algorithms, my_mac_algorithms
|
|
144
|
+
msg.write_string my_compression_algorithms, my_compression_algorithms
|
|
145
|
+
msg.write_string my_languages, my_languages
|
|
146
|
+
msg.write_bool false
|
|
147
|
+
msg.write_long 0
|
|
148
|
+
|
|
149
|
+
client_algorithm_packet = msg.to_s
|
|
150
|
+
session.send_message msg
|
|
151
|
+
|
|
152
|
+
# negotiate algorithms
|
|
153
|
+
|
|
154
|
+
kex_algorithm = first_matching_element( @algorithms[ :kex ],
|
|
155
|
+
kex_algorithms )
|
|
156
|
+
raise Net::SSH::Exception,
|
|
157
|
+
"could not settle on kex algorithm" unless kex_algorithm
|
|
158
|
+
@logger.debug "kex algorithm: #{kex_algorithm}" if @logger.debug?
|
|
159
|
+
|
|
160
|
+
host_key_algorithm = first_matching_element(
|
|
161
|
+
@algorithms[ :host_key ], server_host_key_algorithms )
|
|
162
|
+
raise Net::SSH::Exception,
|
|
163
|
+
"could not settle on host key algorithm" unless host_key_algorithm
|
|
164
|
+
if @logger.debug?
|
|
165
|
+
@logger.debug "host key algorithm: #{host_key_algorithm}"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
encryption_algorithm_c2s = first_matching_element(
|
|
169
|
+
@algorithms[ :encryption ], encryption_algorithms_client_to_server )
|
|
170
|
+
unless encryption_algorithm_c2s
|
|
171
|
+
raise Net::SSH::Exception,
|
|
172
|
+
"could not settle on client-to-server encryption algorithm"
|
|
173
|
+
end
|
|
174
|
+
if @logger.debug?
|
|
175
|
+
@logger.debug "encryption algorithm (client-to-server): " +
|
|
176
|
+
encryption_algorithm_c2s
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
encryption_algorithm_s2c = first_matching_element(
|
|
180
|
+
@algorithms[ :encryption ], encryption_algorithms_server_to_client )
|
|
181
|
+
unless encryption_algorithm_s2c
|
|
182
|
+
raise Net::SSH::Exception,
|
|
183
|
+
"could not settle on server-to-client encryption algorithm"
|
|
184
|
+
end
|
|
185
|
+
if @logger.debug?
|
|
186
|
+
@logger.debug "encryption algorithm (server-to-client): " +
|
|
187
|
+
encryption_algorithm_s2c
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
mac_algorithm_c2s = first_matching_element(
|
|
191
|
+
@algorithms[ :hmac ], mac_algorithms_client_to_server )
|
|
192
|
+
unless mac_algorithm_c2s
|
|
193
|
+
raise Net::SSH::Exception,
|
|
194
|
+
"could not settle on client-to-server HMAC algorithm"
|
|
195
|
+
end
|
|
196
|
+
if @logger.debug?
|
|
197
|
+
@logger.debug "hmac algorithm (client-to-server): " +
|
|
198
|
+
mac_algorithm_c2s
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
mac_algorithm_s2c = first_matching_element( @algorithms[ :hmac ],
|
|
202
|
+
mac_algorithms_server_to_client )
|
|
203
|
+
unless mac_algorithm_s2c
|
|
204
|
+
raise Net::SSH::Exception,
|
|
205
|
+
"could not settle on server-to-client HMAC algorithm"
|
|
206
|
+
end
|
|
207
|
+
if @logger.debug?
|
|
208
|
+
@logger.debug "hmac algorithm (server-to-client): " +
|
|
209
|
+
mac_algorithm_s2c
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
compression_algorithm_c2s = first_matching_element(
|
|
213
|
+
@algorithms[ :compression ],
|
|
214
|
+
compression_algorithms_client_to_server )
|
|
215
|
+
unless compression_algorithm_c2s
|
|
216
|
+
raise Net::SSH::Exception,
|
|
217
|
+
"could not settle on client-to-server compression algorithm"
|
|
218
|
+
end
|
|
219
|
+
if @logger.debug?
|
|
220
|
+
@logger.debug "compression algorithm (client-to-server): " +
|
|
221
|
+
compression_algorithm_c2s
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
compression_algorithm_s2c = first_matching_element(
|
|
225
|
+
@algorithms[ :compression ],
|
|
226
|
+
compression_algorithms_server_to_client )
|
|
227
|
+
unless compression_algorithm_s2c
|
|
228
|
+
raise Net::SSH::Exception,
|
|
229
|
+
"could not settle on server-to-client compression algorithm"
|
|
230
|
+
end
|
|
231
|
+
if @logger.debug?
|
|
232
|
+
@logger.debug "compression algorithm (server-to-client): " +
|
|
233
|
+
compression_algorithm_s2c
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
language_c2s = first_matching_element( @algorithms[ :languages ],
|
|
237
|
+
languages_client_to_server ) || ""
|
|
238
|
+
if @logger.debug?
|
|
239
|
+
@logger.debug "language (client-to-server): #{language_c2s}"
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
language_s2c = first_matching_element( @algorithms[ :languages ],
|
|
243
|
+
languages_server_to_client ) || ""
|
|
244
|
+
if @logger.debug?
|
|
245
|
+
@logger.debug "language (server-to-client): #{language_s2c}"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
return Algorithms.new( server_algorithm_packet,
|
|
249
|
+
client_algorithm_packet,
|
|
250
|
+
kex_algorithm,
|
|
251
|
+
host_key_algorithm,
|
|
252
|
+
encryption_algorithm_c2s,
|
|
253
|
+
encryption_algorithm_s2c,
|
|
254
|
+
mac_algorithm_c2s,
|
|
255
|
+
mac_algorithm_s2c,
|
|
256
|
+
compression_algorithm_c2s,
|
|
257
|
+
compression_algorithm_s2c,
|
|
258
|
+
language_c2s,
|
|
259
|
+
language_s2c,
|
|
260
|
+
@compression_level )
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|