hrr_rb_ssh 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c9084a6fa67c8c215d0fa795eef327f6f7b8c52c7a6bda4a33ab0e97b65560b
4
- data.tar.gz: 655a99379cf97086e9d29f1fcb3ffcabeec8058c7c4cbede3ce5c06ddaa8c7a2
3
+ metadata.gz: 895f28010a8eb2c1e18c12369fd4c10922ba5a69ef0527ce165c97529eae0739
4
+ data.tar.gz: c6a0669ea1e9561e007ae22259d111ce011589e383cc0ac73b7678d5668b14cc
5
5
  SHA512:
6
- metadata.gz: 240f03f04c51b1cb0671d15d7a0a89fdc9b64113cf0a5ba26ae2aa315dd77c107ec19213744065b7dd601e11937ad36a63085c5474bd4669e5e7b7f19499a2f5
7
- data.tar.gz: ade3eac97ff8a9734a68336329c6cae56d5f427e73cdf96e65d3f8808b7128ac6a791c945fae975879a65e73b0565c56a6fd3c10ba5aa7c2dd89a3264db5f6e5
6
+ metadata.gz: 3e41e26012c69eab8dbc2324fd71cd1212ebb56d87b76a30b4c7e59a4eba8627301bcfc18a26d42b7742784034f8ffbcf9ed06ca770a4717e05b7aac47e7780c
7
+ data.tar.gz: efd75bcd9eb57930c867880010eb6ffc9cb307080f4478df3ffa6363cf46845be9f0aa13a273d1abc161cfab60250562ae8d87ab9db068f7afe6c76382244ed9
data/README.md CHANGED
@@ -7,6 +7,32 @@
7
7
 
8
8
  hrr_rb_ssh is a pure Ruby SSH 2.0 server implementation.
9
9
 
