net-ssh 5.0.2 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/config/rubocop_linter_action.yml +4 -0
  5. data/.github/workflows/ci-with-docker.yml +44 -0
  6. data/.github/workflows/ci.yml +87 -0
  7. data/.github/workflows/rubocop.yml +13 -0
  8. data/.gitignore +3 -0
  9. data/.rubocop.yml +19 -2
  10. data/.rubocop_todo.yml +623 -511
  11. data/CHANGES.txt +76 -0
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +2 -0
  15. data/Gemfile.noed25519 +2 -0
  16. data/Manifest +0 -1
  17. data/README.md +293 -0
  18. data/Rakefile +6 -2
  19. data/appveyor.yml +4 -2
  20. data/docker-compose.yml +23 -0
  21. data/lib/net/ssh/authentication/agent.rb +36 -14
  22. data/lib/net/ssh/authentication/certificate.rb +19 -7
  23. data/lib/net/ssh/authentication/constants.rb +0 -1
  24. data/lib/net/ssh/authentication/ed25519.rb +83 -50
  25. data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
  26. data/lib/net/ssh/authentication/key_manager.rb +74 -33
  27. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  28. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  29. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +5 -3
  30. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  31. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  32. data/lib/net/ssh/authentication/methods/publickey.rb +58 -16
  33. data/lib/net/ssh/authentication/pageant.rb +97 -97
  34. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -3
  35. data/lib/net/ssh/authentication/session.rb +27 -23
  36. data/lib/net/ssh/buffer.rb +91 -40
  37. data/lib/net/ssh/buffered_io.rb +24 -26
  38. data/lib/net/ssh/config.rb +99 -53
  39. data/lib/net/ssh/connection/channel.rb +101 -87
  40. data/lib/net/ssh/connection/constants.rb +0 -4
  41. data/lib/net/ssh/connection/event_loop.rb +30 -25
  42. data/lib/net/ssh/connection/keepalive.rb +12 -12
  43. data/lib/net/ssh/connection/session.rb +115 -111
  44. data/lib/net/ssh/connection/term.rb +56 -58
  45. data/lib/net/ssh/errors.rb +12 -12
  46. data/lib/net/ssh/key_factory.rb +108 -22
  47. data/lib/net/ssh/known_hosts.rb +120 -36
  48. data/lib/net/ssh/loggable.rb +10 -11
  49. data/lib/net/ssh/packet.rb +1 -1
  50. data/lib/net/ssh/prompt.rb +9 -11
  51. data/lib/net/ssh/proxy/command.rb +1 -2
  52. data/lib/net/ssh/proxy/errors.rb +2 -4
  53. data/lib/net/ssh/proxy/http.rb +18 -20
  54. data/lib/net/ssh/proxy/https.rb +8 -10
  55. data/lib/net/ssh/proxy/jump.rb +8 -10
  56. data/lib/net/ssh/proxy/socks4.rb +2 -4
  57. data/lib/net/ssh/proxy/socks5.rb +3 -6
  58. data/lib/net/ssh/service/forward.rb +9 -8
  59. data/lib/net/ssh/test/channel.rb +24 -26
  60. data/lib/net/ssh/test/extensions.rb +37 -35
  61. data/lib/net/ssh/test/kex.rb +6 -8
  62. data/lib/net/ssh/test/local_packet.rb +0 -2
  63. data/lib/net/ssh/test/packet.rb +3 -3
  64. data/lib/net/ssh/test/remote_packet.rb +6 -8
  65. data/lib/net/ssh/test/script.rb +25 -27
  66. data/lib/net/ssh/test/socket.rb +12 -15
  67. data/lib/net/ssh/test.rb +12 -12
  68. data/lib/net/ssh/transport/algorithms.rb +177 -118
  69. data/lib/net/ssh/transport/cipher_factory.rb +34 -50
  70. data/lib/net/ssh/transport/constants.rb +13 -9
  71. data/lib/net/ssh/transport/ctr.rb +8 -14
  72. data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
  73. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  74. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  75. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  78. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  79. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  80. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  81. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  82. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  83. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  84. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  85. data/lib/net/ssh/transport/hmac.rb +13 -11
  86. data/lib/net/ssh/transport/identity_cipher.rb +11 -13
  87. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  88. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  89. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  90. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +5 -19
  92. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +30 -139
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -8
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  96. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +20 -81
  97. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
  99. data/lib/net/ssh/transport/kex.rb +15 -10
  100. data/lib/net/ssh/transport/key_expander.rb +7 -8
  101. data/lib/net/ssh/transport/openssl.rb +149 -111
  102. data/lib/net/ssh/transport/packet_stream.rb +53 -22
  103. data/lib/net/ssh/transport/server_version.rb +17 -16
  104. data/lib/net/ssh/transport/session.rb +35 -11
  105. data/lib/net/ssh/transport/state.rb +44 -44
  106. data/lib/net/ssh/verifiers/accept_new.rb +7 -2
  107. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  108. data/lib/net/ssh/verifiers/always.rb +10 -4
  109. data/lib/net/ssh/verifiers/never.rb +4 -2
  110. data/lib/net/ssh/version.rb +2 -2
  111. data/lib/net/ssh.rb +17 -9
  112. data/net-ssh-public_cert.pem +18 -19
  113. data/net-ssh.gemspec +9 -7
  114. data/support/ssh_tunnel_bug.rb +3 -3
  115. data.tar.gz.sig +0 -0
  116. metadata +65 -41
  117. metadata.gz.sig +0 -0
  118. data/.travis.yml +0 -52
  119. data/Gemfile.noed25519.lock +0 -41
  120. data/README.rdoc +0 -169
  121. data/lib/net/ssh/ruby_compat.rb +0 -13
  122. data/support/arcfour_check.rb +0 -20
