hrr_rb_ssh 0.3.0.pre1 → 0.4.2

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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -3
  3. data/.travis.yml +1 -0
  4. data/README.md +208 -46
  5. data/demo/client.rb +71 -0
  6. data/demo/echo_server.rb +8 -3
  7. data/demo/more_flexible_auth.rb +105 -0
  8. data/demo/multi_step_auth.rb +99 -0
  9. data/demo/server.rb +10 -4
  10. data/demo/subsystem_echo_server.rb +8 -3
  11. data/hrr_rb_ssh.gemspec +6 -6
  12. data/lib/hrr_rb_ssh.rb +1 -1
  13. data/lib/hrr_rb_ssh/algorithm/publickey.rb +0 -1
  14. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2.rb +12 -9
  15. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/ecdsa_signature_blob.rb +2 -4
  16. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/public_key_blob.rb +2 -4
  17. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/signature.rb +2 -4
  18. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss.rb +10 -7
  19. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss/public_key_blob.rb +2 -4
  20. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss/signature.rb +2 -4
  21. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa.rb +9 -6
  22. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa/public_key_blob.rb +2 -4
  23. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa/signature.rb +2 -4
  24. data/lib/hrr_rb_ssh/authentication.rb +103 -22
  25. data/lib/hrr_rb_ssh/authentication/constant.rb +14 -0
  26. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive.rb +44 -7
  27. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/context.rb +16 -9
  28. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/info_request.rb +7 -6
  29. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/info_response.rb +5 -2
  30. data/lib/hrr_rb_ssh/authentication/method/none.rb +23 -7
  31. data/lib/hrr_rb_ssh/authentication/method/none/context.rb +15 -7
  32. data/lib/hrr_rb_ssh/authentication/method/password.rb +28 -7
  33. data/lib/hrr_rb_ssh/authentication/method/password/context.rb +16 -7
  34. data/lib/hrr_rb_ssh/authentication/method/publickey.rb +63 -10
  35. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm.rb +0 -1
  36. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/functionable.rb +32 -8
  37. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/signature_blob.rb +2 -4
  38. data/lib/hrr_rb_ssh/authentication/method/publickey/context.rb +11 -2
  39. data/lib/hrr_rb_ssh/client.rb +234 -0
  40. data/lib/hrr_rb_ssh/codable.rb +15 -13
  41. data/lib/hrr_rb_ssh/compat/ruby.rb +0 -1
  42. data/lib/hrr_rb_ssh/connection.rb +145 -75
  43. data/lib/hrr_rb_ssh/connection/channel.rb +342 -109
  44. data/lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb +24 -19
  45. data/lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb +24 -19
  46. data/lib/hrr_rb_ssh/connection/channel/channel_type/session.rb +19 -12
  47. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain.rb +0 -2
  48. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain/chain_context.rb +0 -3
  49. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env.rb +2 -5
  50. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env/context.rb +5 -4
  51. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec.rb +2 -5
  52. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec/context.rb +5 -4
  53. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req.rb +2 -5
  54. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req/context.rb +5 -4
  55. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell.rb +2 -5
  56. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell/context.rb +5 -4
  57. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem.rb +2 -5
  58. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem/context.rb +5 -4
  59. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change.rb +2 -5
  60. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change/context.rb +5 -4
  61. data/lib/hrr_rb_ssh/connection/global_request_handler.rb +14 -12
  62. data/lib/hrr_rb_ssh/connection/request_handler.rb +1 -3
  63. data/lib/hrr_rb_ssh/connection/request_handler/reference_env_request_handler.rb +0 -2
  64. data/lib/hrr_rb_ssh/connection/request_handler/reference_exec_request_handler.rb +4 -6
  65. data/lib/hrr_rb_ssh/connection/request_handler/reference_pty_req_request_handler.rb +10 -12
  66. data/lib/hrr_rb_ssh/connection/request_handler/reference_shell_request_handler.rb +4 -6
  67. data/lib/hrr_rb_ssh/connection/request_handler/reference_window_change_request_handler.rb +0 -2
  68. data/lib/hrr_rb_ssh/error/closed_authentication.rb +1 -1
  69. data/lib/hrr_rb_ssh/error/closed_connection.rb +1 -1
  70. data/lib/hrr_rb_ssh/error/closed_transport.rb +1 -1
  71. data/lib/hrr_rb_ssh/loggable.rb +42 -0
  72. data/lib/hrr_rb_ssh/message/001_ssh_msg_disconnect.rb +2 -4
  73. data/lib/hrr_rb_ssh/message/002_ssh_msg_ignore.rb +2 -4
  74. data/lib/hrr_rb_ssh/message/003_ssh_msg_unimplemented.rb +2 -4
  75. data/lib/hrr_rb_ssh/message/004_ssh_msg_debug.rb +2 -4
  76. data/lib/hrr_rb_ssh/message/005_ssh_msg_service_request.rb +2 -4
  77. data/lib/hrr_rb_ssh/message/006_ssh_msg_service_accept.rb +2 -4
  78. data/lib/hrr_rb_ssh/message/020_ssh_msg_kexinit.rb +2 -4
  79. data/lib/hrr_rb_ssh/message/021_ssh_msg_newkeys.rb +2 -4
  80. data/lib/hrr_rb_ssh/message/030_ssh_msg_kex_dh_gex_request_old.rb +2 -4
  81. data/lib/hrr_rb_ssh/message/030_ssh_msg_kexdh_init.rb +2 -4
  82. data/lib/hrr_rb_ssh/message/030_ssh_msg_kexecdh_init.rb +2 -4
  83. data/lib/hrr_rb_ssh/message/031_ssh_msg_kex_dh_gex_group.rb +2 -4
  84. data/lib/hrr_rb_ssh/message/031_ssh_msg_kexdh_reply.rb +2 -4
  85. data/lib/hrr_rb_ssh/message/031_ssh_msg_kexecdh_reply.rb +2 -4
  86. data/lib/hrr_rb_ssh/message/032_ssh_msg_kex_dh_gex_init.rb +2 -4
  87. data/lib/hrr_rb_ssh/message/033_ssh_msg_kex_dh_gex_reply.rb +2 -4
  88. data/lib/hrr_rb_ssh/message/034_ssh_msg_kex_dh_gex_request.rb +2 -4
  89. data/lib/hrr_rb_ssh/message/050_ssh_msg_userauth_request.rb +2 -4
  90. data/lib/hrr_rb_ssh/message/051_ssh_msg_userauth_failure.rb +2 -4
  91. data/lib/hrr_rb_ssh/message/052_ssh_msg_userauth_success.rb +2 -4
  92. data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_info_request.rb +2 -4
  93. data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_pk_ok.rb +2 -4
  94. data/lib/hrr_rb_ssh/message/061_ssh_msg_userauth_info_response.rb +2 -4
  95. data/lib/hrr_rb_ssh/message/080_ssh_msg_global_request.rb +2 -4
  96. data/lib/hrr_rb_ssh/message/081_ssh_msg_request_success.rb +2 -4
  97. data/lib/hrr_rb_ssh/message/082_ssh_msg_request_failure.rb +2 -4
  98. data/lib/hrr_rb_ssh/message/090_ssh_msg_channel_open.rb +2 -4
  99. data/lib/hrr_rb_ssh/message/091_ssh_msg_channel_open_confirmation.rb +2 -4
  100. data/lib/hrr_rb_ssh/message/092_ssh_msg_channel_open_failure.rb +2 -4
  101. data/lib/hrr_rb_ssh/message/093_ssh_msg_channel_window_adjust.rb +2 -4
  102. data/lib/hrr_rb_ssh/message/094_ssh_msg_channel_data.rb +2 -4
  103. data/lib/hrr_rb_ssh/message/095_ssh_msg_channel_extended_data.rb +2 -4
  104. data/lib/hrr_rb_ssh/message/096_ssh_msg_channel_eof.rb +2 -4
  105. data/lib/hrr_rb_ssh/message/097_ssh_msg_channel_close.rb +2 -4
  106. data/lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb +3 -5
  107. data/lib/hrr_rb_ssh/message/099_ssh_msg_channel_success.rb +2 -4
  108. data/lib/hrr_rb_ssh/message/100_ssh_msg_channel_failure.rb +2 -4
  109. data/lib/hrr_rb_ssh/server.rb +16 -10
  110. data/lib/hrr_rb_ssh/transport.rb +113 -77
  111. data/lib/hrr_rb_ssh/transport/compression_algorithm/functionable.rb +5 -3
  112. data/lib/hrr_rb_ssh/transport/compression_algorithm/unfunctionable.rb +5 -3
  113. data/lib/hrr_rb_ssh/transport/encryption_algorithm/functionable.rb +5 -3
  114. data/lib/hrr_rb_ssh/transport/encryption_algorithm/unfunctionable.rb +5 -3
  115. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman.rb +43 -37
  116. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman/h0.rb +2 -4
  117. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange.rb +87 -52
  118. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange/h0.rb +2 -4
  119. data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman.rb +43 -37
  120. data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman/h0.rb +2 -4
  121. data/lib/hrr_rb_ssh/transport/mac_algorithm/functionable.rb +5 -3
  122. data/lib/hrr_rb_ssh/transport/mac_algorithm/unfunctionable.rb +5 -3
  123. data/lib/hrr_rb_ssh/transport/receiver.rb +8 -7
  124. data/lib/hrr_rb_ssh/transport/sender.rb +5 -3
  125. data/lib/hrr_rb_ssh/transport/sequence_number.rb +0 -4
  126. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm.rb +0 -1
  127. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/functionable.rb +5 -3
  128. data/lib/hrr_rb_ssh/version.rb +1 -1
  129. metadata +18 -51
  130. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519.rb +0 -61
  131. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/openssh_private_key.rb +0 -29
  132. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/openssh_private_key_content.rb +0 -26
  133. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/pkey.rb +0 -158
  134. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/public_key_blob.rb +0 -23
  135. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/signature.rb +0 -23
  136. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/ssh_ed25519.rb +0 -21
  137. data/lib/hrr_rb_ssh/compat/ruby/array.rb +0 -14
  138. data/lib/hrr_rb_ssh/logger.rb +0 -56
  139. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/ssh_ed25519.rb +0 -20