10
+ ## Table of Contents
11
+
12
+ - [Installation](#installation)
13
+ - [Usage](#usage)
14
+ - [Writing standard SSH server](#writing-standard-ssh-server)
15
+ - [Requiring hrr\_rb\_ssh library](#requiring-hrr_rb_ssh-library)
16
+ - [Starting server application](#starting-server-application)
17
+ - [Logging](#logging)
18
+ - [Registering pre\-generated secret keys for server host key](#registering-pre-generated-secret-keys-for-server-host-key)
19
+ - [Defining authentications](#defining-authentications)
20
+ - [Password authentication](#password-authentication)
21
+ - [Publickey authentication](#publickey-authentication)
22
+ - [None authentication (NOT recomended)](#none-authentication-not-recomended)
23
+ - [Handling session channel requests](#handling-session-channel-requests)
24
+ - [Reference request handlers](#reference-request-handlers)
25
+ - [Custom request handlers](#custom-request-handlers)
26
+ - [Defining preferred algorithms (optional)](#defining-preferred-algorithms-optional)
27
+ - [Demo](#demo)
28
+ - [Supported Features](#supported-features)
29
+ - [Connection layer](#connection-layer)
30
+ - [Authentication layer](#authentication-layer)
31
+ - [Transport layer](#transport-layer)
32
+ - [Contributing](#contributing)
33
+ - [Code of Conduct](#code-of-conduct)
34
+ - [License](#license)
35
+
10
36
  ## Installation
11
37
 
12
38
  Add this line to your application's Gemfile:
@@ -84,6 +110,21 @@ To disable logging, you can un-initialize `HrrRbSsh::Logger`.
84
110
  HrrRbSsh::Logger.uninitialize
85
111
  ```
86
112
 
113
+ #### Registering pre-generated secret keys for server host key
114
+
115
+ By default, server host keys are generated everytime the gem is loaded. To use pre-generated keys, it is possible to register the keys in HrrRbSsh::Transport through `options` variable. The secret key value must be PEM or DER format string. The below is an example of registering ecdsa-sha2-nistp256 secret key. The supported server host key algorithms are listed later in this document.
116
+
117
+ ```ruby
118
+ options['transport_server_secret_host_keys'] = {}
119
+ options['transport_server_secret_host_keys']['ecdsa-sha2-nistp256'] = <<-'EOB'
120
+ -----BEGIN EC PRIVATE KEY-----
121
+ MHcCAQEEIFFtGZHk6A8anZkLCJan9YBlB63uCIN/ZcQNCaJout8loAoGCCqGSM49
122
+ AwEHoUQDQgAEk8m548Xga+XGEmRx7P71xGlxCfgjPj3XVOw+fXPXRgA03a5yDJEp
123
+ OfeosJOO9twerD7pPhmXREkygblPsEXaVA==
124
+ -----END EC PRIVATE KEY-----
125
+ EOB
126
+ ```
127
+
87
128
  #### Defining authentications
88
129
 
89
130
  By default, any authentications get failed. To allow users to login to the SSH service, at least one of the authentication methods must be defined and registered into the instance of HrrRbSsh::Authentication through `options` variable.
@@ -225,16 +266,11 @@ p HrrRbSsh::Transport::EncryptionAlgorithm.list_supported
225
266
  p HrrRbSsh::Transport::EncryptionAlgorithm.list_preferred
226
267
  # => ["aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour"]
227
268
 
228
- p HrrRbSsh::Transport::EncryptionAlgorithm.list_supported
229
- # => ["none", "3des-cbc", "blowfish-cbc", "aes128-cbc", "aes192-cbc", "aes256-cbc", "arcfour", "cast128-cbc", "aes128-ctr", "aes192-ctr", "aes256-ctr"]
230
- HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_preferred
231
- # => ["ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss"]
232
-
233
269
  p HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_supported
234
270
  # => ["ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521"]
235
- p HrrRbSsh::Transport::KexAlgorithm.list_preferred
236
- # => ["ecdh-sha2-nistp521", "ecdh-sha2-nistp384", "ecdh-sha2-nistp256", "diffie-hellman-group18-sha512", "diffie-hellman-group17-sha512", "diffie-hellman-group16-sha512", "diffie-hellman-group15-sha512", "diffie-hellman-group14-sha256", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1"]
237
-
271
+ p HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_preferred
272
+ # => ["ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss"]
273
+
238
274
  p HrrRbSsh::Transport::KexAlgorithm.list_supported
239
275
  # => ["diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", "diffie-hellman-group15-sha512", "diffie-hellman-group16-sha512", "diffie-hellman-group17-sha512", "diffie-hellman-group18-sha512", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521"]
240
276
  p HrrRbSsh::Transport::KexAlgorithm.list_preferred
data/demo/server.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
+ require 'etc'
4
5
  require 'logger'
5
6
  require 'socket'
6
7
 
@@ -27,10 +28,14 @@ auth_none = HrrRbSsh::Authentication::Authenticator.new { |context|
27
28
  false
28
29
  }
29
30
  auth_publickey = HrrRbSsh::Authentication::Authenticator.new { |context|
30
- username = ENV['USER']
31
- authorized_keys = HrrRbSsh::Compat::OpenSSH::AuthorizedKeys.new(File.read(File.join(Dir.home, '.ssh', 'authorized_keys')))
32
- authorized_keys.any?{ |public_key|
33
- context.verify username, public_key.algorithm_name, public_key.to_pem
31
+ users = ['user1', 'user2']
32
+ users.any?{ |username|
33
+ passwd = Etc.getpwnam(username)
34
+ homedir = passwd.dir
35
+ authorized_keys = HrrRbSsh::Compat::OpenSSH::AuthorizedKeys.new(File.read(File.join(homedir, '.ssh', 'authorized_keys')))
36
+ authorized_keys.any?{ |public_key|
37
+ context.verify username, public_key.algorithm_name, public_key.to_pem
38
+ }
34
39
  }
35
40
  }
36
41
  auth_password = HrrRbSsh::Authentication::Authenticator.new { |context|
@@ -52,6 +57,15 @@ options['transport_preferred_kex_algorithms'] = tran_preferred_kex_a
52
57
  options['transport_preferred_mac_algorithms'] = tran_preferred_mac_algorithms
53
58
  options['transport_preferred_compression_algorithms'] = tran_preferred_compression_algorithms
54
59
 
60
+ options['transport_server_secret_host_keys'] = {}
61
+ options['transport_server_secret_host_keys']['ecdsa-sha2-nistp256'] = <<-'EOB'
62
+ -----BEGIN EC PRIVATE KEY-----
63
+ MHcCAQEEIFFtGZHk6A8anZkLCJan9YBlB63uCIN/ZcQNCaJout8loAoGCCqGSM49
64
+ AwEHoUQDQgAEk8m548Xga+XGEmRx7P71xGlxCfgjPj3XVOw+fXPXRgA03a5yDJEp
65
+ OfeosJOO9twerD7pPhmXREkygblPsEXaVA==
66
+ -----END EC PRIVATE KEY-----
67
+ EOB
68
+
55
69
  options['authentication_none_authenticator'] = auth_none
56
70
  options['authentication_publickey_authenticator'] = auth_publickey
57
71
  options['authentication_password_authenticator'] = auth_password
@@ -35,28 +35,27 @@ module HrrRbSsh
35
35
  }.join
36
36
  end
37
37
 
38
- def decode payload, complementary_message={}
39
- def decode_recursively payload_io, message=nil
40
- if message.class == Array and message.size == 0
41
- []
42
- else
43
- definition = case message
44
- when nil
45
- common_definition
46
- when Array
47
- conditional_definition(message)
48
- end
49
- decoded_message = definition.map{ |data_type, field_name|
50
- [
51
- field_name,
52
- data_type.decode( payload_io )
53
- ]
54
- }
55
-
56
- decoded_message + decode_recursively(payload_io, decoded_message)
57
- end
38
+ def decode_recursively payload_io, message=nil
39
+ if message.class == Array and message.size == 0
40
+ []
41
+ else
42
+ definition = case message
43
+ when nil
44
+ common_definition
45
+ when Array
46
+ conditional_definition(message)
47
+ end
48
+ decoded_message = definition.map{ |data_type, field_name|
49
+ [
50
+ field_name,
51
+ data_type.decode( payload_io )
52
+ ]
53
+ }
54
+ decoded_message + decode_recursively(payload_io, decoded_message)
58
55
  end
56
+ end
59
57
 
58
+ def decode payload, complementary_message={}
60
59
  payload_io = StringIO.new payload
61
60
  decoded_message = decode_recursively(payload_io).to_h
62
61
  if complementary_message.any?
@@ -66,6 +66,13 @@ module HrrRbSsh
66
66
  end
67
67
 
68
68
  def wait_until_senders_closed
69
+ [@w_io_out, @w_io_err].each{ |io|
70
+ begin
71
+ io.close
72
+ rescue IOError # for compatibility for Ruby version < 2.3
73
+ Thread.pass
74
+ end
75
+ }
69
76
  [@out_sender_thread, @err_sender_thread].select{ |t| t.instance_of? Thread }.each(&:join)
70
77
  end
71
78
 
@@ -163,7 +170,7 @@ module HrrRbSsh
163
170
  break
164
171
  end
165
172
  begin
166
- data = @r_io_out.readpartial(1024)
173
+ data = @r_io_out.readpartial(10240)
167
174
  sendable_size = [data.size, @remote_window_size].min
168
175
  sending_data = data[0, sendable_size]
169
176
  send_channel_data sending_data if sendable_size > 0
@@ -195,7 +202,7 @@ module HrrRbSsh
195
202
  break
196
203
  end
197
204
  begin
198
- data = @r_io_err.readpartial(1024)
205
+ data = @r_io_err.readpartial(10240)
199
206
  sendable_size = [data.size, @remote_window_size].min
200
207
  sending_data = data[0, sendable_size]
201
208
  send_channel_extended_data sending_data if sendable_size > 0
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
+ require 'etc'
4
5
  require 'hrr_rb_ssh/logger'
5
6
  require 'hrr_rb_ssh/connection/request_handler'
6
7
 
@@ -11,15 +12,124 @@ module HrrRbSsh
11
12
  def initialize
12
13
  @logger = HrrRbSsh::Logger.new self.class.name
13
14
  @proc = Proc.new { |context|
15
+ ptm = context.vars[:ptm]
16
+ pts = context.vars[:pts]
17
+
14
18
  context.chain_proc { |chain|
19
+ passwd = Etc.getpwnam(context.username)
20
+
21
+ env = context.vars.fetch(:env, Hash.new)
22
+ env['USER'] = passwd.name
23
+ env['HOME'] = passwd.dir
24
+ env['SHELL'] = passwd.shell
25
+
26
+ program = context.command
27
+
28
+ args = Array.new
29
+
30
+ options = Hash.new
31
+ options[:unsetenv_others] = true
32
+ options[:close_others] = true
33
+ unless ptm
34
+ options[:in] = context.io[0]
35
+ options[:out] = context.io[1]
36
+ options[:err] = context.io[2]
37
+ end
38
+
15
39
  pid = fork do
16
40
  Process.setsid
17
- context.vars[:env] ||= Hash.new
18
- exec context.vars[:env], context.command, in: context.io[0], out: context.io[1], err: context.io[2]
41
+ Dir.chdir passwd.dir
42
+ Process.gid = passwd.gid
43
+ Process.egid = passwd.gid
44
+ Process.uid = passwd.uid
45
+ Process.euid = passwd.uid
46
+ if ptm
47
+ STDIN.reopen pts, 'r'
48
+ STDOUT.reopen pts, 'w'
49
+ STDERR.reopen pts, 'w'
50
+ pts.close
51
+ end
52
+ exec env, program, *args, options
53
+ end
54
+
55
+ unless ptm
56
+ pid, status = Process.waitpid2 pid
57
+ status.exitstatus
58
+ else
59
+ pts.close
60
+
61
+ ptm_read_thread = Thread.start {
62
+ loop do
63
+ begin
64
+ context.io[1].write ptm.readpartial(10240)
65
+ rescue EOFError => e
66
+ context.logger.info { "ptm is EOF in ptm_read_thread" }
67
+ break
68
+ rescue IOError => e
69
+ context.logger.warn { "IO Error in ptm_read_thread" }
70
+ break
71
+ rescue Errno::EIO => e
72
+ context.logger.info { "EIO Error in ptm_read_thread" }
73
+ break
74
+ rescue => e
75
+ context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
76
+ break
77
+ end
78
+ end
79
+ }
80
+ ptm_write_thread = Thread.start {
81
+ loop do
82
+ begin
83
+ ptm.write context.io[0].readpartial(10240)
84
+ rescue EOFError => e
85
+ context.logger.info { "IO is EOF in ptm_write_thread" }
86
+ break
87
+ rescue IOError => e
88
+ context.logger.warn { "IO Error in ptm_write_thread" }
89
+ break
90
+ rescue Errno::EIO => e
91
+ context.logger.info { "EIO Error in ptm_read_thread" }
92
+ break
93
+ rescue => e
94
+ context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
95
+ break
96
+ end
97
+ end
98
+ }
99
+
100
+ begin
101
+ pid, status = Process.waitpid2 pid
102
+ context.logger.info { "program exited with status #{status.inspect}" }
103
+ status.exitstatus
104
+ ensure
105
+ unless status
106
+ context.logger.info { "exiting program" }
107
+ Process.kill :TERM, pid
108
+ begin
109
+ Timeout.timeout(1) do
110
+ pid, status = Process.waitpid2 pid
111
+ end
112
+ rescue Timeout::Error
113
+ context.logger.warn { "force exiting program" }
114
+ Process.kill :KILL, pid
115
+ pid, status = Process.waitpid2 pid
116
+ end
117
+ context.logger.info { "program exited with status #{status.inspect}" }
118
+ end
119
+ begin
120
+ ptm_read_thread.join
121
+ rescue => e
122
+ context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
123
+ end
124
+ begin
125
+ ptm_write_thread.exit
126
+ ptm_write_thread.join
127
+ rescue => e
128
+ context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
129
+ end
130
+ context.logger.info { "proc chain finished" }
131
+ end
19
132
  end
20
- context.io.each{ |io| io.close }
21
- pid, status = Process.waitpid2 pid
22
- status.exitstatus
23
133
  }
24
134
  }
25
135
  end
@@ -1,6 +1,8 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
+ require 'etc'
5
+ require 'fileutils'
4
6
  require 'pty'
5
7
  require 'io/console'
6
8
  require 'hrr_rb_ssh/logger'
@@ -13,20 +15,44 @@ module HrrRbSsh
13
15
  def initialize
14
16
  @logger = HrrRbSsh::Logger.new self.class.name
15
17
  @proc = Proc.new { |context|
16
- ptm, pts = PTY.open
17
- ptm.winsize = [context.terminal_height_rows, context.terminal_width_characters]
18
- context.vars[:ptm] = ptm
19
- context.vars[:pts] = pts
20
- context.vars[:env] ||= Hash.new
21
- context.vars[:env]['TERM'] = context.term_environment_variable_value
22
- context.chain_proc { |chain|
18
+ begin
19
+ ptm, pts = PTY.open
20
+ passwd = Etc.getpwnam(context.username)
21
+ FileUtils.chown passwd.uid, -1, pts
22
+ FileUtils.chmod 'u+rw,g+w', pts
23
+ ptm.winsize = [context.terminal_height_rows, context.terminal_width_characters, context.terminal_width_pixels, context.terminal_height_pixels]
24
+ context.vars[:ptm] = ptm
25
+ context.vars[:pts] = pts
26
+ context.vars[:env] ||= Hash.new
27
+ context.vars[:env]['TERM'] = context.term_environment_variable_value
28
+ context.chain_proc { |chain|
29
+ begin
30
+ chain.call_next
31
+ ensure
32
+ begin
33
+ context.vars[:ptm].close
34
+ rescue
35
+ end
36
+ begin
37
+ context.vars[:pts].close
38
+ rescue
39
+ end
40
+ end
41
+ }
42
+ rescue => e
23
43
  begin
24
- chain.call_next
25
- ensure
26
- context.vars[:ptm].close
27
- context.vars[:pts].close
44
+ ptm.close
45
+ rescue
28
46
  end
29
- }
47
+ begin
48
+ pts.close
49
+ rescue
50
+ end
51
+ context.chain_proc{ |chain|
52
+ exitstatus = 1
53
+ }
54
+ raise e
55
+ end
30
56
  }
31
57
  end
32
58
  end
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
+ require 'etc'
4
5
  require 'timeout'
5
6
  require 'hrr_rb_ssh/logger'
6
7
  require 'hrr_rb_ssh/connection/request_handler'
@@ -15,56 +16,76 @@ module HrrRbSsh
15
16
  ptm = context.vars[:ptm]
16
17
  pts = context.vars[:pts]
17
18
 
18
- context.io[2].close # never use err output in shell handler
19
-
20
19
  context.chain_proc { |chain|
20
+ passwd = Etc.getpwnam(context.username)
21
+
22
+ env = context.vars.fetch(:env, Hash.new)
23
+ env['USER'] = passwd.name
24
+ env['HOME'] = passwd.dir
25
+ env['SHELL'] = passwd.shell
26
+
27
+ program = [passwd.shell, passwd.shell.split('/').last.sub(/^/,'-')]
28
+
29
+ args = Array.new
30
+
31
+ options = Hash.new
32
+ options[:unsetenv_others] = true
33
+ options[:close_others] = true
34
+
21
35
  pid = fork do
22
36
  ptm.close
23
37
  Process.setsid
38
+ Dir.chdir passwd.dir
39
+ Process.gid = passwd.gid
40
+ Process.egid = passwd.gid
41
+ Process.uid = passwd.uid
42
+ Process.euid = passwd.uid
24
43
  STDIN.reopen pts, 'r'
25
44
  STDOUT.reopen pts, 'w'
26
45
  STDERR.reopen pts, 'w'
27
46
  pts.close
28
- context.vars[:env] ||= Hash.new
29
- exec context.vars[:env], 'login', '-pf', context.username
47
+ exec env, program, *args, options
30
48
  end
31
49
 
32
50
  pts.close
33
51
 
34
- threads = []
35
- threads.push Thread.start {
52
+ ptm_read_thread = Thread.start {
36
53
  loop do
37
54
  begin
38
- context.io[1].write ptm.readpartial(1024)
55
+ context.io[1].write ptm.readpartial(10240)
39
56
  rescue EOFError => e
40
- context.logger.info { "ptm is EOF" }
57
+ context.logger.info { "ptm is EOF in ptm_read_thread" }
41
58
  break
42
59
  rescue IOError => e
43
- context.logger.warn { "IO is closed" }
60
+ context.logger.warn { "IO Error in ptm_read_thread" }
61
+ break
62
+ rescue Errno::EIO => e
63
+ context.logger.info { "EIO Error in ptm_read_thread" }
44
64
  break
45
65
  rescue => e
46
66
  context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
47
67
  break
48
68
  end
49
69
  end
50
- context.io[1].close
51
70
  }
52
- threads.push Thread.start {
71
+ ptm_write_thread = Thread.start {
53
72
  loop do
54
73
  begin
55
- ptm.write context.io[0].readpartial(1024)
74
+ ptm.write context.io[0].readpartial(10240)
56
75
  rescue EOFError => e
57
- context.logger.info { "IO is EOF" }
76
+ context.logger.info { "IO is EOF in ptm_write_thread" }
58
77
  break
59
78
  rescue IOError => e
60
- context.logger.warn { "IO is closed" }
79
+ context.logger.warn { "IO Error in ptm_write_thread" }
80
+ break
81
+ rescue Errno::EIO => e
82
+ context.logger.info { "EIO Error in ptm_read_thread" }
61
83
  break
62
84
  rescue => e
63
85
  context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
64
86
  break
65
87
  end
66
88
  end
67
- ptm.close
68
89
  }
69
90
 
70
91
  begin
@@ -86,13 +107,16 @@ module HrrRbSsh
86
107
  end
87
108
  context.logger.info { "shell exited with status #{status.inspect}" }
88
109
  end
89
- threads.each do |t|
90
- begin
91
- t.exit
92
- t.join
93
- rescue => e
94
- context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
95
- end
110
+ begin
111
+ ptm_read_thread.join
112
+ rescue => e
113
+ context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
114
+ end
115
+ begin
116
+ ptm_write_thread.exit
117
+ ptm_write_thread.join
118
+ rescue => e
119
+ context.logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
96
120
  end
97
121
  context.logger.info { "proc chain finished" }
98
122
  end
@@ -12,7 +12,7 @@ module HrrRbSsh
12
12
  def initialize
13
13
  @logger = HrrRbSsh::Logger.new self.class.name
14
14
  @proc = Proc.new { |context|
15
- context.vars[:ptm].winsize = [context.terminal_height_rows, context.terminal_width_columns]
15
+ context.vars[:ptm].winsize = [context.terminal_height_rows, context.terminal_width_columns, context.terminal_width_pixels, context.terminal_height_pixels]
16
16
  }
17
17
  end
18
18
  end
@@ -0,0 +1,12 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'securerandom'
5
+ require 'openssl'
6
+
7
+ module HrrRbSsh
8
+ module OpenSslSecureRandom
9
+ N_BYTES = 1024
10
+ OpenSSL::Random.seed SecureRandom.random_bytes(N_BYTES)
11
+ end
12
+ end
@@ -53,6 +53,7 @@ module HrrRbSsh
53
53
  def initialize io, mode, options={}
54
54
  @io = io
55
55
  @mode = mode
56
+ @options = options
56
57
 
57
58
  @logger = HrrRbSsh::Logger.new self.class.name
58
59
 
@@ -76,7 +77,7 @@ module HrrRbSsh
76
77
  @acceptable_services = Array.new
77
78
 
78
79
  update_supported_algorithms
79
- update_preferred_algorithms options
80
+ update_preferred_algorithms
80
81
  initialize_local_algorithms
81
82
  initialize_algorithms
82
83
  end
@@ -185,6 +186,8 @@ module HrrRbSsh
185
186
  @logger.info { "close transport" }
186
187
  @closed = true
187
188
  disconnect
189
+ @incoming_compression_algorithm.close
190
+ @outgoing_compression_algorithm.close
188
191
  @logger.info { "transport closed" }
189
192
  end
190
193
 
@@ -254,12 +257,12 @@ module HrrRbSsh
254
257
  @supported_compression_algorithms = HrrRbSsh::Transport::CompressionAlgorithm.list_supported
255
258
  end
256
259
 
257
- def update_preferred_algorithms options
258
- @preferred_kex_algorithms = options['transport_preferred_kex_algorithms'] || HrrRbSsh::Transport::KexAlgorithm.list_preferred
259
- @preferred_server_host_key_algorithms = options['transport_preferred_server_host_key_algorithms'] || HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_preferred
260
- @preferred_encryption_algorithms = options['transport_preferred_encryption_algorithms'] || HrrRbSsh::Transport::EncryptionAlgorithm.list_preferred
261
- @preferred_mac_algorithms = options['transport_preferred_mac_algorithms'] || HrrRbSsh::Transport::MacAlgorithm.list_preferred
262
- @preferred_compression_algorithms = options['transport_preferred_compression_algorithms'] || HrrRbSsh::Transport::CompressionAlgorithm.list_preferred
260
+ def update_preferred_algorithms
261
+ @preferred_kex_algorithms = @options['transport_preferred_kex_algorithms'] || HrrRbSsh::Transport::KexAlgorithm.list_preferred
262
+ @preferred_server_host_key_algorithms = @options['transport_preferred_server_host_key_algorithms'] || HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_preferred
263
+ @preferred_encryption_algorithms = @options['transport_preferred_encryption_algorithms'] || HrrRbSsh::Transport::EncryptionAlgorithm.list_preferred
264
+ @preferred_mac_algorithms = @options['transport_preferred_mac_algorithms'] || HrrRbSsh::Transport::MacAlgorithm.list_preferred
265
+ @preferred_compression_algorithms = @options['transport_preferred_compression_algorithms'] || HrrRbSsh::Transport::CompressionAlgorithm.list_preferred
263
266
 
264
267
  check_if_preferred_algorithms_are_supported
265
268
  end
@@ -430,8 +433,9 @@ module HrrRbSsh
430
433
  server_host_key_algorithm_name = @local_server_host_key_algorithms.find{ |a| @remote_server_host_key_algorithms.include? a } or raise
431
434
  end
432
435
 
436
+ server_secret_host_key = @options.fetch('transport_server_secret_host_keys', {}).fetch(server_host_key_algorithm_name, nil)
433
437
  @kex_algorithm = HrrRbSsh::Transport::KexAlgorithm[kex_algorithm_name].new
434
- @server_host_key_algorithm = HrrRbSsh::Transport::ServerHostKeyAlgorithm[server_host_key_algorithm_name].new
438
+ @server_host_key_algorithm = HrrRbSsh::Transport::ServerHostKeyAlgorithm[server_host_key_algorithm_name].new server_secret_host_key
435
439
  end
436
440
 
437
441
  def update_encryption_mac_compression_algorithms
@@ -479,6 +483,8 @@ module HrrRbSsh
479
483
  incoming_compression_algorithm_name = compression_algorithm_c_to_s_name
480
484
  outgoing_compression_algorithm_name = compression_algorithm_s_to_c_name
481
485
  end
486
+ @incoming_compression_algorithm.close
487
+ @outgoing_compression_algorithm.close
482
488
  @incoming_compression_algorithm = HrrRbSsh::Transport::CompressionAlgorithm[incoming_compression_algorithm_name].new Direction::INCOMING
483
489
  @outgoing_compression_algorithm = HrrRbSsh::Transport::CompressionAlgorithm[outgoing_compression_algorithm_name].new Direction::OUTGOING
484
490
  end
@@ -25,6 +25,11 @@ module HrrRbSsh
25
25
  def inflate data
26
26
  @inflator.inflate(data)
27
27
  end
28
+
29
+ def close
30
+ @deflator.close if @deflator && @deflator.closed?.!
31
+ @inflator.close if @inflator && @inflator.closed?.!
32
+ end
28
33
  end
29
34
  end
30
35
  end
@@ -18,6 +18,10 @@ module HrrRbSsh
18
18
  def inflate data
19
19
  data
20
20
  end
21
+
22
+ def close
23
+ nil
24
+ end
21
25
  end
22
26
  end
23
27
  end
@@ -2,6 +2,7 @@
2
2
  # vim: et ts=2 sw=2
3
3
 
4
4
  require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/openssl_secure_random'
5
6
 
6
7
  module HrrRbSsh
7
8
  class Transport
@@ -11,17 +12,11 @@ module HrrRbSsh
11
12
  PREFERENCE = 30
12
13
  DIGEST = 'sha256'
13
14
  IDENTIFIER = 'nistp256'
14
- SECRET_KEY = <<-EOB
15
- -----BEGIN EC PRIVATE KEY-----
16
- MHcCAQEEIB+8vCekxXfgw+Nz10ZykUGaI+X6ftdGG6b2UX2iz7oEoAoGCCqGSM49
17
- AwEHoUQDQgAEt1em9ko6A2kZFFwVtKgQ0xpggZg17EJQmhFz7ObGNsZ8VIFEc0Hg
18
- SpNC6qrqdhUfVAjsF9y5O/3Z/LGh/lNTig==
19
- -----END EC PRIVATE KEY-----
20
- EOB
15
+ SECRET_KEY = OpenSSL::PKey::EC.new('prime256v1').generate_key.to_pem
21
16
 
22
- def initialize
17
+ def initialize secret_key=nil
23
18
  @logger = HrrRbSsh::Logger.new(self.class.name)
24
- @algorithm = OpenSSL::PKey::EC.new SECRET_KEY
19
+ @algorithm = OpenSSL::PKey::EC.new (secret_key || self.class::SECRET_KEY)
25
20
  end
26
21
 
27
22
  def server_public_host_key
@@ -2,6 +2,7 @@
2
2
  # vim: et ts=2 sw=2
3
3
 
4
4
  require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/openssl_secure_random'
5
6
 
6
7
  module HrrRbSsh
7
8
  class Transport
@@ -11,18 +12,11 @@ module HrrRbSsh
11
12
  PREFERENCE = 40
12
13
  DIGEST = 'sha384'
13
14
  IDENTIFIER = 'nistp384'
14
- SECRET_KEY = <<-EOB
15
- -----BEGIN EC PRIVATE KEY-----
16
- MIGkAgEBBDCKZ6ulBka9rUw+gqKiQdVBG6fzH1klswyMrxrzCcfwRfoc5CGnj8e7
17
- emk+IHyUsd6gBwYFK4EEACKhZANiAATnWMWRgfp3DFiBmdT7LunyBk9YIBYqPsrk
18
- Zil+AWvlISusiW2JcZVB+Hz79tyrgzfwp6n6k9r5s31EIGTGf/n7UMwISrUCfcx+
19
- xVrnYV8pOoy+dcUiGb9okf1jc41bLHc=
20
- -----END EC PRIVATE KEY-----
21
- EOB
15
+ SECRET_KEY = OpenSSL::PKey::EC.new('secp384r1').generate_key.to_pem
22
16
 
23
- def initialize
17
+ def initialize secret_key=nil
24
18
  @logger = HrrRbSsh::Logger.new(self.class.name)
25
- @algorithm = OpenSSL::PKey::EC.new SECRET_KEY
19
+ @algorithm = OpenSSL::PKey::EC.new (secret_key || self.class::SECRET_KEY)
26
20
  end
27
21
 
28
22
  def server_public_host_key
@@ -2,6 +2,7 @@
2
2
  # vim: et ts=2 sw=2
3
3
 
4
4
  require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/openssl_secure_random'
5
6
 
6
7
  module HrrRbSsh
7
8
  class Transport
@@ -11,19 +12,11 @@ module HrrRbSsh
11
12
  PREFERENCE = 50
12
13
  DIGEST = 'sha512'
13
14
  IDENTIFIER = 'nistp521'
14
- SECRET_KEY = <<-EOB
15
- -----BEGIN EC PRIVATE KEY-----
16
- MIHcAgEBBEIByLZ82qYoJid43PwFAdhr3mSH7SalBTdrK8H6h4p3RKEisAsVhmVb
17
- Sx+uGtgKVxxZT5s9tjr7W7Aqc6We5Fg9z7igBwYFK4EEACOhgYkDgYYABAFLHJ3H
18
- 6HBJyJFsN2PRsjJyRMfYE57BB8dmZgwTsHuSAXBkj+2g4ucwtF240zAWw6JOYdqE
19
- V5O4BMNxGfYj+0ceKABJ4MgfUXQ3a1cXn8Dk2Q2uibbfVi7tQ7ET4k/A6B9f/Zwq
20
- /zEM5OVWhfyc+vuEg+TfTtTqgVI2zJpLI7+mSjB/5Q==
21
- -----END EC PRIVATE KEY-----
22
- EOB
15
+ SECRET_KEY = OpenSSL::PKey::EC.new('secp521r1').generate_key.to_pem
23
16
 
24
- def initialize
17
+ def initialize secret_key=nil
25
18
  @logger = HrrRbSsh::Logger.new(self.class.name)
26
- @algorithm = OpenSSL::PKey::EC.new SECRET_KEY
19
+ @algorithm = OpenSSL::PKey::EC.new (secret_key || self.class::SECRET_KEY)
27
20
  end
28
21
 
29
22
  def server_public_host_key
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'hrr_rb_ssh/logger'
5
5
  require 'hrr_rb_ssh/data_type'
6
+ require 'hrr_rb_ssh/openssl_secure_random'
6
7
 
7
8
  module HrrRbSsh
8
9
  class Transport
@@ -11,24 +12,11 @@ module HrrRbSsh
11
12
  NAME = 'ssh-dss'
12
13
  PREFERENCE = 10
13
14
  DIGEST = 'sha1'
14
- SECRET_KEY = <<-EOB
15
- -----BEGIN DSA PRIVATE KEY-----
16
- MIIBuwIBAAKBgQD3fQ6cwTtOJpVI0iASOQZxkhwPRNy7UwovQkEK6bXW33HaCebO
17
- PnNiY/rR4uFhjvHRzF8KnC8xk3fNo4ZJQJlaEHv6qySiXHeX1fw/eo/uzM5WafLd
18
- oaRtE2muky1i3FBCiboXDlNcwuA/efsOE5qsGBbk6svw+8pGolHmOZFSvQIVAN2G
19
- ZxtE9Kqqh6z48/VulQZsrh5hAoGAH3191okH8kUwP3dinp5j5YtNzrJ20sBXNNZG
20
- 0aWjtS2xjSjIXjnlkiwhhvcUcCEkUQ507exvSLgf4dyV/V4+nf5Q5zjLztiSMe9D
21
- qhTRIR23lsDu0OdITQihTu+Y4GEvNLUL9r2P1aoF/sde97xzzqmXPKx0UL1nNzcL
22
- dnAdjjMCgYAa1dRvXe65jufPk0kRwhewRSScfg+YK4DOLUYGalsjHZbXtXqHKNpB
23
- YkTlWKMg6QVREN0+5aNY1z1aJAbNboLz55YBnS9tOBYzvsXQF7ZP1ECMO6m4I8DI
24
- wxt35i8hEVOJc+8x/xtmogzbjepar+1UycJQTMjhvqCW7RF4LuepvwIVANELTvnl
25
- MRl/p42OrQzL/chRPvRf
26
- -----END DSA PRIVATE KEY-----
27
- EOB
15
+ SECRET_KEY = OpenSSL::PKey::DSA.new(1024).to_pem
28
16
 
29
- def initialize
17
+ def initialize secret_key=nil
30
18
  @logger = HrrRbSsh::Logger.new(self.class.name)
31
- @dss = OpenSSL::PKey::DSA.new SECRET_KEY
19
+ @dss = OpenSSL::PKey::DSA.new (secret_key || self.class::SECRET_KEY)
32
20
  end
33
21
 
34
22
  def server_public_host_key
@@ -2,6 +2,7 @@
2
2
  # vim: et ts=2 sw=2
3
3
 
4
4
  require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/openssl_secure_random'
5
6
 
6
7
  module HrrRbSsh
7
8
  class Transport
@@ -10,39 +11,11 @@ module HrrRbSsh
10
11
  NAME = 'ssh-rsa'
11
12
  PREFERENCE = 20
12
13
  DIGEST = 'sha1'
13
- SECRET_KEY = <<-EOB
14
- -----BEGIN RSA PRIVATE KEY-----
15
- MIIEpAIBAAKCAQEA71zHt9RvbXmxuOCWPKR65iBHO+a8M7Mfo4vRCs/dorZN7XL1
16
- lYwjclvo0X1T39BRX+qJ2m4HB+7Vlef9YF7spYKm6czuSCYmJjD5X+PW5QYSGED1
17
- fFSXwjTdDwJi1OKS4kL0Dd6zcSjlFxfjVLNCyUcix36XgDpoBLBFkDZd5P2ow3J6
18
- WNanBasXrckjCk4M3kFclvmxl1O56bbV9VZq51ZqLjv/ZhOrE3WIPfrJGdZssODa
19
- DnI6tM1puwZGVba9VaI8FfnuJcacJ3T9oEoXPY5W+kPZAw6dOARXnJTg+oZk/dBD
20
- Bgej0aMO+1XM7HKz5BiqbhGGSXGas5zoefHbNwIDAQABAoIBAQDP2aQ/2EOuL8eI
21
- /9TV8goafRr+RB1XU4r8zHOIzPnryhyfPX1OEDPToUXpa8gCiPWwsYxlVbfbRqTH
22
- mHzoS2V5T5u7WE3t7tqfvVU+1C0OERhzYS0KeraRWLBA0VSbAeiEe5lL1f/CGr3c
23
- MM0iBsvO1mu4ChBqs80RjTPKx7r/FStpWtqWN4kn+Bhj06qCqhftnudZdYFTHa/G
24
- ia4YWOUH6dSIZKpE7oG53Gm/2ZdK2YiAgMOdrTQkvRzxuIa/RHaETj21hKpetmI7
25
- TfS26RbU2t1Bf/fdFhtTqoAz+CrZEH7Z407ZO45fdc31zJAFIK2Zf3CDVnKwih3t
26
- O0bEVSSpAoGBAP/zEWaTivdQtcemMRhFQBySgnStov+dsxnGBnTkWxVIU7VoFgyg
27
- mgNRlWUxMf12mlfqBVRpx0/ALggHf5KFmbAZ+3qvKSLmfIVM5E9l5NKbZnCWtIqq
28
- 1DN9kHPPOZn3uYvOs9Cpn7S6sa+rVZ82Mg8EZMsPesvFMOjrgNbMQxt7AoGBAO9o
29
- 38VM0+M09sAgOhmqv+Esa2gUGw5n18o/fdmlZdnA+D2ntgr70AD6JUCSYrZgTJRq
30
- HNMuKrbD6HyaPjVaxYJVCFJIcfV+nViZdE8cHh9WXQ/JP/T6nvNajCC8StvoQg4I
31
- vAZFTzChoe2yrOsWXezn9QAecQ8L2WHDLImpayR1AoGADoc1jaUCVld2egas8ru7
32
- j+OhFA5nGitRZz0eULRFl0eruLhXyA+1rkqLOFs6gzCgQi0+cDQw5A38jugeDasX
33
- ti9DXwtiQmDi4I4kx3z5KBs6DVoAlX5s3R9be7dfhaXSGmV5P3bhYdjXDSmkio0A
34
- +mk9b2lJhxeCVzZG8epWRNECgYB2KzGoVQ+Q6ieRFVcYLCuhnSc2rBXeumrMrSIV
35
- N4paPOFKrWkxarF0igOxJ5AJrOafqvCnW/ZBV9l9BzUFaNRsTERbON7m6aQIg1Xh
36
- ZmOH3Dz6+b7T0JB8VYks70OT38Qa4TzNa5B21JD0nmizcMrTkHphoKT1ZEfb9VYa
37
- bMExsQKBgQDoSpo/ZP8+dwR1A/gcu2K5Ie47c3WgKw7qQMarxqzTeS8Xu6/KAn+J
38
- Ka2zIvoHhxlhXFBRhp+FIaFlYRR38gHeNxCoUylpboCUyMkHOsOP43AiKsmbNK20
39
- vzTNM3SFzgt3bHkdEtDLc64aoBX+dHOot6u71XLZrshnHPtiZ0C/ZA==
40
- -----END RSA PRIVATE KEY-----
41
- EOB
14
+ SECRET_KEY = OpenSSL::PKey::RSA.new(2048).to_pem
42
15
 
43
- def initialize
16
+ def initialize secret_key=nil
44
17
  @logger = HrrRbSsh::Logger.new(self.class.name)
45
- @rsa = OpenSSL::PKey::RSA.new SECRET_KEY
18
+ @rsa = OpenSSL::PKey::RSA.new (secret_key || self.class::SECRET_KEY)
46
19
  end
47
20
 
48
21
  def server_public_host_key
@@ -2,5 +2,5 @@
2
2
  # vim: et ts=2 sw=2
3
3
 
4
4
  module HrrRbSsh
5
- VERSION = "0.1.8"
5
+ VERSION = "0.1.9"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hrr_rb_ssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - hirura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-01 00:00:00.000000000 Z
11
+ date: 2018-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -193,6 +193,7 @@ files:
193
193
  - lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb
194
194
  - lib/hrr_rb_ssh/message/099_ssh_msg_channel_success.rb
195
195
  - lib/hrr_rb_ssh/message/100_ssh_msg_channel_failure.rb
196
+ - lib/hrr_rb_ssh/openssl_secure_random.rb
196
197
  - lib/hrr_rb_ssh/subclass_with_preference_listable.rb
197
198
  - lib/hrr_rb_ssh/transport.rb
198
199
  - lib/hrr_rb_ssh/transport/compression_algorithm.rb