@@ -31,7 +31,17 @@ module Net
31
31
  cert.key_id = buffer.read_string
32
32
  cert.valid_principals = buffer.read_buffer.read_all(&:read_string)
33
33
  cert.valid_after = Time.at(buffer.read_int64)
34
- cert.valid_before = Time.at(buffer.read_int64)
34
+
35
+ cert.valid_before = if RUBY_PLATFORM == "java"
36
+ # 0x20c49ba5e353f7 = 0x7fffffffffffffff/1000, the largest value possible for JRuby
37
+ # JRuby Time.at multiplies the arg by 1000, and then stores it in a signed long.
38
+ # 0x20c49ba2d52500 = 292278993-01-01 00:00:00 +0000
39
+ # JRuby 9.1 does not accept the year 292278994 because of edge cases (https://github.com/JodaOrg/joda-time/issues/190)
40
+ Time.at([0x20c49ba2d52500, buffer.read_int64].min)
41
+ else
42
+ Time.at(buffer.read_int64)
43
+ end
44
+
35
45
  cert.critical_options = read_options(buffer)
36
46
  cert.extensions = read_options(buffer)
37
47
  cert.reserved = buffer.read_string
@@ -56,12 +66,12 @@ module Net
56
66
  ).to_s
57
67
  end
58
68
 
59
- def ssh_do_sign(data)
60
- key.ssh_do_sign(data)
69
+ def ssh_do_sign(data, sig_alg = nil)
70
+ key.ssh_do_sign(data, sig_alg)
61
71
  end
62
72
 
63
- def ssh_do_verify(sig, data)
64
- key.ssh_do_verify(sig, data)
73
+ def ssh_do_verify(sig, data, options = {})
74
+ key.ssh_do_verify(sig, data, options)
65
75
  end
66
76
 
67
77
  def to_pem
@@ -73,7 +83,7 @@ module Net
73
83
  end
74
84
 
75
85
  # Signs the certificate with key.
