hrr_rb_ssh 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/demo/echo_server.rb +67 -0
- data/demo/server.rb +38 -7
- data/hrr_rb_ssh.gemspec +5 -5
- data/lib/hrr_rb_ssh/authentication/method/publickey/context.rb +63 -0
- data/lib/hrr_rb_ssh/authentication/method/publickey/ssh_rsa.rb +116 -0
- data/lib/hrr_rb_ssh/authentication/method/publickey.rb +68 -0
- data/lib/hrr_rb_ssh/authentication/method.rb +1 -0
- data/lib/hrr_rb_ssh/authentication.rb +16 -6
- data/lib/hrr_rb_ssh/connection/channel.rb +28 -21
- data/lib/hrr_rb_ssh/connection.rb +20 -8
- data/lib/hrr_rb_ssh/message/001_ssh_msg_disconnect.rb +1 -1
- data/lib/hrr_rb_ssh/message/002_ssh_msg_ignore.rb +1 -1
- data/lib/hrr_rb_ssh/message/003_ssh_msg_unimplemented.rb +1 -1
- data/lib/hrr_rb_ssh/message/004_ssh_msg_debug.rb +1 -1
- data/lib/hrr_rb_ssh/message/005_ssh_msg_service_request.rb +1 -1
- data/lib/hrr_rb_ssh/message/006_ssh_msg_service_accept.rb +1 -1
- data/lib/hrr_rb_ssh/message/020_ssh_msg_kexinit.rb +1 -1
- data/lib/hrr_rb_ssh/message/021_ssh_msg_newkeys.rb +1 -1
- data/lib/hrr_rb_ssh/message/030_ssh_msg_kexdh_init.rb +1 -1
- data/lib/hrr_rb_ssh/message/031_ssh_msg_kexdh_reply.rb +1 -1
- data/lib/hrr_rb_ssh/message/050_ssh_msg_userauth_request.rb +1 -1
- data/lib/hrr_rb_ssh/message/051_ssh_msg_userauth_failure.rb +1 -1
- data/lib/hrr_rb_ssh/message/052_ssh_msg_userauth_success.rb +1 -1
- data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_pk_ok.rb +1 -1
- data/lib/hrr_rb_ssh/message/080_ssh_msg_global_request.rb +1 -1
- data/lib/hrr_rb_ssh/message/081_ssh_msg_request_success.rb +1 -1
- data/lib/hrr_rb_ssh/message/082_ssh_msg_request_failure.rb +1 -1
- data/lib/hrr_rb_ssh/message/090_ssh_msg_channel_open.rb +1 -1
- data/lib/hrr_rb_ssh/message/091_ssh_msg_channel_open_confirmation.rb +1 -1
- data/lib/hrr_rb_ssh/message/092_ssh_msg_channel_open_failure.rb +1 -1
- data/lib/hrr_rb_ssh/message/093_ssh_msg_channel_window_adjust.rb +1 -1
- data/lib/hrr_rb_ssh/message/094_ssh_msg_channel_data.rb +1 -1
- data/lib/hrr_rb_ssh/message/095_ssh_msg_channel_extended_data.rb +1 -1
- data/lib/hrr_rb_ssh/message/096_ssh_msg_channel_eof.rb +1 -1
- data/lib/hrr_rb_ssh/message/097_ssh_msg_channel_close.rb +1 -1
- data/lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb +1 -1
- data/lib/hrr_rb_ssh/message/099_ssh_msg_channel_success.rb +1 -1
- data/lib/hrr_rb_ssh/message/100_ssh_msg_channel_failure.rb +1 -1
- data/lib/hrr_rb_ssh/transport.rb +9 -9
- data/lib/hrr_rb_ssh/version.rb +1 -1
- metadata +12 -9
- data/bin/console +0 -14
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af60a04fe1efba55f720ee23fd252e624d3c2ba955f36462e5ef766f6ad8acaa
|
4
|
+
data.tar.gz: 5be5463dbc89b27652abd85ad6e4ef5d87f0639f8a32df370b0677d20dcbb39b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 500b7e3562f9d413c9698cc167e51e3c2635b4658a6667ddfbf0d717aec68711aa81f51df85b203d7a26e42f62b9d5bc4e11a2c86b2f81594f5458b6b64f5790
|
7
|
+
data.tar.gz: cc7bcc38e495f362dec7bbfe7fdae03c743945e1093b09ae1122f0cd27a43c4ba274935cd390120b6a845307d3340d033e04ab94722cd79a66985687ae5f0b28
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[](https://travis-ci.org/hirura/hrr_rb_ssh)
|
4
4
|
[](https://codeclimate.com/github/hirura/hrr_rb_ssh/maintainability)
|
5
5
|
[](https://codeclimate.com/github/hirura/hrr_rb_ssh/test_coverage)
|
6
|
+
[](https://badge.fury.io/rb/hrr_rb_ssh)
|
6
7
|
|
7
8
|
hrr_rb_ssh is a pure Ruby SSH2 server implementation.
|
8
9
|
|
data/demo/echo_server.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'logger'
|
5
|
+
require 'pty'
|
6
|
+
require 'socket'
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'hrr_rb_ssh'
|
10
|
+
rescue LoadError
|
11
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
12
|
+
require 'hrr_rb_ssh'
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
logger = Logger.new STDOUT
|
17
|
+
logger.level = Logger::INFO
|
18
|
+
HrrRbSsh::Logger.initialize logger
|
19
|
+
|
20
|
+
|
21
|
+
auth_password = HrrRbSsh::Authentication::Authenticator.new { |context|
|
22
|
+
user_and_pass = [
|
23
|
+
['user1', 'password1'],
|
24
|
+
['user2', 'password2'],
|
25
|
+
]
|
26
|
+
user_and_pass.any? { |user, pass|
|
27
|
+
context.verify user, pass
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
conn_echo = HrrRbSsh::Connection::RequestHandler.new { |context|
|
32
|
+
context.chain_proc { |chain|
|
33
|
+
begin
|
34
|
+
loop do
|
35
|
+
buf = context.io.readpartial(10240)
|
36
|
+
break if buf.include?(0x04.chr) # break if ^D
|
37
|
+
context.io.write buf
|
38
|
+
end
|
39
|
+
exitstatus = 0
|
40
|
+
rescue => e
|
41
|
+
logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
42
|
+
exitstatus = 1
|
43
|
+
end
|
44
|
+
exitstatus
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
options = {}
|
49
|
+
options['authentication_password_authenticator'] = auth_password
|
50
|
+
options['connection_channel_request_shell'] = conn_echo
|
51
|
+
|
52
|
+
|
53
|
+
server = TCPServer.new 10022
|
54
|
+
while true
|
55
|
+
t = Thread.new(server.accept) do |io|
|
56
|
+
begin
|
57
|
+
tran = HrrRbSsh::Transport.new io, HrrRbSsh::Transport::Mode::SERVER
|
58
|
+
auth = HrrRbSsh::Authentication.new tran, options
|
59
|
+
conn = HrrRbSsh::Connection.new auth, options
|
60
|
+
conn.start
|
61
|
+
rescue => e
|
62
|
+
logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
63
|
+
ensure
|
64
|
+
io.close
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/demo/server.rb
CHANGED
@@ -18,11 +18,25 @@ logger.level = Logger::INFO
|
|
18
18
|
HrrRbSsh::Logger.initialize logger
|
19
19
|
|
20
20
|
|
21
|
-
options = {}
|
22
|
-
|
23
21
|
auth_none = HrrRbSsh::Authentication::Authenticator.new { |context|
|
24
22
|
false
|
25
23
|
}
|
24
|
+
auth_publickey = HrrRbSsh::Authentication::Authenticator.new { |context|
|
25
|
+
username = 'user1'
|
26
|
+
public_key_algorithm_name = 'ssh-rsa'
|
27
|
+
public_key = <<-'EOB'
|
28
|
+
-----BEGIN PUBLIC KEY-----
|
29
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3OnIQcRTdeTZFjhGcx8f
|
30
|
+
ssCgeqzY47p5KhT/gKMz2nOANNLCBr9e6IGaRePew03St3Cn0ApikuGzPnWxSlBT
|
31
|
+
H6OpR/EnUmBttlvcL28CGOsZIwYJtAdVsGXpIXtiPLl2eEzaM9aBsS/LGWKgQNo3
|
32
|
+
86UGa5j20yGJfsL9WIMCVoGvsA06+4VX1/zlWXwVJSNep674bmSWPcVtXWWZIk19
|
33
|
+
T6b+xuqhfiUpbc/stfdmgDc3B/ZgpFsQh5oWBoAfkL6kAEa4oQBFhqF0QM5ej6h5
|
34
|
+
wqbQt4paM0aEuypWE+CaizA0I+El7f0y+59sUqTAN/7F9UlXaOBdd9SZkhACBrAR
|
35
|
+
nQIDAQAB
|
36
|
+
-----END PUBLIC KEY-----
|
37
|
+
EOB
|
38
|
+
context.verify username, public_key_algorithm_name, public_key
|
39
|
+
}
|
26
40
|
auth_password = HrrRbSsh::Authentication::Authenticator.new { |context|
|
27
41
|
user_and_pass = [
|
28
42
|
['user1', 'password1'],
|
@@ -114,12 +128,29 @@ conn_shell = HrrRbSsh::Connection::RequestHandler.new { |context|
|
|
114
128
|
status.exitstatus
|
115
129
|
}
|
116
130
|
}
|
131
|
+
conn_exec = HrrRbSsh::Connection::RequestHandler.new { |context|
|
132
|
+
context.chain_proc { |chain|
|
133
|
+
pid = fork do
|
134
|
+
Process.setsid
|
135
|
+
context.vars[:env] ||= Hash.new
|
136
|
+
exec context.vars[:env], context.command, in: context.io, out: context.io, err: context.io
|
137
|
+
end
|
138
|
+
pid, status = Process.waitpid2 pid
|
139
|
+
status.exitstatus
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
options = {}
|
145
|
+
|
146
|
+
options['authentication_none_authenticator'] = auth_none
|
147
|
+
options['authentication_publickey_authenticator'] = auth_publickey
|
148
|
+
options['authentication_password_authenticator'] = auth_password
|
117
149
|
|
118
|
-
options['
|
119
|
-
options['
|
120
|
-
options['
|
121
|
-
options['
|
122
|
-
options['connection_channel_request_shell'] = conn_shell
|
150
|
+
options['connection_channel_request_pty_req'] = conn_pty
|
151
|
+
options['connection_channel_request_env'] = conn_env
|
152
|
+
options['connection_channel_request_shell'] = conn_shell
|
153
|
+
options['connection_channel_request_exec'] = conn_exec
|
123
154
|
|
124
155
|
|
125
156
|
server = TCPServer.new 10022
|
data/hrr_rb_ssh.gemspec
CHANGED
@@ -6,20 +6,20 @@ require "hrr_rb_ssh/version"
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "hrr_rb_ssh"
|
8
8
|
spec.version = HrrRbSsh::VERSION
|
9
|
+
spec.license = 'Apache-2.0'
|
10
|
+
spec.summary = %q{Pure Ruby SSH2 server implementation}
|
11
|
+
spec.description = %q{Pure Ruby SSH2 server implementation}
|
9
12
|
spec.authors = ["hirura"]
|
10
13
|
spec.email = ["hirura@gmail.com"]
|
11
|
-
|
12
|
-
spec.summary = %q{SSH2 protocol implementation}
|
13
|
-
spec.description = %q{SSH2 protocol implementation}
|
14
14
|
spec.homepage = "https://github.com/hirura/hrr_rb_ssh"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
17
|
f.match(%r{^(test|spec|features)/})
|
18
18
|
end
|
19
|
-
spec.bindir = "exe"
|
20
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
19
|
spec.require_paths = ["lib"]
|
22
20
|
|
21
|
+
spec.required_ruby_version = '>= 2.0.0'
|
22
|
+
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.16"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.0"
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'hrr_rb_ssh/logger'
|
5
|
+
|
6
|
+
module HrrRbSsh
|
7
|
+
class Authentication
|
8
|
+
module Method
|
9
|
+
class Publickey
|
10
|
+
class Context
|
11
|
+
attr_reader \
|
12
|
+
:username,
|
13
|
+
:session_id,
|
14
|
+
:message_number,
|
15
|
+
:service_name,
|
16
|
+
:method_name,
|
17
|
+
:with_signature,
|
18
|
+
:public_key_algorithm_name,
|
19
|
+
:public_key_blob,
|
20
|
+
:signature
|
21
|
+
|
22
|
+
def initialize username, algorithm, session_id, message
|
23
|
+
@username = username
|
24
|
+
@algorithm = algorithm
|
25
|
+
@session_id = session_id
|
26
|
+
@message = message
|
27
|
+
|
28
|
+
@message_number = message['message number']
|
29
|
+
@service_name = message['service name']
|
30
|
+
@method_name = message['method name']
|
31
|
+
@with_signature = message['with signature']
|
32
|
+
@public_key_algorithm_name = message['public key algorithm name']
|
33
|
+
@public_key_blob = message['public key blob']
|
34
|
+
@signature = message['signature']
|
35
|
+
end
|
36
|
+
|
37
|
+
def verify username, public_key_algorithm_name, public_key
|
38
|
+
verify_username(username) \
|
39
|
+
&& verify_public_key_algorithm_name(public_key_algorithm_name) \
|
40
|
+
&& verify_public_key(public_key_algorithm_name, public_key) \
|
41
|
+
&& verify_signature
|
42
|
+
end
|
43
|
+
|
44
|
+
def verify_username username
|
45
|
+
username == @username
|
46
|
+
end
|
47
|
+
|
48
|
+
def verify_public_key_algorithm_name public_key_algorithm_name
|
49
|
+
public_key_algorithm_name == @public_key_algorithm_name
|
50
|
+
end
|
51
|
+
|
52
|
+
def verify_public_key public_key_algorithm_name, public_key
|
53
|
+
@algorithm.verify_public_key(public_key_algorithm_name, public_key, @public_key_blob)
|
54
|
+
end
|
55
|
+
|
56
|
+
def verify_signature
|
57
|
+
@algorithm.verify_signature(@session_id, @message)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'hrr_rb_ssh/logger'
|
5
|
+
require 'hrr_rb_ssh/transport/data_type'
|
6
|
+
|
7
|
+
module HrrRbSsh
|
8
|
+
class Authentication
|
9
|
+
module Method
|
10
|
+
class Publickey
|
11
|
+
name_list = [
|
12
|
+
'ssh-rsa'
|
13
|
+
]
|
14
|
+
|
15
|
+
class SshRsa
|
16
|
+
NAME = 'ssh-rsa'
|
17
|
+
DIGEST = 'sha1'
|
18
|
+
|
19
|
+
PUBLIC_KEY_BLOB_DEFINITION = [
|
20
|
+
['string', 'public key algorithm name'],
|
21
|
+
['mpint', 'e'],
|
22
|
+
['mpint', 'n'],
|
23
|
+
]
|
24
|
+
|
25
|
+
SIGNATURE_DEFINITION = [
|
26
|
+
['string', 'public key algorithm name'],
|
27
|
+
['string', 'signature blob'],
|
28
|
+
]
|
29
|
+
|
30
|
+
SIGNATURE_BLOB_DEFINITION = [
|
31
|
+
['string', 'session identifier'],
|
32
|
+
['byte', 'message number'],
|
33
|
+
['string', 'user name'],
|
34
|
+
['string', 'service name'],
|
35
|
+
['string', 'method name'],
|
36
|
+
['boolean', 'with signature'],
|
37
|
+
['string', 'public key algorithm name'],
|
38
|
+
['string', 'public key blob'],
|
39
|
+
]
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@logger = HrrRbSsh::Logger.new self.class.name
|
43
|
+
end
|
44
|
+
|
45
|
+
def encode definition, payload
|
46
|
+
definition.map{ |data_type, field_name|
|
47
|
+
field_value = if payload[field_name].instance_of? ::Proc then payload[field_name].call else payload[field_name] end
|
48
|
+
HrrRbSsh::Transport::DataType[data_type].encode(field_value)
|
49
|
+
}.join
|
50
|
+
end
|
51
|
+
|
52
|
+
def decode definition, payload
|
53
|
+
payload_io = StringIO.new payload, 'r'
|
54
|
+
definition.map{ |data_type, field_name|
|
55
|
+
[
|
56
|
+
field_name,
|
57
|
+
HrrRbSsh::Transport::DataType[data_type].decode(payload_io)
|
58
|
+
]
|
59
|
+
}.to_h
|
60
|
+
end
|
61
|
+
|
62
|
+
def verify_public_key public_key_algorithm_name, public_key, public_key_blob
|
63
|
+
public_key = case public_key
|
64
|
+
when String
|
65
|
+
OpenSSL::PKey::RSA.new(public_key)
|
66
|
+
when OpenSSL::PKey::RSA
|
67
|
+
public_key
|
68
|
+
else
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
public_key_message = {
|
72
|
+
'public key algorithm name' => public_key_algorithm_name,
|
73
|
+
'e' => public_key.e.to_i,
|
74
|
+
'n' => public_key.n.to_i,
|
75
|
+
}
|
76
|
+
public_key_blob == encode(PUBLIC_KEY_BLOB_DEFINITION, public_key_message)
|
77
|
+
end
|
78
|
+
|
79
|
+
def verify_signature session_id, message
|
80
|
+
signature_message = decode SIGNATURE_DEFINITION, message['signature']
|
81
|
+
signature_algorithm = signature_message['public key algorithm name']
|
82
|
+
signature_blob = signature_message['signature blob']
|
83
|
+
|
84
|
+
public_key = decode PUBLIC_KEY_BLOB_DEFINITION, message['public key blob']
|
85
|
+
algorithm = OpenSSL::PKey::RSA.new
|
86
|
+
if algorithm.respond_to?(:set_key)
|
87
|
+
algorithm.set_key public_key['n'], public_key['e'], nil
|
88
|
+
else
|
89
|
+
algorithm.e = public_key['e']
|
90
|
+
algorithm.n = public_key['n']
|
91
|
+
end
|
92
|
+
|
93
|
+
data_message = {
|
94
|
+
'session identifier' => session_id,
|
95
|
+
'message number' => message['message number'],
|
96
|
+
'user name' => message['user name'],
|
97
|
+
'service name' => message['service name'],
|
98
|
+
'method name' => message['method name'],
|
99
|
+
'with signature' => message['with signature'],
|
100
|
+
'public key algorithm name' => message['public key algorithm name'],
|
101
|
+
'public key blob' => message['public key blob'],
|
102
|
+
}
|
103
|
+
data_blob = encode SIGNATURE_BLOB_DEFINITION, data_message
|
104
|
+
|
105
|
+
(signature_algorithm == message['public key algorithm name']) && algorithm.verify(DIGEST, signature_blob, data_blob)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
@@algorithm_list ||= Hash.new
|
110
|
+
name_list.each do |name|
|
111
|
+
@@algorithm_list[name] = SshRsa
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'hrr_rb_ssh/logger'
|
5
|
+
require 'hrr_rb_ssh/authentication/method/publickey/context'
|
6
|
+
require 'hrr_rb_ssh/authentication/method/publickey/ssh_rsa'
|
7
|
+
|
8
|
+
module HrrRbSsh
|
9
|
+
class Authentication
|
10
|
+
module Method
|
11
|
+
name_list = [
|
12
|
+
'publickey'
|
13
|
+
]
|
14
|
+
|
15
|
+
class Publickey
|
16
|
+
@@algorithm_list ||= Hash.new
|
17
|
+
|
18
|
+
def self.[] key
|
19
|
+
@@algorithm_list[key]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.algorithm_name_list
|
23
|
+
@@algorithm_list.keys
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize options
|
27
|
+
@logger = HrrRbSsh::Logger.new self.class.name
|
28
|
+
|
29
|
+
@session_id = options['session id']
|
30
|
+
@authenticator = options.fetch( 'authentication_publickey_authenticator', Authenticator.new { false } )
|
31
|
+
end
|
32
|
+
|
33
|
+
def authenticate userauth_request_message
|
34
|
+
public_key_algorithm_name = userauth_request_message['public key algorithm name']
|
35
|
+
unless @@algorithm_list.has_key?(public_key_algorithm_name)
|
36
|
+
@logger.info("unsupported public key algorithm: #{public_key_algorithm_name}")
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
unless userauth_request_message['with signature']
|
40
|
+
@logger.info("public key algorithm is ok, require signature")
|
41
|
+
public_key_blob = userauth_request_message['public key blob']
|
42
|
+
userauth_pk_ok_message public_key_algorithm_name, public_key_blob
|
43
|
+
else
|
44
|
+
@logger.info("verify signature")
|
45
|
+
username = userauth_request_message['user name']
|
46
|
+
algorithm = @@algorithm_list[public_key_algorithm_name].new
|
47
|
+
context = Context.new(username, algorithm, @session_id, userauth_request_message)
|
48
|
+
@authenticator.authenticate context
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def userauth_pk_ok_message public_key_algorithm_name, public_key_blob
|
53
|
+
message = {
|
54
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_USERAUTH_PK_OK::VALUE,
|
55
|
+
'public key algorithm name from the request' => public_key_algorithm_name,
|
56
|
+
'public key blob from the request' => public_key_blob,
|
57
|
+
}
|
58
|
+
payload = HrrRbSsh::Message::SSH_MSG_USERAUTH_PK_OK.encode message
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
@@list ||= Hash.new
|
63
|
+
name_list.each do |name|
|
64
|
+
@@list[name] = Publickey
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -69,15 +69,21 @@ module HrrRbSsh
|
|
69
69
|
when HrrRbSsh::Message::SSH_MSG_USERAUTH_REQUEST::VALUE
|
70
70
|
userauth_request_message = HrrRbSsh::Message::SSH_MSG_USERAUTH_REQUEST.decode payload
|
71
71
|
method_name = userauth_request_message['method name']
|
72
|
-
method = Method[method_name].new(@options)
|
73
|
-
|
72
|
+
method = Method[method_name].new({'session id' => @transport.session_id}.merge(@options))
|
73
|
+
result = method.authenticate(userauth_request_message)
|
74
|
+
case result
|
75
|
+
when TrueClass
|
76
|
+
@logger.info("verified")
|
74
77
|
send_userauth_success
|
75
78
|
@username = userauth_request_message['user name']
|
76
79
|
@closed = false
|
77
80
|
break
|
78
|
-
|
81
|
+
when FalseClass
|
82
|
+
@logger.info("verify failed")
|
79
83
|
send_userauth_failure
|
80
|
-
|
84
|
+
when String
|
85
|
+
@logger.info("send method specific message to continue")
|
86
|
+
send_method_specific_message result
|
81
87
|
end
|
82
88
|
else
|
83
89
|
@closed = true
|
@@ -88,7 +94,7 @@ module HrrRbSsh
|
|
88
94
|
|
89
95
|
def send_userauth_failure
|
90
96
|
message = {
|
91
|
-
'
|
97
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_USERAUTH_FAILURE::VALUE,
|
92
98
|
'authentications that can continue' => Method.name_list,
|
93
99
|
'partial success' => false,
|
94
100
|
}
|
@@ -98,10 +104,14 @@ module HrrRbSsh
|
|
98
104
|
|
99
105
|
def send_userauth_success
|
100
106
|
message = {
|
101
|
-
'
|
107
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_USERAUTH_SUCCESS::VALUE,
|
102
108
|
}
|
103
109
|
payload = HrrRbSsh::Message::SSH_MSG_USERAUTH_SUCCESS.encode message
|
104
110
|
@transport.send payload
|
105
111
|
end
|
112
|
+
|
113
|
+
def send_method_specific_message payload
|
114
|
+
@transport.send payload
|
115
|
+
end
|
106
116
|
end
|
107
117
|
end
|
@@ -104,20 +104,22 @@ module HrrRbSsh
|
|
104
104
|
begin
|
105
105
|
message = @receive_payload_queue.deq
|
106
106
|
if message.nil? && @receive_payload_queue.closed?
|
107
|
+
@receive_data_queue.close
|
107
108
|
@logger.info("closing channel loop thread")
|
108
109
|
break
|
109
110
|
end
|
110
|
-
|
111
|
+
case message['message number']
|
112
|
+
when HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::VALUE
|
111
113
|
@logger.info("received channel request: #{message['request type']}")
|
112
114
|
request message, variables
|
113
115
|
if message['want reply']
|
114
116
|
send_channel_success
|
115
117
|
end
|
116
|
-
|
118
|
+
when HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA::VALUE
|
117
119
|
@logger.info("received channel data")
|
118
120
|
local_channel = message['recipient channel']
|
119
121
|
@receive_data_queue.enq message['data']
|
120
|
-
|
122
|
+
when HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
|
121
123
|
@logger.debug("received channel window adjust")
|
122
124
|
@remote_window_size = [@remote_window_size + message['bytes to add'], 0xffff_ffff].min
|
123
125
|
else
|
@@ -125,10 +127,10 @@ module HrrRbSsh
|
|
125
127
|
end
|
126
128
|
rescue => e
|
127
129
|
@logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
130
|
+
close from=:channel_loop_thread
|
128
131
|
break
|
129
132
|
end
|
130
133
|
end
|
131
|
-
close from=:channel_loop_thread
|
132
134
|
@logger.info("channel loop thread closed")
|
133
135
|
end
|
134
136
|
end
|
@@ -173,6 +175,9 @@ module HrrRbSsh
|
|
173
175
|
data = @receive_data_queue.deq
|
174
176
|
if data.nil? && @receive_data_queue.closed?
|
175
177
|
@logger.info("closing receiver thread")
|
178
|
+
@logger.info("closing channel IO write")
|
179
|
+
@channel_io.close_write
|
180
|
+
@logger.info("channel IO write closed")
|
176
181
|
break
|
177
182
|
end
|
178
183
|
@channel_io.write data
|
@@ -185,9 +190,11 @@ module HrrRbSsh
|
|
185
190
|
rescue IOError => e
|
186
191
|
@logger.warn("channel IO is closed")
|
187
192
|
close
|
193
|
+
break
|
188
194
|
rescue => e
|
189
195
|
@logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
190
196
|
close
|
197
|
+
break
|
191
198
|
end
|
192
199
|
end
|
193
200
|
@logger.info("receiver thread closed")
|
@@ -217,8 +224,8 @@ module HrrRbSsh
|
|
217
224
|
|
218
225
|
def send_channel_success
|
219
226
|
message = {
|
220
|
-
'
|
221
|
-
'recipient channel'
|
227
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_SUCCESS::VALUE,
|
228
|
+
'recipient channel' => @remote_channel,
|
222
229
|
}
|
223
230
|
payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_SUCCESS.encode message
|
224
231
|
@connection.send payload
|
@@ -226,9 +233,9 @@ module HrrRbSsh
|
|
226
233
|
|
227
234
|
def send_channel_window_adjust
|
228
235
|
message = {
|
229
|
-
'
|
230
|
-
'recipient channel'
|
231
|
-
'bytes to add'
|
236
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE,
|
237
|
+
'recipient channel' => @remote_channel,
|
238
|
+
'bytes to add' => INITIAL_WINDOW_SIZE,
|
232
239
|
}
|
233
240
|
payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.encode message
|
234
241
|
@connection.send payload
|
@@ -236,9 +243,9 @@ module HrrRbSsh
|
|
236
243
|
|
237
244
|
def send_channel_data data
|
238
245
|
message = {
|
239
|
-
'
|
240
|
-
'recipient channel'
|
241
|
-
'data'
|
246
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA::VALUE,
|
247
|
+
'recipient channel' => @remote_channel,
|
248
|
+
'data' => data,
|
242
249
|
}
|
243
250
|
payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA.encode message
|
244
251
|
@connection.send payload
|
@@ -246,11 +253,11 @@ module HrrRbSsh
|
|
246
253
|
|
247
254
|
def send_channel_request_exit_status exitstatus
|
248
255
|
message = {
|
249
|
-
'
|
250
|
-
'recipient channel'
|
251
|
-
'request type'
|
252
|
-
'want reply'
|
253
|
-
'exit status'
|
256
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
|
257
|
+
'recipient channel' => @remote_channel,
|
258
|
+
'request type' => 'exit-status',
|
259
|
+
'want reply' => false,
|
260
|
+
'exit status' => exitstatus,
|
254
261
|
}
|
255
262
|
payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST.encode message
|
256
263
|
@connection.send payload
|
@@ -258,8 +265,8 @@ module HrrRbSsh
|
|
258
265
|
|
259
266
|
def send_channel_eof
|
260
267
|
message = {
|
261
|
-
'
|
262
|
-
'recipient channel'
|
268
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_EOF::VALUE,
|
269
|
+
'recipient channel' => @remote_channel,
|
263
270
|
}
|
264
271
|
payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_EOF.encode message
|
265
272
|
@connection.send payload
|
@@ -267,8 +274,8 @@ module HrrRbSsh
|
|
267
274
|
|
268
275
|
def send_channel_close
|
269
276
|
message = {
|
270
|
-
'
|
271
|
-
'recipient channel'
|
277
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE::VALUE,
|
278
|
+
'recipient channel' => @remote_channel,
|
272
279
|
}
|
273
280
|
payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE.encode message
|
274
281
|
@connection.send payload
|
@@ -74,6 +74,8 @@ module HrrRbSsh
|
|
74
74
|
channel_window_adjust payload
|
75
75
|
when HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA::VALUE
|
76
76
|
channel_data payload
|
77
|
+
when HrrRbSsh::Message::SSH_MSG_CHANNEL_EOF::VALUE
|
78
|
+
channel_eof payload
|
77
79
|
when HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE::VALUE
|
78
80
|
channel_close payload
|
79
81
|
else
|
@@ -129,18 +131,28 @@ module HrrRbSsh
|
|
129
131
|
@channels[local_channel].receive_payload_queue.enq message
|
130
132
|
end
|
131
133
|
|
134
|
+
def channel_eof payload
|
135
|
+
@logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_EOF::ID)
|
136
|
+
message = HrrRbSsh::Message::SSH_MSG_CHANNEL_EOF.decode payload
|
137
|
+
local_channel = message['recipient channel']
|
138
|
+
channel = @channels[local_channel]
|
139
|
+
channel.receive_payload_queue.close
|
140
|
+
end
|
141
|
+
|
132
142
|
def channel_close payload
|
133
143
|
@logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE::ID)
|
134
144
|
message = HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE.decode payload
|
135
145
|
local_channel = message['recipient channel']
|
136
146
|
channel = @channels[local_channel]
|
137
147
|
channel.close
|
148
|
+
@logger.info("deleting channel")
|
138
149
|
@channels.delete local_channel
|
150
|
+
@logger.info("channel deleted")
|
139
151
|
end
|
140
152
|
|
141
153
|
def send_request_success
|
142
154
|
message = {
|
143
|
-
'
|
155
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_REQUEST_SUCCESS::VALUE,
|
144
156
|
}
|
145
157
|
payload = HrrRbSsh::Message::SSH_MSG_REQUEST_SUCCESS.encode message
|
146
158
|
@authentication.send payload
|
@@ -148,7 +160,7 @@ module HrrRbSsh
|
|
148
160
|
|
149
161
|
def send_request_failure
|
150
162
|
message = {
|
151
|
-
'
|
163
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_REQUEST_FAILURE::VALUE,
|
152
164
|
}
|
153
165
|
payload = HrrRbSsh::Message::SSH_MSG_REQUEST_FAILURE.encode message
|
154
166
|
@authentication.send payload
|
@@ -156,12 +168,12 @@ module HrrRbSsh
|
|
156
168
|
|
157
169
|
def send_channel_open_confirmation channel_type, local_channel, remote_channel, initial_window_size, maximum_packet_size
|
158
170
|
message = {
|
159
|
-
'
|
160
|
-
'channel type'
|
161
|
-
'recipient channel'
|
162
|
-
'sender channel'
|
163
|
-
'initial window size'
|
164
|
-
'maximum packet size'
|
171
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE,
|
172
|
+
'channel type' => channel_type,
|
173
|
+
'recipient channel' => remote_channel,
|
174
|
+
'sender channel' => local_channel,
|
175
|
+
'initial window size' => initial_window_size,
|
176
|
+
'maximum packet size' => maximum_packet_size,
|
165
177
|
}
|
166
178
|
payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.encode message
|
167
179
|
@authentication.send payload
|
data/lib/hrr_rb_ssh/transport.rb
CHANGED
@@ -280,10 +280,10 @@ module HrrRbSsh
|
|
280
280
|
|
281
281
|
def send_disconnect
|
282
282
|
message = {
|
283
|
-
|
284
|
-
"reason code"
|
285
|
-
"description"
|
286
|
-
"language tag"
|
283
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_DISCONNECT::VALUE,
|
284
|
+
"reason code" => HrrRbSsh::Message::SSH_MSG_DISCONNECT::ReasonCode::SSH_DISCONNECT_BY_APPLICATION,
|
285
|
+
"description" => "disconnected by user",
|
286
|
+
"language tag" => ""
|
287
287
|
}
|
288
288
|
payload = HrrRbSsh::Message::SSH_MSG_DISCONNECT.encode message
|
289
289
|
@sender.send self, payload
|
@@ -291,7 +291,7 @@ module HrrRbSsh
|
|
291
291
|
|
292
292
|
def send_kexinit
|
293
293
|
message = {
|
294
|
-
'
|
294
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_KEXINIT::VALUE,
|
295
295
|
'cookie (random byte)' => lambda { rand(0x01_00) },
|
296
296
|
'kex_algorithms' => @local_kex_algorithms,
|
297
297
|
'server_host_key_algorithms' => @local_server_host_key_algorithms,
|
@@ -343,7 +343,7 @@ module HrrRbSsh
|
|
343
343
|
|
344
344
|
def send_kexdh_reply
|
345
345
|
message = {
|
346
|
-
'
|
346
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_KEXDH_REPLY::VALUE,
|
347
347
|
'server public host key and certificates (K_S)' => @server_host_key_algorithm.server_public_host_key,
|
348
348
|
'f' => @kex_algorithm.pub_key,
|
349
349
|
'signature of H' => @kex_algorithm.sign(self),
|
@@ -354,7 +354,7 @@ module HrrRbSsh
|
|
354
354
|
|
355
355
|
def send_newkeys
|
356
356
|
message = {
|
357
|
-
'
|
357
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_NEWKEYS::VALUE,
|
358
358
|
}
|
359
359
|
payload = HrrRbSsh::Message::SSH_MSG_NEWKEYS.encode message
|
360
360
|
@sender.send self, payload
|
@@ -376,8 +376,8 @@ module HrrRbSsh
|
|
376
376
|
|
377
377
|
def send_service_accept service_name
|
378
378
|
message = {
|
379
|
-
'
|
380
|
-
'service name'
|
379
|
+
'message number' => HrrRbSsh::Message::SSH_MSG_SERVICE_ACCEPT::VALUE,
|
380
|
+
'service name' => service_name,
|
381
381
|
}
|
382
382
|
payload = HrrRbSsh::Message::SSH_MSG_SERVICE_ACCEPT.encode message
|
383
383
|
@sender.send self, payload
|
data/lib/hrr_rb_ssh/version.rb
CHANGED
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.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hirura
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 1.0.8
|
69
|
-
description: SSH2
|
69
|
+
description: Pure Ruby SSH2 server implementation
|
70
70
|
email:
|
71
71
|
- hirura@gmail.com
|
72
72
|
executables: []
|
@@ -81,8 +81,7 @@ files:
|
|
81
81
|
- LICENSE
|
82
82
|
- README.md
|
83
83
|
- Rakefile
|
84
|
-
-
|
85
|
-
- bin/setup
|
84
|
+
- demo/echo_server.rb
|
86
85
|
- demo/server.rb
|
87
86
|
- hrr_rb_ssh.gemspec
|
88
87
|
- lib/hrr_rb_ssh.rb
|
@@ -93,6 +92,9 @@ files:
|
|
93
92
|
- lib/hrr_rb_ssh/authentication/method/none/context.rb
|
94
93
|
- lib/hrr_rb_ssh/authentication/method/password.rb
|
95
94
|
- lib/hrr_rb_ssh/authentication/method/password/context.rb
|
95
|
+
- lib/hrr_rb_ssh/authentication/method/publickey.rb
|
96
|
+
- lib/hrr_rb_ssh/authentication/method/publickey/context.rb
|
97
|
+
- lib/hrr_rb_ssh/authentication/method/publickey/ssh_rsa.rb
|
96
98
|
- lib/hrr_rb_ssh/closed_authentication_error.rb
|
97
99
|
- lib/hrr_rb_ssh/closed_connection_error.rb
|
98
100
|
- lib/hrr_rb_ssh/closed_transport_error.rb
|
@@ -168,7 +170,8 @@ files:
|
|
168
170
|
- lib/hrr_rb_ssh/transport/server_host_key_algorithm/ssh_rsa.rb
|
169
171
|
- lib/hrr_rb_ssh/version.rb
|
170
172
|
homepage: https://github.com/hirura/hrr_rb_ssh
|
171
|
-
licenses:
|
173
|
+
licenses:
|
174
|
+
- Apache-2.0
|
172
175
|
metadata: {}
|
173
176
|
post_install_message:
|
174
177
|
rdoc_options: []
|
@@ -178,7 +181,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
178
181
|
requirements:
|
179
182
|
- - ">="
|
180
183
|
- !ruby/object:Gem::Version
|
181
|
-
version:
|
184
|
+
version: 2.0.0
|
182
185
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
186
|
requirements:
|
184
187
|
- - ">="
|
@@ -189,5 +192,5 @@ rubyforge_project:
|
|
189
192
|
rubygems_version: 2.7.6
|
190
193
|
signing_key:
|
191
194
|
specification_version: 4
|
192
|
-
summary: SSH2
|
195
|
+
summary: Pure Ruby SSH2 server implementation
|
193
196
|
test_files: []
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "hrr_rb_ssh"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start(__FILE__)
|