@@ -8,10 +8,8 @@ module HrrRbSsh
8
8
  module Algorithm
9
9
  class Publickey
10
10
  module EcdsaSha2
11
- module Signature
12
- class << self
13
- include Codable
14
- end
11
+ class Signature
12
+ include Codable
15
13
  DEFINITION = [
16
14
  [DataType::String, :'public key algorithm name'],
17
15
  [DataType::String, :'ecdsa signature blob'],
@@ -1,16 +1,19 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
- require 'hrr_rb_ssh/logger'
4
+ require 'hrr_rb_ssh/loggable'
5
5
 
6
6
  module HrrRbSsh
7
7
  module Algorithm
8
8
  class Publickey
9
9
  class SshDss < Publickey
10
+ include Loggable
11
+
10
12
  NAME = 'ssh-dss'
11
13
  DIGEST = 'sha1'
12
14
 
13
- def initialize arg
15
+ def initialize arg, logger: nil
16
+ self.logger = logger
14
17
  begin
15
18
  new_by_key_str arg
16
19
  rescue OpenSSL::PKey::DSAError
@@ -23,7 +26,7 @@ module HrrRbSsh
23
26
  end
24
27
 
25
28
  def new_by_public_key_blob public_key_blob
26
- public_key_blob_h = PublicKeyBlob.decode(public_key_blob)
29
+ public_key_blob_h = PublicKeyBlob.new(logger: logger).decode public_key_blob
27
30
  @publickey = OpenSSL::PKey::DSA.new
28
31
  if @publickey.respond_to?(:set_pqg)
29
32
  @publickey.set_pqg public_key_blob_h[:'p'], public_key_blob_h[:'q'], public_key_blob_h[:'g']
@@ -51,24 +54,24 @@ module HrrRbSsh
51
54
  :'g' => @publickey.g.to_i,
52
55
  :'y' => @publickey.pub_key.to_i,
53
56
  }