76
- def sign!(key, sign_nonce=nil)
86
+ def sign!(key, sign_nonce = nil)
77
87
  # ssh-keygen uses 32 bytes of nonce.
78
88
  self.nonce = sign_nonce || SecureRandom.random_bytes(32)
79
89
  self.signature_key = key
@@ -84,7 +94,7 @@ module Net
84
94
  self
85
95
  end
86
96
 
87
- def sign(key, sign_nonce=nil)
97
+ def sign(key, sign_nonce = nil)
88
98
  cert = clone
89
99
  cert.sign!(key, sign_nonce)
90
100
  end
@@ -115,6 +125,7 @@ module Net
115
125
  def self.type_symbol(type)
116
126
  types = { 1 => :user, 2 => :host }
117
127
  raise ArgumentError("unsupported type: #{type}") unless types.include?(type)
128
+
118
129
  types.fetch(type)
119
130
  end
120
131
  private_class_method :type_symbol
@@ -124,6 +135,7 @@ module Net
124
135
  def type_value(type)
125
136
  types = { user: 1, host: 2 }
126
137
  raise ArgumentError("unsupported type: #{type}") unless types.include?(type)
138
+
127
139
  types.fetch(type)
128
140
  end
129
141
 
@@ -1,7 +1,6 @@
1
1
  module Net
2
2
  module SSH
3
3
  module Authentication
4
-
5
4
  # Describes the constants used by the Net::SSH::Authentication components
6
5
  # of the Net::SSH library. Individual authentication method implemenations
7
6
  # may define yet more constants that are specific to their implementation.
@@ -14,7 +14,7 @@ module Net
14
14
  module Authentication
15
15
  module ED25519
16
16
  class SigningKeyFromFile < SimpleDelegator
17
- def initialize(pk,sk)
17
+ def initialize(pk, sk)
18
18
  key = ::Ed25519::SigningKey.from_keypair(sk)
19
19
  raise ArgumentError, "pk does not match sk" unless pk == key.verify_key.to_bytes
20
20
 
@@ -22,56 +22,33 @@ module Net
22
22
  end
23
23
  end
24
24
 
25
- class PubKey
26
- include Net::SSH::Authentication::PubKeyFingerprint
27
-
28
- attr_reader :verify_key
29
-
30
- def initialize(data)
31
- @verify_key = ::Ed25519::VerifyKey.new(data)
32
- end
33
-
34
- def self.read_keyblob(buffer)
35
- PubKey.new(buffer.read_string)
36
- end
37
-
38
- def to_blob
39
- Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s
40
- end
41
-
42
- def ssh_type
43
- "ssh-ed25519"
44
- end
45
-
46
- def ssh_signature_type
47
- ssh_type
48
- end
49
-
50
- def ssh_do_verify(sig,data)
51
- @verify_key.verify(sig,data)
52
- end
53
-
54
- def to_pem
55
- # TODO this is not pem
56
- ssh_type + Base64.encode64(@verify_key.to_bytes)
57
- end
58
- end
59
-
60
- class PrivKey
25
+ class OpenSSHPrivateKeyLoader
61
26
  CipherFactory = Net::SSH::Transport::CipherFactory
62
27
 
63
28
  MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
64
- MEND = "-----END OPENSSH PRIVATE KEY-----\n"
29
+ MEND = "-----END OPENSSH PRIVATE KEY-----"
65
30
  MAGIC = "openssh-key-v1"
66
31
 
67
- attr_reader :sign_key
32
+ class DecryptError < ArgumentError
33
+ def initialize(message, encrypted_key: false)
34
+ super(message)
35
+ @encrypted_key = encrypted_key
36
+ end
37
+
38
+ def encrypted_key?
39
+ return @encrypted_key
40
+ end
41
+ end
68
42
 
69
- def initialize(datafull,password)
43
+ def self.read(datafull, password)
44
+ datafull = datafull.strip
70
45
  raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
71
46
  raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
47
+
72
48
  datab64 = datafull[MBEGIN.size...-MEND.size]
73
49
  data = Base64.decode64(datab64)
74
50
  raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
51
+
75
52
  buffer = Net::SSH::Buffer.new(data[MAGIC.size + 1..-1])
76
53
 
77
54
  ciphername = buffer.read_string
@@ -84,6 +61,7 @@ module Net
84
61
  kdfopts = Net::SSH::Buffer.new(buffer.read_string)
85
62
  num_keys = buffer.read_long
86
63
  raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
64
+
87
65
  _pubkey = buffer.read_string
88
66
 
89
67
  len = buffer.read_long
@@ -97,12 +75,13 @@ module Net
97
75
  rounds = kdfopts.read_long
98
76
 
99
77
  raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java"
78
+
100
79
  key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds)
101
80
  else
102
81
  key = '\x00' * (keylen + ivlen)
103
82
  end
104
83
 
105
- cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv:key[keylen...keylen + ivlen], decrypt: true)
84
+ cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv: key[keylen...keylen + ivlen], decrypt: true)
106
85
 
107
86
  decoded = cipher.update(buffer.remainder_as_buffer.to_s)
108
87
  decoded << cipher.final
@@ -111,15 +90,69 @@ module Net
111
90
  check1 = decoded.read_long
112
91
  check2 = decoded.read_long
113
92
 
114
- raise ArgumentError, "Decrypt failed on private key" if (check1 != check2)
93
+ raise DecryptError.new("Decrypt failed on private key", encrypted_key: kdfname == 'bcrypt') if (check1 != check2)
94
+
95
+ type_name = decoded.read_string
96
+ case type_name
97
+ when "ssh-ed25519"
98
+ PrivKey.new(decoded)
99
+ else
100
+ decoded.read_private_keyblob(type_name)
101
+ end
102
+ end
103
+ end
104
+
105
+ class PubKey
106
+ include Net::SSH::Authentication::PubKeyFingerprint
107
+
108
+ attr_reader :verify_key
109
+
110
+ def initialize(data)
111
+ @verify_key = ::Ed25519::VerifyKey.new(data)
112
+ end
113
+
114
+ def self.read_keyblob(buffer)
115
+ PubKey.new(buffer.read_string)
116
+ end
117
+
118
+ def to_blob
119
+ Net::SSH::Buffer.from(:mstring, "ssh-ed25519".dup, :string, @verify_key.to_bytes).to_s
120
+ end
121
+
122
+ def ssh_type
123
+ "ssh-ed25519"
124
+ end
125
+
126
+ def ssh_signature_type
127
+ ssh_type
128
+ end
129
+
130
+ def ssh_do_verify(sig, data, options = {})
131
+ @verify_key.verify(sig, data)
132
+ end
133
+
134
+ def to_pem
135
+ # TODO this is not pem
136
+ ssh_type + Base64.encode64(@verify_key.to_bytes)
137
+ end
138
+ end
139
+
140
+ class PrivKey
141
+ CipherFactory = Net::SSH::Transport::CipherFactory
142
+
143
+ MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
144
+ MEND = "-----END OPENSSH PRIVATE KEY-----\n"
145
+ MAGIC = "openssh-key-v1"
146
+
147
+ attr_reader :sign_key
115
148
 
116
- _type_name = decoded.read_string
117
- pk = decoded.read_string
118
- sk = decoded.read_string
119
- _comment = decoded.read_string
149
+ def initialize(buffer)
150
+ pk = buffer.read_string
151
+ sk = buffer.read_string
152
+ _comment = buffer.read_string
120
153
 
121
154
  @pk = pk
122
- @sign_key = SigningKeyFromFile.new(pk,sk)
155
+ @sign_key = SigningKeyFromFile.new(pk, sk)
123
156
  end
124
157
 
125
158
  def to_blob
@@ -138,12 +171,12 @@ module Net
138
171
  PubKey.new(@pk)