54
- PublicKeyBlob.encode(public_key_blob_h)
57
+ PublicKeyBlob.new(logger: logger).encode public_key_blob_h
55
58
  end
56
59
 
57
60
  def sign signature_blob
58
61
  hash = OpenSSL::Digest.digest(self.class::DIGEST, signature_blob)
59
62
  sign_der = @publickey.syssign(hash)
60
- sign_asn1 = OpenSSL::ASN1.decode(sign_der)
63
+ sign_asn1 = OpenSSL::ASN1.decode sign_der
61
64
  sign_r = sign_asn1.value[0].value.to_s(2).rjust(20, ["00"].pack("H"))
62
65
  sign_s = sign_asn1.value[1].value.to_s(2).rjust(20, ["00"].pack("H"))
63
66
  signature_h = {
64
67
  :'public key algorithm name' => self.class::NAME,
65
68
  :'signature blob' => (sign_r + sign_s),
66
69
  }
67
- Signature.encode signature_h
70
+ Signature.new(logger: logger).encode signature_h
68
71
  end
69
72
 
70
73
  def verify signature, signature_blob
71
- signature_h = Signature.decode signature
74
+ signature_h = Signature.new(logger: logger).decode signature
72
75
  sign_r = signature_h[:'signature blob'][ 0, 20]