139
172
  end
140
173
 
141
- def ssh_do_sign(data)
174
+ def ssh_do_sign(data, sig_alg = nil)
142
175
  @sign_key.sign(data)
143
176
  end
144
177
 
145
- def self.read(data,password)
146
- self.new(data,password)
178
+ def self.read(data, password)
179
+ OpenSSHPrivateKeyLoader.read(data, password)
147
180
  end
148
181
  end
149
182
  end
@@ -1,11 +1,9 @@
1
- module Net
2
- module SSH
1
+ module Net
2
+ module SSH
3
3
  module Authentication
4
-
5
4
  # Loads ED25519 support which requires optinal dependecies like
6
- # rbnacl, bcrypt_pbkdf
5
+ # ed25519, bcrypt_pbkdf
7
6
  module ED25519Loader
8
-
9
7
  begin
10
8
  require 'net/ssh/authentication/ed25519'
11
9
  LOADED = true
@@ -14,20 +12,19 @@ module Net
14
12
  ERROR = e
15
13
  LOADED = false
16
14
  end
17
-
15
+
18
16
  def self.raiseUnlessLoaded(message)
19
17
  description = ERROR.is_a?(LoadError) ? dependenciesRequiredForED25519 : ''
20
18
  description << "#{ERROR.class} : \"#{ERROR.message}\"\n" if ERROR
21
19
  raise NotImplementedError, "#{message}\n#{description}" unless LOADED
22
20
  end
23
-
21
+
24
22
  def self.dependenciesRequiredForED25519
25
23
  result = "net-ssh requires the following gems for ed25519 support:\n"
26
24
  result << " * ed25519 (>= 1.2, < 2.0)\n"
27
25
  result << " * bcrypt_pbkdf (>= 1.0, < 2.0)\n" unless RUBY_PLATFORM == "java"
28
26
  result << "See https://github.com/net-ssh/net-ssh/issues/565 for more information\n"
29
27
  end
30
-
31
28
  end
32
29
  end
33
30
  end
@@ -6,7 +6,6 @@ require 'net/ssh/authentication/agent'
6
6
  module Net
7
7
  module SSH
8
8
  module Authentication
9
-
10
9
  # A trivial exception class used to report errors in the key manager.
11
10
  class KeyManagerError < Net::SSH::Exception; end
12
11
 
@@ -30,6 +29,9 @@ module Net
30
29
  # The list of user key data that will be examined
31
30
  attr_reader :key_data
32
31
 
32
+ # The list of user key certificate files that will be examined
33
+ attr_reader :keycert_files
34
+
33
35
  # The map of loaded identities
34
36
  attr_reader :known_identities
35
37
 
@@ -39,10 +41,11 @@ module Net
39
41
  # Create a new KeyManager. By default, the manager will
40
42
  # use the ssh-agent if it is running and the `:use_agent` option
41
43
  # is not false.
42
- def initialize(logger, options={})
44
+ def initialize(logger, options = {})
43
45
  self.logger = logger
44
46
  @key_files = []
45
47
  @key_data = []
48
+ @keycert_files = []
46
49
  @use_agent = options[:use_agent] != false
47
50
  @known_identities = {}
48
51
  @agent = nil
@@ -66,6 +69,12 @@ module Net
66
69
  self
67
70
  end
68
71
 
72
+ # Add the given keycert_file to the list of keycert files that will be used.
73
+ def add_keycert(keycert_file)
74
+ keycert_files.push(File.expand_path(keycert_file)).uniq!
75
+ self
76
+ end
77
+
69
78
  # Add the given key_file to the list of keys that will be used.
70
79
  def add_key_data(key_data_)
71
80
  key_data.push(key_data_).uniq!
@@ -108,7 +117,7 @@ module Net
108
117
  user_identities.delete(corresponding_user_identity) if corresponding_user_identity
109
118
 
110
119
  if !options[:keys_only] || corresponding_user_identity
111
- known_identities[key] = { from: :agent }
120
+ known_identities[key] = { from: :agent, identity: key }
112
121
  yield key
113
122
  end
114
123
  end
@@ -122,6 +131,21 @@ module Net
122
131
  yield key
123
132
  end
124
133
 
134
+ known_identity_blobs = known_identities.keys.map(&:to_blob)
135
+ keycert_files.each do |keycert_file|
136
+ keycert = KeyFactory.load_public_key(keycert_file)
137
+ next if known_identity_blobs.include?(keycert.to_blob)
138
+
139
+ (_, corresponding_identity) = known_identities.detect { |public_key, _|
140
+ public_key.to_pem == keycert.to_pem
141
+ }
142
+
143
+ if corresponding_identity
144
+ known_identities[keycert] = corresponding_identity
145
+ yield keycert
146
+ end
147
+ end
148
+
125
149
  self
126
150
  end
127
151
 
@@ -134,25 +158,39 @@ module Net
134
158
  # Regardless of the identity's origin or who does the signing, this
135
159
  # will always return the signature in an SSH2-specified "signature
136
160
  # blob" format.
137
- def sign(identity, data)
161
+ def sign(identity, data, sig_alg = nil)
138
162
  info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
139
163
 
140
164
  if info[:key].nil? && info[:from] == :file
141
165
  begin
142
- info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive])
166
+ info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive], options[:password_prompt])
143
167
  rescue OpenSSL::OpenSSLError, Exception => e
144
168
  raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
145
169
  end
146
170
  end
147
171
 
148
172
  if info[:key]
149
- return Net::SSH::Buffer.from(:string, identity.ssh_signature_type,
150
- :mstring, info[:key].ssh_do_sign(data.to_s)).to_s
173
+ if sig_alg.nil?
174
+ signed = info[:key].ssh_do_sign(data.to_s)
175
+ sig_alg = identity.ssh_signature_type
176
+ else
177
+ signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
178
+ end
179
+ return Net::SSH::Buffer.from(:string, sig_alg,
180
+ :mstring, signed).to_s
151
181
  end
152
182
 
153
183
  if info[:from] == :agent
154
184
  raise KeyManagerError, "the agent is no longer available" unless agent
155
- return agent.sign(identity, data.to_s)
185
+
186
+ case sig_alg
187
+ when "rsa-sha2-512"
188
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
189
+ when "rsa-sha2-256"
190
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
191
+ else
192
+ return agent.sign(info[:identity], data.to_s)
193
+ end
156
194
  end
157
195
 
158
196
  raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
@@ -176,7 +214,8 @@ module Net
176
214
  # or if the agent is otherwise not available.
177
215
  def agent
178
216
  return unless use_agent?
179
- @agent ||= Agent.connect(logger, options[:agent_socket_factory])
217
+
218
+ @agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
180
219
  rescue AgentNotAvailable
181
220
  @use_agent = false
182
221
  nil
@@ -223,33 +262,35 @@ module Net
223
262
  # Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
224
263
  def load_identities(identities, ask_passphrase, ignore_decryption_errors)
225
264
  identities.map do |identity|