73
76
  sign_s = signature_h[:'signature blob'][20, 20]
74
77
  sign_asn1 = OpenSSL::ASN1::Sequence.new(
@@ -8,10 +8,8 @@ module HrrRbSsh
8
8
  module Algorithm
9
9
  class Publickey
10
10
  class SshDss
11
- module PublicKeyBlob
12
- class << self
13
- include Codable
14
- end
11
+ class PublicKeyBlob
12
+ include Codable
15
13
  DEFINITION = [
16
14
  [DataType::String, :'public key algorithm name'],
17
15
  [DataType::Mpint, :'p'],
@@ -8,10 +8,8 @@ module HrrRbSsh
8
8
  module Algorithm
9
9
  class Publickey
10
10
  class SshDss
11
- module Signature
12
- class << self
13
- include Codable
14
- end
11
+ class Signature
12
+ include Codable
15
13
  DEFINITION = [
16
14
  [DataType::String, :'public key algorithm name'],
17
15
  [DataType::String, :'signature blob'],
@@ -1,16 +1,19 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
- require 'hrr_rb_ssh/logger'
4
+ require 'hrr_rb_ssh/loggable'
5
5
 
6
6
  module HrrRbSsh
7
7
  module Algorithm
8
8
  class Publickey
9
9
  class SshRsa < Publickey
10
+ include Loggable
11
+
10
12
  NAME = 'ssh-rsa'
11
13
  DIGEST = 'sha1'
12
14
 
13
- def initialize arg
15
+ def initialize arg, logger: nil
16
+ self.logger = logger
14
17
  begin
15
18
  new_by_key_str arg
16
19
  rescue OpenSSL::PKey::RSAError
@@ -23,7 +26,7 @@ module HrrRbSsh
23
26
  end
24
27
 
25
28
  def new_by_public_key_blob public_key_blob
26
- public_key_blob_h = PublicKeyBlob.decode(public_key_blob)
29
+ public_key_blob_h = PublicKeyBlob.new(logger: logger).decode public_key_blob
27
30
  @publickey = OpenSSL::PKey::RSA.new
28
31
  if @publickey.respond_to?(:set_key)
29
32
  @publickey.set_key public_key_blob_h[:'n'], public_key_blob_h[:'e'], nil
@@ -43,7 +46,7 @@ module HrrRbSsh
43
46
  :'e' => @publickey.e.to_i,
44
47
  :'n' => @publickey.n.to_i,
45
48
  }
46
- PublicKeyBlob.encode(public_key_blob_h)
49
+ PublicKeyBlob.new(logger: logger).encode public_key_blob_h
47
50
  end
48
51
 
49
52
  def sign signature_blob
@@ -51,11 +54,11 @@ module HrrRbSsh
51
54
  :'public key algorithm name' => self.class::NAME,
52
55
  :'signature blob' => @publickey.sign(self.class::DIGEST, signature_blob),
53
56
  }
54
- Signature.encode signature_h
57
+ Signature.new(logger: logger).encode signature_h
55
58
  end
56
59
 
57
60
  def verify signature, signature_blob
58
- signature_h = Signature.decode signature
61
+ signature_h = Signature.new(logger: logger).decode signature
59
62
  signature_h[:'public key algorithm name'] == self.class::NAME && @publickey.verify(self.class::DIGEST, signature_h[:'signature blob'], signature_blob)
60
63
  end
61
64
  end
@@ -8,10 +8,8 @@ module HrrRbSsh
8
8
  module Algorithm
9
9
  class Publickey
10
10
  class SshRsa
11
- module PublicKeyBlob
12
- class << self
13
- include Codable
14
- end
11
+ class PublicKeyBlob
12
+ include Codable
15
13
  DEFINITION = [
16
14
  [DataType::String, :'public key algorithm name'],
17
15
  [DataType::Mpint, :'e'],
@@ -8,10 +8,8 @@ module HrrRbSsh
8
8
  module Algorithm
9
9
  class Publickey
10
10
  class SshRsa
11
- module Signature
12
- class << self
13
- include Codable
14
- end
11
+ class Signature
12
+ include Codable
15
13
  DEFINITION = [
16
14
  [DataType::String, :'public key algorithm name'],
17
15
  [DataType::String, :'signature blob'],
@@ -1,27 +1,31 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
- require 'hrr_rb_ssh/logger'
4
+ require 'hrr_rb_ssh/loggable'
5
5
  require 'hrr_rb_ssh/message'
6
6
  require 'hrr_rb_ssh/error/closed_authentication'
7
+ require 'hrr_rb_ssh/authentication/constant'
7
8
  require 'hrr_rb_ssh/authentication/authenticator'
8
9
  require 'hrr_rb_ssh/authentication/method'
9
10
 
10
11
  module HrrRbSsh
11
12
  class Authentication
12
- SERVICE_NAME = 'ssh-userauth'
13
+ include Loggable
14
+ include Constant
15
+
16
+ def initialize transport, mode, options={}, logger: nil
17
+ self.logger = logger
13
18
 
14
- def initialize transport, options={}
15
19
  @transport = transport
20
+ @mode = mode
16
21
  @options = options
17
22
 
18
- @logger = Logger.new self.class.name
19
-
20
23
  @transport.register_acceptable_service SERVICE_NAME
21
24
 
22
25
  @closed = nil
23
26
 
24
27
  @username = nil
28
+ @variables = {}
25
29
  end
26
30
 
27
31
  def send payload
@@ -29,6 +33,7 @@ module HrrRbSsh
29
33
  begin
30
34
  @transport.send payload
31
35
  rescue Error::ClosedTransport
36
+ close
32
37
  raise Error::ClosedAuthentication
33
38
  end
34
39
  end
@@ -38,19 +43,28 @@ module HrrRbSsh
38
43
  begin
39
44
  @transport.receive
40
45
  rescue Error::ClosedTransport
46
+ close
41
47
  raise Error::ClosedAuthentication
42
48
  end
43
49
  end
44
50
 
45
51
  def start
46
- @transport.start
47
- authenticate
52
+ log_info { "start authentication" }
53
+ begin
54
+ @transport.start
55
+ authenticate
56
+ rescue Error::ClosedTransport
57
+ close
58
+ raise Error::ClosedAuthentication
59
+ end
48
60
  end
49
61
 
50
62
  def close
51
63
  return if @closed
64
+ log_info { "close authentication" }
52
65
  @closed = true
53
66
  @transport.close
67
+ log_info { "authentication closed" }
54
68
  end
55
69
 
56
70
  def closed?
@@ -62,43 +76,110 @@ module HrrRbSsh
62
76
  @username
63
77
  end
64
78
 
79
+ def variables
80
+ raise Error::ClosedAuthentication if @closed
81
+ @variables
82
+ end
83
+
65
84
  def authenticate
85
+ case @mode
86
+ when Mode::SERVER
87
+ respond_to_authentication
88
+ when Mode::CLIENT
89
+ request_authentication
90
+ end
91
+ end
92
+
93
+ def respond_to_authentication
94
+ authentication_methods = (@options['authentication_preferred_authentication_methods'].dup rescue nil) || Method.list_preferred # rescue nil.dup for Ruby version < 2.4
95
+ log_info { "preferred authentication methods: #{authentication_methods}" }
66
96
  loop do
67
97
  payload = @transport.receive
68
98
  case payload[0,1].unpack("C")[0]
69
99
  when Message::SSH_MSG_USERAUTH_REQUEST::VALUE
70
- userauth_request_message = Message::SSH_MSG_USERAUTH_REQUEST.decode payload
100
+ userauth_request_message = Message::SSH_MSG_USERAUTH_REQUEST.new(logger: logger).decode payload
71
101
  method_name = userauth_request_message[:'method name']
72
- method = Method[method_name].new(@transport, {'session id' => @transport.session_id}.merge(@options))
102
+ log_info { "authentication method: #{method_name}" }
103
+ method = Method[method_name].new(@transport, {'session id' => @transport.session_id}.merge(@options), @variables, authentication_methods, logger: logger)
73
104
  result = method.authenticate(userauth_request_message)
74
105
  case result
75
- when TrueClass
76
- @logger.info { "verified" }
106
+ when true, SUCCESS
107
+ log_info { "verified" }
77
108
  send_userauth_success
78
109
  @username = userauth_request_message[:'user name']
79
110
  @closed = false
80
111
  break
81
- when FalseClass
82
- @logger.info { "verify failed" }
83
- send_userauth_failure
112
+ when PARTIAL_SUCCESS
113
+ log_info { "partially verified" }
114
+ authentication_methods.delete method_name
115
+ log_info { "authentication methods that can continue: #{authentication_methods}" }
116
+ if authentication_methods.empty?
117
+ log_info { "verified" }
118
+ send_userauth_success
119
+ @username = userauth_request_message[:'user name']
120
+ @closed = false
121
+ break
122
+ else
123
+ log_info { "continue" }
124
+ send_userauth_failure authentication_methods, true
125
+ end
84
126
  when String
85
- @logger.info { "send method specific message to continue" }
127
+ log_info { "send method specific message to continue" }
86
128
  send_method_specific_message result
129
+ else # when false, FAILURE
130
+ log_info { "verify failed" }
131
+ send_userauth_failure authentication_methods, false
87
132
  end
88
133
  else
89
- @closed = true
90
- raise
134
+ close
135
+ raise Error::ClosedAuthentication
136
+ end
137
+ end
138
+ end
139
+
140
+ def request_authentication
141
+ authentication_methods = (@options['authentication_preferred_authentication_methods'].dup rescue nil) || Method.list_preferred # rescue nil.dup for Ruby version < 2.4
142
+ log_info { "preferred authentication methods: #{authentication_methods}" }
143
+ next_method_name = "none"
144
+ log_info { "authentication request begins with none method" }
145
+ loop do
146
+ log_info { "authentication method: #{next_method_name}" }
147
+ method = Method[next_method_name].new(@transport, {'session id' => @transport.session_id}.merge(@options), @variables, authentication_methods, logger: logger)
148
+ payload = method.request_authentication @options['username'], "ssh-connection"
149
+ case payload[0,1].unpack("C")[0]
150
+ when Message::SSH_MSG_USERAUTH_SUCCESS::VALUE
151
+ log_info { "verified" }
152
+ @username = @options['username']
153
+ @closed = false
154
+ break
155
+ when Message::SSH_MSG_USERAUTH_FAILURE::VALUE
156
+ message = Message::SSH_MSG_USERAUTH_FAILURE.new(logger: logger).decode payload
157
+ partial_success = message[:'partial success']
158
+ if partial_success
159
+ log_info { "partially verified" }
160
+ end
161
+ authentication_methods_that_can_continue = message[:'authentications that can continue']
162
+ log_info { "authentication methods that can continue: #{authentication_methods_that_can_continue}" }
163
+ next_method_name = authentication_methods.find{ |local_m| authentication_methods_that_can_continue.find{ |remote_m| local_m == remote_m } }
164
+ if next_method_name
165
+ authentication_methods.delete next_method_name
166
+ log_info { "continue" }
167
+ else
168
+ log_info { "no more available authentication methods" }
169
+ @closed = true
170
+ raise "failed authentication"
171
+ end
91
172
  end
92
173
  end
93
174
  end
94
175
 
95
- def send_userauth_failure
176
+ def send_userauth_failure authentication_methods, partial_success
96
177
  message = {
97
178
  :'message number' => Message::SSH_MSG_USERAUTH_FAILURE::VALUE,
98
- :'authentications that can continue' => Method.list_preferred,
99
- :'partial success' => false,
179
+ :'authentications that can continue' => authentication_methods,
180
+ :'partial success' => partial_success,
100
181
  }
101
- payload = Message::SSH_MSG_USERAUTH_FAILURE.encode message
182
+ payload = Message::SSH_MSG_USERAUTH_FAILURE.new(logger: logger).encode message
102
183
  @transport.send payload
103
184
  end
104
185
 
@@ -106,7 +187,7 @@ module HrrRbSsh
106
187
  message = {
107
188
  :'message number' => Message::SSH_MSG_USERAUTH_SUCCESS::VALUE,
108
189
  }
109
- payload = Message::SSH_MSG_USERAUTH_SUCCESS.encode message
190
+ payload = Message::SSH_MSG_USERAUTH_SUCCESS.new(logger: logger).encode message
110
191
  @transport.send payload
111
192
  end
112
193
 
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbSsh
5
+ class Authentication
6
+ module Constant
7
+ SERVICE_NAME = 'ssh-userauth'
8
+
9
+ SUCCESS = :success
10
+ PARTIAL_SUCCESS = :partial_success
11
+ FAILURE = :failure
12
+ end
13
+ end
14
+ end
@@ -1,29 +1,66 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
- require 'hrr_rb_ssh/logger'
4
+ require 'hrr_rb_ssh/loggable'
5
5
 
6
6
  module HrrRbSsh
7
7
  class Authentication
8
8
  class Method
9
9
  class KeyboardInteractive < Method
10
+ include Loggable
11
+
10
12
  NAME = 'keyboard-interactive'
11
13
  PREFERENCE = 30
12
14
 
13
- def initialize transport, options
14
- @logger = Logger.new(self.class.name)
15
+ def initialize transport, options, variables, authentication_methods, logger: nil
16
+ self.logger = logger
15
17
  @transport = transport
16
- @authenticator = options.fetch( 'authentication_keyboard_interactive_authenticator', Authenticator.new { false } )
18
+ @options = options
19
+ @authenticator = options.fetch( 'authentication_keyboard_interactive_authenticator', Authenticator.new{ false } )
20
+ @variables = variables
21
+ @authentication_methods = authentication_methods
17
22
  end
18
23
 
19
24
  def authenticate userauth_request_message
20
- @logger.info { "authenticate" }
21
- @logger.debug { "userauth request: " + userauth_request_message.inspect }
25
+ log_info { "authenticate" }
22
26
  username = userauth_request_message[:'user name']
23
27
  submethods = userauth_request_message[:'submethods']
24
- context = Context.new(@transport, username, submethods)
28
+ context = Context.new(@transport, username, submethods, @variables, @authentication_methods, logger: logger)
25
29
  @authenticator.authenticate context
26
30
  end
31
+
32
+ def request_authentication username, service_name
33
+ message = {
34
+ :'message number' => Message::SSH_MSG_USERAUTH_REQUEST::VALUE,
35
+ :"user name" => username,
36
+ :"service name" => service_name,
37
+ :"method name" => NAME,
38
+ :"language tag" => "",
39
+ :'submethods' => "",
40
+ }
41
+ payload = Message::SSH_MSG_USERAUTH_REQUEST.new(logger: logger).encode message
42
+ @transport.send payload
43
+
44
+ payload = @transport.receive
45
+ case payload[0,1].unpack("C")[0]
46
+ when Message::SSH_MSG_USERAUTH_INFO_REQUEST::VALUE
47
+ message = Message::SSH_MSG_USERAUTH_INFO_REQUEST.new(logger: logger).decode payload
48
+ num_responses = @options['client_authentication_keyboard_interactive'].size
49
+ message = {
50
+ :'message number' => Message::SSH_MSG_USERAUTH_INFO_RESPONSE::VALUE,
51
+ :'num-responses' => num_responses,
52
+ }
53
+ message_responses = @options['client_authentication_keyboard_interactive'].map.with_index{ |response, i|
54
+ {:"response[#{i+1}]" => response}
55
+ }.inject(Hash.new){ |a, b| a.merge(b) }
56
+ message.update(message_responses)
57
+ payload = Message::SSH_MSG_USERAUTH_INFO_RESPONSE.new(logger: logger).encode message
58
+ @transport.send payload
59
+ @transport.receive
60
+ else
61
+ payload
62
+ end
63
+ end
27
64
  end
28
65
  end
29
66
  end