226
- begin
227
- case identity[:load_from]
228
- when :pubkey_file
229
- key = KeyFactory.load_public_key(identity[:pubkey_file])
230
- { public_key: key, from: :file, file: identity[:privkey_file] }
231
- when :privkey_file
232
- private_key = KeyFactory.load_private_key(identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt])
233
- key = private_key.send(:public_key)
234
- { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
235
- when :data
236
- private_key = KeyFactory.load_data_private_key(identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt])
237
- key = private_key.send(:public_key)
238
- { public_key: key, from: :key_data, data: identity[:data], key: private_key }
239
- else
240
- identity
241
- end
242
- rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
243
- if ignore_decryption_errors
244
- identity
245
- else
246
- process_identity_loading_error(identity, e)
247
- nil
248
- end
249
- rescue Exception => e
265
+ case identity[:load_from]
266
+ when :pubkey_file
267
+ key = KeyFactory.load_public_key(identity[:pubkey_file])
268
+ { public_key: key, from: :file, file: identity[:privkey_file] }
269
+ when :privkey_file
270
+ private_key = KeyFactory.load_private_key(
271
+ identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
272
+ )
273
+ key = private_key.send(:public_key)
274
+ { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
275
+ when :data
276
+ private_key = KeyFactory.load_data_private_key(
277
+ identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
278
+ )
279
+ key = private_key.send(:public_key)
280
+ { public_key: key, from: :key_data, data: identity[:data], key: private_key }
281
+ else
282
+ identity
283
+ end
284
+ rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
285
+ if ignore_decryption_errors
286
+ identity
287
+ else
250
288
  process_identity_loading_error(identity, e)
251
289
  nil
252
290
  end
291
+ rescue Exception => e
292
+ process_identity_loading_error(identity, e)
293
+ nil
253
294
  end.compact
254
295
  end
255
296
 
@@ -7,7 +7,6 @@ module Net
7
7
  module SSH
8
8
  module Authentication
9
9
  module Methods
10
-
11
10
  # The base class of all user authentication methods. It provides a few
12
11
  # bits of common functionality.
13
12
  class Abstract
@@ -21,12 +20,22 @@ module Net
21
20
  # this.
22
21
  attr_reader :key_manager
23
22
 
23
+ # So far only affects algorithms used for rsa keys, but can be
24
+ # extended to other keys, e.g after reading of
25
+ # PubkeyAcceptedAlgorithms option from ssh_config file is implemented.
26
+ attr_reader :pubkey_algorithms
27
+
24
28
  # Instantiates a new authentication method.
25
- def initialize(session, options={})
29
+ def initialize(session, options = {})
26
30
  @session = session
27
31
  @key_manager = options[:key_manager]
28
32
  @options = options
29
33
  @prompt = options[:password_prompt]
34
+ @pubkey_algorithms = options[:pubkey_algorithms] \
35
+ || %w[rsa-sha2-256-cert-v01@openssh.com
36
+ ssh-rsa-cert-v01@openssh.com
37
+ rsa-sha2-256
38
+ ssh-rsa]
30
39
  self.logger = session.logger
31
40
  end
32
41
 
@@ -47,7 +56,7 @@ module Net
47
56
  # of the packet. The new packet is returned, ready for sending.
48
57
  def userauth_request(username, next_service, auth_method, *others)
49
58
  buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
50
- :string, username, :string, next_service, :string, auth_method)
59
+ :string, username, :string, next_service, :string, auth_method)
51
60
 
52
61
  others.each do |value|
53
62
  case value
@@ -4,19 +4,18 @@ module Net
4
4
  module SSH
5
5
  module Authentication
6
6
  module Methods
7
-
8
7
  # Implements the host-based SSH authentication method.
9
8
  class Hostbased < Abstract
10
9
  include Constants
11
10
 
12
11
  # Attempts to perform host-based authorization of the user by trying
13
12
  # all known keys.
14
- def authenticate(next_service, username, password=nil)
13
+ def authenticate(next_service, username, password = nil)
15
14
  return false unless key_manager
16
15
 
17
16
  key_manager.each_identity do |identity|
18
17
  return true if authenticate_with(identity, next_service,
19
- username, key_manager)
18
+ username, key_manager)
20
19
  end
21
20
 
22
21
  return false
@@ -64,10 +63,9 @@ module Net
64
63
  # Build the "core" hostbased request string.
65
64
  def build_request(identity, next_service, username, hostname, client_username)
66
65
  userauth_request(username, next_service, "hostbased", identity.ssh_type,
67
- Buffer.from(:key, identity).to_s, hostname, client_username).to_s
66
+ Buffer.from(:key, identity).to_s, hostname, client_username).to_s
68
67
  end
69
68
  end
70
-
71
69
  end
72
70
  end
73
71
  end
@@ -5,14 +5,13 @@ module Net
5
5
  module SSH
6
6
  module Authentication
7
7
  module Methods
8
-
9
8
  # Implements the "keyboard-interactive" SSH authentication method.
10
9
  class KeyboardInteractive < Abstract
11
10
  USERAUTH_INFO_REQUEST = 60
12
11
  USERAUTH_INFO_RESPONSE = 61
13
12
 
14
13
  # Attempt to authenticate the given user for the given service.
15
- def authenticate(next_service, username, password=nil)
14
+ def authenticate(next_service, username, password = nil)
16
15
  debug { "trying keyboard-interactive" }
17
16
  send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
18
17
 
@@ -32,6 +31,7 @@ module Net
32
31
  message[:authentications].split(/,/).include? 'keyboard-interactive'
33
32
 
34
33
  return false unless interactive?
34
+
35
35
  password = nil
36
36
  debug { "retrying keyboard-interactive" }
37
37
  send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
@@ -40,7 +40,9 @@ module Net
40
40
  instruction = message.read_string
41
41
  debug { "keyboard-interactive info request" }
42
42
 
43
- prompter = prompt.start(type: 'keyboard-interactive', name: name, instruction: instruction) if password.nil? && interactive? && prompter.nil?
43
+ if password.nil? && interactive? && prompter.nil?
44
+ prompter = prompt.start(type: 'keyboard-interactive', name: name, instruction: instruction)
45
+ end
44
46
 
45
47
  _ = message.read_string # lang_tag
46
48
  responses = []
@@ -5,32 +5,29 @@ module Net
5
5
  module SSH
6
6
  module Authentication
7
7
  module Methods
8
-
9
8
  # Implements the "none" SSH authentication method.
10
9
  class None < Abstract
11
10
  # Attempt to authenticate as "none"
12
- def authenticate(next_service, user="", password="")
13
- send_message(userauth_request(user, next_service, "none"))
11
+ def authenticate(next_service, user = "", password = "")
12
+ send_message(userauth_request(user, next_service, "none"))
14
13
  message = session.next_message
15
-
14
+
16
15
  case message.type
17
16
  when USERAUTH_SUCCESS
18
17
  debug { "none succeeded" }
19
18
  return true
20
19
  when USERAUTH_FAILURE
21
20
  debug { "none failed" }
22
-
21
+
23
22
  raise Net::SSH::Authentication::DisallowedMethod unless
24
23
  message[:authentications].split(/,/).include? 'none'
25
-
24
+
26
25
  return false
27
26
  else
28
27
  raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
29
- end
30
-
28
+ end
31
29
  end
32
30
  end
33
-
34
31
  end
35
32
  end
36
33
  end
@@ -6,12 +6,11 @@ module Net
6
6
  module SSH
7
7
  module Authentication
8
8
  module Methods
9
-
10
9
  # Implements the "password" SSH authentication method.
11
10
  class Password < Abstract
12
11
  # Attempt to authenticate the given user for the given service. If
13
12
  # the password parameter is nil, this will ask for password
14
- def authenticate(next_service, username, password=nil)
13
+ def authenticate(next_service, username, password = nil)
15
14
  clear_prompter!
16
15
  retries = 0
17
16
  max_retries = get_max_retries
@@ -29,6 +28,7 @@ module Net
29
28
 
30
29
  raise Net::SSH::Authentication::DisallowedMethod unless
31
30
  message[:authentications].split(/,/).include? 'password'
31
+
32
32
  password = nil
33
33
  end
34
34
  end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
@@ -74,7 +74,6 @@ module Net
74
74
  options[:non_interactive] ? 0 : result
75
75
  end
76
76
  end
77
-
78
77
  end
79
78
  end
80
79
  end