hrr_rb_ssh 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +27 -0
- data/.rspec +3 -0
- data/.travis.yml +22 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE +201 -0
- data/README.md +47 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/demo/server.rb +134 -0
- data/hrr_rb_ssh.gemspec +27 -0
- data/lib/hrr_rb_ssh/authentication/authenticator.rb +16 -0
- data/lib/hrr_rb_ssh/authentication/method/none/context.rb +28 -0
- data/lib/hrr_rb_ssh/authentication/method/none.rb +38 -0
- data/lib/hrr_rb_ssh/authentication/method/password/context.rb +29 -0
- data/lib/hrr_rb_ssh/authentication/method/password.rb +37 -0
- data/lib/hrr_rb_ssh/authentication/method.rb +21 -0
- data/lib/hrr_rb_ssh/authentication.rb +107 -0
- data/lib/hrr_rb_ssh/closed_authentication_error.rb +7 -0
- data/lib/hrr_rb_ssh/closed_connection_error.rb +7 -0
- data/lib/hrr_rb_ssh/closed_transport_error.rb +7 -0
- data/lib/hrr_rb_ssh/compat.rb +65 -0
- data/lib/hrr_rb_ssh/connection/channel/proc_chain/chain_context.rb +22 -0
- data/lib/hrr_rb_ssh/connection/channel/proc_chain.rb +25 -0
- data/lib/hrr_rb_ssh/connection/channel/session/env/context.rb +43 -0
- data/lib/hrr_rb_ssh/connection/channel/session/env.rb +31 -0
- data/lib/hrr_rb_ssh/connection/channel/session/exec/context.rb +41 -0
- data/lib/hrr_rb_ssh/connection/channel/session/exec.rb +31 -0
- data/lib/hrr_rb_ssh/connection/channel/session/pty_req/context.rb +50 -0
- data/lib/hrr_rb_ssh/connection/channel/session/pty_req.rb +31 -0
- data/lib/hrr_rb_ssh/connection/channel/session/shell/context.rb +37 -0
- data/lib/hrr_rb_ssh/connection/channel/session/shell.rb +31 -0
- data/lib/hrr_rb_ssh/connection/channel/session/subsystem/context.rb +40 -0
- data/lib/hrr_rb_ssh/connection/channel/session/subsystem.rb +31 -0
- data/lib/hrr_rb_ssh/connection/channel/session.rb +31 -0
- data/lib/hrr_rb_ssh/connection/channel.rb +278 -0
- data/lib/hrr_rb_ssh/connection/request_handler.rb +18 -0
- data/lib/hrr_rb_ssh/connection.rb +170 -0
- data/lib/hrr_rb_ssh/logger.rb +52 -0
- data/lib/hrr_rb_ssh/message/001_ssh_msg_disconnect.rb +44 -0
- data/lib/hrr_rb_ssh/message/002_ssh_msg_ignore.rb +24 -0
- data/lib/hrr_rb_ssh/message/003_ssh_msg_unimplemented.rb +24 -0
- data/lib/hrr_rb_ssh/message/004_ssh_msg_debug.rb +26 -0
- data/lib/hrr_rb_ssh/message/005_ssh_msg_service_request.rb +24 -0
- data/lib/hrr_rb_ssh/message/006_ssh_msg_service_accept.rb +24 -0
- data/lib/hrr_rb_ssh/message/020_ssh_msg_kexinit.rb +51 -0
- data/lib/hrr_rb_ssh/message/021_ssh_msg_newkeys.rb +23 -0
- data/lib/hrr_rb_ssh/message/030_ssh_msg_kexdh_init.rb +24 -0
- data/lib/hrr_rb_ssh/message/031_ssh_msg_kexdh_reply.rb +26 -0
- data/lib/hrr_rb_ssh/message/050_ssh_msg_userauth_request.rb +58 -0
- data/lib/hrr_rb_ssh/message/051_ssh_msg_userauth_failure.rb +25 -0
- data/lib/hrr_rb_ssh/message/052_ssh_msg_userauth_success.rb +23 -0
- data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_pk_ok.rb +25 -0
- data/lib/hrr_rb_ssh/message/080_ssh_msg_global_request.rb +47 -0
- data/lib/hrr_rb_ssh/message/081_ssh_msg_request_success.rb +36 -0
- data/lib/hrr_rb_ssh/message/082_ssh_msg_request_failure.rb +23 -0
- data/lib/hrr_rb_ssh/message/090_ssh_msg_channel_open.rb +67 -0
- data/lib/hrr_rb_ssh/message/091_ssh_msg_channel_open_confirmation.rb +67 -0
- data/lib/hrr_rb_ssh/message/092_ssh_msg_channel_open_failure.rb +34 -0
- data/lib/hrr_rb_ssh/message/093_ssh_msg_channel_window_adjust.rb +25 -0
- data/lib/hrr_rb_ssh/message/094_ssh_msg_channel_data.rb +25 -0
- data/lib/hrr_rb_ssh/message/095_ssh_msg_channel_extended_data.rb +30 -0
- data/lib/hrr_rb_ssh/message/096_ssh_msg_channel_eof.rb +24 -0
- data/lib/hrr_rb_ssh/message/097_ssh_msg_channel_close.rb +24 -0
- data/lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb +139 -0
- data/lib/hrr_rb_ssh/message/099_ssh_msg_channel_success.rb +24 -0
- data/lib/hrr_rb_ssh/message/100_ssh_msg_channel_failure.rb +24 -0
- data/lib/hrr_rb_ssh/message/codable.rb +67 -0
- data/lib/hrr_rb_ssh/message.rb +36 -0
- data/lib/hrr_rb_ssh/transport/compression_algorithm/none.rb +33 -0
- data/lib/hrr_rb_ssh/transport/compression_algorithm/zlib.rb +38 -0
- data/lib/hrr_rb_ssh/transport/compression_algorithm.rb +22 -0
- data/lib/hrr_rb_ssh/transport/constant.rb +11 -0
- data/lib/hrr_rb_ssh/transport/data_type.rb +163 -0
- data/lib/hrr_rb_ssh/transport/encryption_algorithm/aes_128_cbc.rb +73 -0
- data/lib/hrr_rb_ssh/transport/encryption_algorithm/none.rb +49 -0
- data/lib/hrr_rb_ssh/transport/encryption_algorithm.rb +22 -0
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman.rb +129 -0
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group14_sha1.rb +42 -0
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group1_sha1.rb +34 -0
- data/lib/hrr_rb_ssh/transport/kex_algorithm.rb +22 -0
- data/lib/hrr_rb_ssh/transport/mac_algorithm/hmac_sha1.rb +45 -0
- data/lib/hrr_rb_ssh/transport/mac_algorithm/none.rb +40 -0
- data/lib/hrr_rb_ssh/transport/mac_algorithm.rb +22 -0
- data/lib/hrr_rb_ssh/transport/mode.rb +11 -0
- data/lib/hrr_rb_ssh/transport/receiver.rb +75 -0
- data/lib/hrr_rb_ssh/transport/sender.rb +57 -0
- data/lib/hrr_rb_ssh/transport/sequence_number.rb +22 -0
- data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/ssh_rsa.rb +108 -0
- data/lib/hrr_rb_ssh/transport/server_host_key_algorithm.rb +21 -0
- data/lib/hrr_rb_ssh/transport.rb +459 -0
- data/lib/hrr_rb_ssh/version.rb +6 -0
- data/lib/hrr_rb_ssh.rb +13 -0
- metadata +193 -0
@@ -0,0 +1,108 @@
|
|
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 Transport
|
9
|
+
class ServerHostKeyAlgorithm
|
10
|
+
name_list = [
|
11
|
+
'ssh-rsa'
|
12
|
+
]
|
13
|
+
|
14
|
+
class SshRsa
|
15
|
+
SECRET_KEY = <<-EOB
|
16
|
+
-----BEGIN RSA PRIVATE KEY-----
|
17
|
+
MIIEpAIBAAKCAQEA71zHt9RvbXmxuOCWPKR65iBHO+a8M7Mfo4vRCs/dorZN7XL1
|
18
|
+
lYwjclvo0X1T39BRX+qJ2m4HB+7Vlef9YF7spYKm6czuSCYmJjD5X+PW5QYSGED1
|
19
|
+
fFSXwjTdDwJi1OKS4kL0Dd6zcSjlFxfjVLNCyUcix36XgDpoBLBFkDZd5P2ow3J6
|
20
|
+
WNanBasXrckjCk4M3kFclvmxl1O56bbV9VZq51ZqLjv/ZhOrE3WIPfrJGdZssODa
|
21
|
+
DnI6tM1puwZGVba9VaI8FfnuJcacJ3T9oEoXPY5W+kPZAw6dOARXnJTg+oZk/dBD
|
22
|
+
Bgej0aMO+1XM7HKz5BiqbhGGSXGas5zoefHbNwIDAQABAoIBAQDP2aQ/2EOuL8eI
|
23
|
+
/9TV8goafRr+RB1XU4r8zHOIzPnryhyfPX1OEDPToUXpa8gCiPWwsYxlVbfbRqTH
|
24
|
+
mHzoS2V5T5u7WE3t7tqfvVU+1C0OERhzYS0KeraRWLBA0VSbAeiEe5lL1f/CGr3c
|
25
|
+
MM0iBsvO1mu4ChBqs80RjTPKx7r/FStpWtqWN4kn+Bhj06qCqhftnudZdYFTHa/G
|
26
|
+
ia4YWOUH6dSIZKpE7oG53Gm/2ZdK2YiAgMOdrTQkvRzxuIa/RHaETj21hKpetmI7
|
27
|
+
TfS26RbU2t1Bf/fdFhtTqoAz+CrZEH7Z407ZO45fdc31zJAFIK2Zf3CDVnKwih3t
|
28
|
+
O0bEVSSpAoGBAP/zEWaTivdQtcemMRhFQBySgnStov+dsxnGBnTkWxVIU7VoFgyg
|
29
|
+
mgNRlWUxMf12mlfqBVRpx0/ALggHf5KFmbAZ+3qvKSLmfIVM5E9l5NKbZnCWtIqq
|
30
|
+
1DN9kHPPOZn3uYvOs9Cpn7S6sa+rVZ82Mg8EZMsPesvFMOjrgNbMQxt7AoGBAO9o
|
31
|
+
38VM0+M09sAgOhmqv+Esa2gUGw5n18o/fdmlZdnA+D2ntgr70AD6JUCSYrZgTJRq
|
32
|
+
HNMuKrbD6HyaPjVaxYJVCFJIcfV+nViZdE8cHh9WXQ/JP/T6nvNajCC8StvoQg4I
|
33
|
+
vAZFTzChoe2yrOsWXezn9QAecQ8L2WHDLImpayR1AoGADoc1jaUCVld2egas8ru7
|
34
|
+
j+OhFA5nGitRZz0eULRFl0eruLhXyA+1rkqLOFs6gzCgQi0+cDQw5A38jugeDasX
|
35
|
+
ti9DXwtiQmDi4I4kx3z5KBs6DVoAlX5s3R9be7dfhaXSGmV5P3bhYdjXDSmkio0A
|
36
|
+
+mk9b2lJhxeCVzZG8epWRNECgYB2KzGoVQ+Q6ieRFVcYLCuhnSc2rBXeumrMrSIV
|
37
|
+
N4paPOFKrWkxarF0igOxJ5AJrOafqvCnW/ZBV9l9BzUFaNRsTERbON7m6aQIg1Xh
|
38
|
+
ZmOH3Dz6+b7T0JB8VYks70OT38Qa4TzNa5B21JD0nmizcMrTkHphoKT1ZEfb9VYa
|
39
|
+
bMExsQKBgQDoSpo/ZP8+dwR1A/gcu2K5Ie47c3WgKw7qQMarxqzTeS8Xu6/KAn+J
|
40
|
+
Ka2zIvoHhxlhXFBRhp+FIaFlYRR38gHeNxCoUylpboCUyMkHOsOP43AiKsmbNK20
|
41
|
+
vzTNM3SFzgt3bHkdEtDLc64aoBX+dHOot6u71XLZrshnHPtiZ0C/ZA==
|
42
|
+
-----END RSA PRIVATE KEY-----
|
43
|
+
EOB
|
44
|
+
|
45
|
+
KEY_FORMAT_DEFINITION = [
|
46
|
+
['string', 'ssh-rsa'],
|
47
|
+
['mpint', 'e'],
|
48
|
+
['mpint', 'n'],
|
49
|
+
]
|
50
|
+
|
51
|
+
SIGN_DEFINITION = [
|
52
|
+
['string', 'ssh-rsa'],
|
53
|
+
['string', 'rsa_signature_blob'],
|
54
|
+
]
|
55
|
+
|
56
|
+
def initialize
|
57
|
+
@logger = HrrRbSsh::Logger.new self.class.name
|
58
|
+
|
59
|
+
@rsa = OpenSSL::PKey::RSA.new SECRET_KEY
|
60
|
+
end
|
61
|
+
|
62
|
+
def encode definition, payload
|
63
|
+
definition.map{ |data_type, field_name|
|
64
|
+
field_value = if payload[field_name].instance_of? ::Proc then payload[field_name].call else payload[field_name] end
|
65
|
+
HrrRbSsh::Transport::DataType[data_type].encode( field_value )
|
66
|
+
}.join
|
67
|
+
end
|
68
|
+
|
69
|
+
def decode definition, payload
|
70
|
+
payload_io = StringIO.new payload, 'r'
|
71
|
+
definition.map{ |data_type, field_name|
|
72
|
+
[
|
73
|
+
field_name,
|
74
|
+
HrrRbSsh::Transport::DataType[data_type].decode( payload_io )
|
75
|
+
]
|
76
|
+
}.to_h
|
77
|
+
end
|
78
|
+
|
79
|
+
def server_public_host_key
|
80
|
+
payload = {
|
81
|
+
'ssh-rsa' => 'ssh-rsa',
|
82
|
+
'e' => @rsa.e.to_i,
|
83
|
+
'n' => @rsa.n.to_i,
|
84
|
+
}
|
85
|
+
encode KEY_FORMAT_DEFINITION, payload
|
86
|
+
end
|
87
|
+
|
88
|
+
def sign digest, data
|
89
|
+
payload = {
|
90
|
+
'ssh-rsa' => 'ssh-rsa',
|
91
|
+
'rsa_signature_blob' => @rsa.sign(digest, data),
|
92
|
+
}
|
93
|
+
encode SIGN_DEFINITION, payload
|
94
|
+
end
|
95
|
+
|
96
|
+
def verify digest, sign, data
|
97
|
+
payload = decode SIGN_DEFINITION, sign
|
98
|
+
payload['ssh-rsa'] == 'ssh-rsa' && @rsa.verify(digest, payload['rsa_signature_blob'], data)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
@@list ||= Hash.new
|
103
|
+
name_list.each do |name|
|
104
|
+
@@list[name] = SshRsa
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'hrr_rb_ssh/logger'
|
5
|
+
require 'hrr_rb_ssh/transport/server_host_key_algorithm/ssh_rsa'
|
6
|
+
|
7
|
+
module HrrRbSsh
|
8
|
+
class Transport
|
9
|
+
class ServerHostKeyAlgorithm
|
10
|
+
@@list ||= Hash.new
|
11
|
+
|
12
|
+
def self.[] key
|
13
|
+
@@list[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.name_list
|
17
|
+
@@list.keys
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,459 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'hrr_rb_ssh/version'
|
5
|
+
require 'hrr_rb_ssh/logger'
|
6
|
+
require 'hrr_rb_ssh/message'
|
7
|
+
require 'hrr_rb_ssh/closed_transport_error'
|
8
|
+
require 'hrr_rb_ssh/transport/constant'
|
9
|
+
require 'hrr_rb_ssh/transport/mode'
|
10
|
+
require 'hrr_rb_ssh/transport/data_type'
|
11
|
+
require 'hrr_rb_ssh/transport/sequence_number'
|
12
|
+
require 'hrr_rb_ssh/transport/sender'
|
13
|
+
require 'hrr_rb_ssh/transport/receiver'
|
14
|
+
require 'hrr_rb_ssh/transport/kex_algorithm'
|
15
|
+
require 'hrr_rb_ssh/transport/server_host_key_algorithm'
|
16
|
+
require 'hrr_rb_ssh/transport/encryption_algorithm'
|
17
|
+
require 'hrr_rb_ssh/transport/mac_algorithm'
|
18
|
+
require 'hrr_rb_ssh/transport/compression_algorithm'
|
19
|
+
|
20
|
+
module HrrRbSsh
|
21
|
+
class Transport
|
22
|
+
include Constant
|
23
|
+
|
24
|
+
attr_reader \
|
25
|
+
:io,
|
26
|
+
:incoming_sequence_number,
|
27
|
+
:outgoing_sequence_number,
|
28
|
+
:server_host_key_algorithm,
|
29
|
+
:incoming_encryption_algorithm,
|
30
|
+
:incoming_mac_algorithm,
|
31
|
+
:incoming_compression_algorithm,
|
32
|
+
:outgoing_encryption_algorithm,
|
33
|
+
:outgoing_mac_algorithm,
|
34
|
+
:outgoing_compression_algorithm,
|
35
|
+
:v_c,
|
36
|
+
:v_s,
|
37
|
+
:i_c,
|
38
|
+
:i_s,
|
39
|
+
:session_id
|
40
|
+
|
41
|
+
def initialize io, mode
|
42
|
+
@io = io
|
43
|
+
@mode = mode
|
44
|
+
|
45
|
+
@logger = HrrRbSsh::Logger.new self.class.name
|
46
|
+
|
47
|
+
@closed = nil
|
48
|
+
@disconnected = nil
|
49
|
+
|
50
|
+
@sender = HrrRbSsh::Transport::Sender.new
|
51
|
+
@receiver = HrrRbSsh::Transport::Receiver.new
|
52
|
+
|
53
|
+
@send_queue = Queue.new
|
54
|
+
@receive_queue = Queue.new
|
55
|
+
|
56
|
+
@sender_thread = nil
|
57
|
+
@receiver_thread = nil
|
58
|
+
|
59
|
+
@local_version = "SSH-2.0-HrrRbSsh-#{HrrRbSsh::VERSION}".force_encoding(Encoding::ASCII_8BIT)
|
60
|
+
@remote_version = "".force_encoding(Encoding::ASCII_8BIT)
|
61
|
+
|
62
|
+
@incoming_sequence_number = HrrRbSsh::Transport::SequenceNumber.new
|
63
|
+
@outgoing_sequence_number = HrrRbSsh::Transport::SequenceNumber.new
|
64
|
+
|
65
|
+
@acceptable_services = Array.new
|
66
|
+
|
67
|
+
initialize_local_algorithms
|
68
|
+
initialize_algorithms
|
69
|
+
end
|
70
|
+
|
71
|
+
def register_acceptable_service service_name
|
72
|
+
@acceptable_services.push service_name
|
73
|
+
end
|
74
|
+
|
75
|
+
def send payload
|
76
|
+
begin
|
77
|
+
@send_queue.enq payload
|
78
|
+
rescue ClosedQueueError => e
|
79
|
+
raise HrrRbSsh::ClosedTransportError
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def receive
|
84
|
+
payload = @receive_queue.deq
|
85
|
+
if @receive_queue.closed?
|
86
|
+
raise HrrRbSsh::ClosedTransportError
|
87
|
+
end
|
88
|
+
payload
|
89
|
+
end
|
90
|
+
|
91
|
+
def start
|
92
|
+
@logger.info("start transport")
|
93
|
+
|
94
|
+
exchange_version
|
95
|
+
exchange_key
|
96
|
+
|
97
|
+
case @mode
|
98
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
99
|
+
verify_service_request
|
100
|
+
end
|
101
|
+
|
102
|
+
@closed = false
|
103
|
+
|
104
|
+
@sender_thread = sender_thread
|
105
|
+
@receiver_thread = receiver_thread
|
106
|
+
|
107
|
+
@logger.info("transport started")
|
108
|
+
end
|
109
|
+
|
110
|
+
def close
|
111
|
+
return if @closed
|
112
|
+
@logger.info("close transport")
|
113
|
+
@closed = true
|
114
|
+
@send_queue.close
|
115
|
+
@receive_queue.close
|
116
|
+
disconnect
|
117
|
+
@logger.info("transport closed")
|
118
|
+
end
|
119
|
+
|
120
|
+
def closed?
|
121
|
+
@closed
|
122
|
+
end
|
123
|
+
|
124
|
+
def disconnect
|
125
|
+
return if @disconnected
|
126
|
+
@logger.info("disconnect transport")
|
127
|
+
@disconnected = true
|
128
|
+
begin
|
129
|
+
send_disconnect
|
130
|
+
rescue IOError
|
131
|
+
@logger.warn("IO is closed")
|
132
|
+
rescue => e
|
133
|
+
@logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
134
|
+
end
|
135
|
+
@logger.info("transport disconnected")
|
136
|
+
end
|
137
|
+
|
138
|
+
def exchange_version
|
139
|
+
send_version
|
140
|
+
receive_version
|
141
|
+
|
142
|
+
update_version_strings
|
143
|
+
end
|
144
|
+
|
145
|
+
def exchange_key
|
146
|
+
send_kexinit
|
147
|
+
receive_kexinit
|
148
|
+
|
149
|
+
update_kex_and_server_host_key_algorithms
|
150
|
+
|
151
|
+
case @mode
|
152
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
153
|
+
receive_kexdh_init
|
154
|
+
send_kexdh_reply
|
155
|
+
|
156
|
+
send_newkeys
|
157
|
+
receive_newkeys
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def verify_service_request
|
162
|
+
service_request_message = receive_service_request
|
163
|
+
service_name = service_request_message['service name']
|
164
|
+
if @acceptable_services.include? service_name
|
165
|
+
send_service_accept service_name
|
166
|
+
else
|
167
|
+
close
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def sender_thread
|
172
|
+
Thread.start {
|
173
|
+
@logger.info("start sender thread")
|
174
|
+
loop do
|
175
|
+
begin
|
176
|
+
payload = @send_queue.deq
|
177
|
+
if @send_queue.closed?
|
178
|
+
@logger.info("closing sender thread")
|
179
|
+
break
|
180
|
+
end
|
181
|
+
@sender.send self, payload
|
182
|
+
rescue => e
|
183
|
+
@logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
184
|
+
close
|
185
|
+
end
|
186
|
+
end
|
187
|
+
@logger.info("sender thread closed")
|
188
|
+
}
|
189
|
+
end
|
190
|
+
|
191
|
+
def receiver_thread
|
192
|
+
Thread.start {
|
193
|
+
@logger.info("start receiver thread")
|
194
|
+
loop do
|
195
|
+
if @receive_queue.closed?
|
196
|
+
@logger.info("closing receiver thread")
|
197
|
+
break
|
198
|
+
end
|
199
|
+
begin
|
200
|
+
payload = @receiver.receive self
|
201
|
+
case payload[0,1].unpack("C")[0]
|
202
|
+
when HrrRbSsh::Message::SSH_MSG_DISCONNECT::VALUE
|
203
|
+
message = HrrRbSsh::Message::SSH_MSG_DISCONNECT.decode payload
|
204
|
+
@logger.debug("received disconnect message: #{message.inspect}")
|
205
|
+
@disconnected = true
|
206
|
+
close
|
207
|
+
when HrrRbSsh::Message::SSH_MSG_IGNORE::VALUE
|
208
|
+
message = HrrRbSsh::Message::SSH_MSG_IGNORE.decode payload
|
209
|
+
@logger.debug("received ignore message: #{message.inspect}")
|
210
|
+
when HrrRbSsh::Message::SSH_MSG_UNIMPLEMENTED::VALUE
|
211
|
+
message = HrrRbSsh::Message::SSH_MSG_UNIMPLEMENTED.decode payload
|
212
|
+
@logger.debug("received unimplemented message: #{message.inspect}")
|
213
|
+
when HrrRbSsh::Message::SSH_MSG_DEBUG::VALUE
|
214
|
+
message = HrrRbSsh::Message::SSH_MSG_DEBUG.decode payload
|
215
|
+
@logger.debug("received debug message: #{message.inspect}")
|
216
|
+
else
|
217
|
+
@receive_queue.enq payload
|
218
|
+
end
|
219
|
+
rescue EOFError => e
|
220
|
+
close
|
221
|
+
rescue => e
|
222
|
+
@logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
223
|
+
close
|
224
|
+
end
|
225
|
+
end
|
226
|
+
@logger.info("receiver thread closed")
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
def initialize_local_algorithms
|
231
|
+
@local_kex_algorithms = HrrRbSsh::Transport::KexAlgorithm.name_list
|
232
|
+
@local_server_host_key_algorithms = HrrRbSsh::Transport::ServerHostKeyAlgorithm.name_list
|
233
|
+
@local_encryption_algorithms_client_to_server = HrrRbSsh::Transport::EncryptionAlgorithm.name_list
|
234
|
+
@local_encryption_algorithms_server_to_client = HrrRbSsh::Transport::EncryptionAlgorithm.name_list
|
235
|
+
@local_mac_algorithms_client_to_server = HrrRbSsh::Transport::MacAlgorithm.name_list
|
236
|
+
@local_mac_algorithms_server_to_client = HrrRbSsh::Transport::MacAlgorithm.name_list
|
237
|
+
@local_compression_algorithms_client_to_server = HrrRbSsh::Transport::CompressionAlgorithm.name_list
|
238
|
+
@local_compression_algorithms_server_to_client = HrrRbSsh::Transport::CompressionAlgorithm.name_list
|
239
|
+
end
|
240
|
+
|
241
|
+
def initialize_algorithms
|
242
|
+
@incoming_encryption_algorithm = HrrRbSsh::Transport::EncryptionAlgorithm['none'].new
|
243
|
+
@incoming_mac_algorithm = HrrRbSsh::Transport::MacAlgorithm['none'].new
|
244
|
+
@incoming_compression_algorithm = HrrRbSsh::Transport::CompressionAlgorithm['none'].new
|
245
|
+
|
246
|
+
@outgoing_encryption_algorithm = HrrRbSsh::Transport::EncryptionAlgorithm['none'].new
|
247
|
+
@outgoing_mac_algorithm = HrrRbSsh::Transport::MacAlgorithm['none'].new
|
248
|
+
@outgoing_compression_algorithm = HrrRbSsh::Transport::CompressionAlgorithm['none'].new
|
249
|
+
end
|
250
|
+
|
251
|
+
def send_version
|
252
|
+
@io.write (@local_version + CR + LF)
|
253
|
+
end
|
254
|
+
|
255
|
+
def receive_version
|
256
|
+
tmp_str = String.new
|
257
|
+
loop do
|
258
|
+
tmp_str << @io.read(1)
|
259
|
+
if tmp_str =~ /#{CR}#{LF}/
|
260
|
+
if tmp_str =~ /^SSH-/
|
261
|
+
@remote_version = tmp_str.match( /(:?SSH-.+)#{CR}#{LF}/ )[1]
|
262
|
+
break
|
263
|
+
else
|
264
|
+
tmp_str.clear
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def update_version_strings
|
271
|
+
case @mode
|
272
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
273
|
+
@v_c = @remote_version
|
274
|
+
@v_s = @local_version
|
275
|
+
when HrrRbSsh::Transport::Mode::CLIENT
|
276
|
+
@v_c = @local_version
|
277
|
+
@v_s = @remote_version
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def send_disconnect
|
282
|
+
message = {
|
283
|
+
"SSH_MSG_DISCONNECT" => 1,
|
284
|
+
"reason code" => HrrRbSsh::Message::SSH_MSG_DISCONNECT::ReasonCode::SSH_DISCONNECT_BY_APPLICATION,
|
285
|
+
"description" => "disconnected by user",
|
286
|
+
"language tag" => ""
|
287
|
+
}
|
288
|
+
payload = HrrRbSsh::Message::SSH_MSG_DISCONNECT.encode message
|
289
|
+
@sender.send self, payload
|
290
|
+
end
|
291
|
+
|
292
|
+
def send_kexinit
|
293
|
+
message = {
|
294
|
+
'SSH_MSG_KEXINIT' => HrrRbSsh::Message::SSH_MSG_KEXINIT::VALUE,
|
295
|
+
'cookie (random byte)' => lambda { rand(0x01_00) },
|
296
|
+
'kex_algorithms' => @local_kex_algorithms,
|
297
|
+
'server_host_key_algorithms' => @local_server_host_key_algorithms,
|
298
|
+
'encryption_algorithms_client_to_server' => @local_encryption_algorithms_client_to_server,
|
299
|
+
'encryption_algorithms_server_to_client' => @local_encryption_algorithms_server_to_client,
|
300
|
+
'mac_algorithms_client_to_server' => @local_mac_algorithms_client_to_server,
|
301
|
+
'mac_algorithms_server_to_client' => @local_mac_algorithms_server_to_client,
|
302
|
+
'compression_algorithms_client_to_server' => @local_compression_algorithms_client_to_server,
|
303
|
+
'compression_algorithms_server_to_client' => @local_compression_algorithms_server_to_client,
|
304
|
+
'languages_client_to_server' => [],
|
305
|
+
'languages_server_to_client' => [],
|
306
|
+
'first_kex_packet_follows' => false,
|
307
|
+
'0 (reserved for future extension)' => 0,
|
308
|
+
}
|
309
|
+
payload = HrrRbSsh::Message::SSH_MSG_KEXINIT.encode message
|
310
|
+
@sender.send self, payload
|
311
|
+
|
312
|
+
case @mode
|
313
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
314
|
+
@i_s = payload
|
315
|
+
when HrrRbSsh::Transport::Mode::CLIENT
|
316
|
+
@i_c = payload
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def receive_kexinit
|
321
|
+
payload = @receiver.receive self
|
322
|
+
|
323
|
+
case @mode
|
324
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
325
|
+
@i_c = payload
|
326
|
+
when HrrRbSsh::Transport::Mode::CLIENT
|
327
|
+
@i_s = payload
|
328
|
+
end
|
329
|
+
|
330
|
+
message = HrrRbSsh::Message::SSH_MSG_KEXINIT.decode payload
|
331
|
+
|
332
|
+
update_remote_algorithms message
|
333
|
+
end
|
334
|
+
|
335
|
+
def receive_kexdh_init
|
336
|
+
payload = @receiver.receive self
|
337
|
+
message = HrrRbSsh::Message::SSH_MSG_KEXDH_INIT.decode payload
|
338
|
+
|
339
|
+
@kex_algorithm.set_e message['e']
|
340
|
+
|
341
|
+
@session_id ||= @kex_algorithm.hash self
|
342
|
+
end
|
343
|
+
|
344
|
+
def send_kexdh_reply
|
345
|
+
message = {
|
346
|
+
'SSH_MSG_KEXDH_REPLY' => HrrRbSsh::Message::SSH_MSG_KEXDH_REPLY::VALUE,
|
347
|
+
'server public host key and certificates (K_S)' => @server_host_key_algorithm.server_public_host_key,
|
348
|
+
'f' => @kex_algorithm.pub_key,
|
349
|
+
'signature of H' => @kex_algorithm.sign(self),
|
350
|
+
}
|
351
|
+
payload = HrrRbSsh::Message::SSH_MSG_KEXDH_REPLY.encode message
|
352
|
+
@sender.send self, payload
|
353
|
+
end
|
354
|
+
|
355
|
+
def send_newkeys
|
356
|
+
message = {
|
357
|
+
'SSH_MSG_NEWKEYS' => HrrRbSsh::Message::SSH_MSG_NEWKEYS::VALUE,
|
358
|
+
}
|
359
|
+
payload = HrrRbSsh::Message::SSH_MSG_NEWKEYS.encode message
|
360
|
+
@sender.send self, payload
|
361
|
+
end
|
362
|
+
|
363
|
+
def receive_newkeys
|
364
|
+
payload = @receiver.receive self
|
365
|
+
message = HrrRbSsh::Message::SSH_MSG_NEWKEYS.decode payload
|
366
|
+
|
367
|
+
update_encryption_mac_compression_algorithms
|
368
|
+
end
|
369
|
+
|
370
|
+
def receive_service_request
|
371
|
+
payload = @receiver.receive self
|
372
|
+
message = HrrRbSsh::Message::SSH_MSG_SERVICE_REQUEST.decode payload
|
373
|
+
|
374
|
+
message
|
375
|
+
end
|
376
|
+
|
377
|
+
def send_service_accept service_name
|
378
|
+
message = {
|
379
|
+
'SSH_MSG_SERVICE_ACCEPT' => HrrRbSsh::Message::SSH_MSG_SERVICE_ACCEPT::VALUE,
|
380
|
+
'service name' => service_name,
|
381
|
+
}
|
382
|
+
payload = HrrRbSsh::Message::SSH_MSG_SERVICE_ACCEPT.encode message
|
383
|
+
@sender.send self, payload
|
384
|
+
end
|
385
|
+
|
386
|
+
def update_remote_algorithms message
|
387
|
+
@remote_kex_algorithms = message['kex_algorithms']
|
388
|
+
@remote_server_host_key_algorithms = message['server_host_key_algorithms']
|
389
|
+
@remote_encryption_algorithms_client_to_server = message['encryption_algorithms_client_to_server']
|
390
|
+
@remote_encryption_algorithms_server_to_client = message['encryption_algorithms_server_to_client']
|
391
|
+
@remote_mac_algorithms_client_to_server = message['mac_algorithms_client_to_server']
|
392
|
+
@remote_mac_algorithms_server_to_client = message['mac_algorithms_server_to_client']
|
393
|
+
@remote_compression_algorithms_client_to_server = message['compression_algorithms_client_to_server']
|
394
|
+
@remote_compression_algorithms_server_to_client = message['compression_algorithms_server_to_client']
|
395
|
+
end
|
396
|
+
|
397
|
+
def update_kex_and_server_host_key_algorithms
|
398
|
+
case @mode
|
399
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
400
|
+
kex_algorithm_name = @remote_kex_algorithms.find{ |a| @local_kex_algorithms.include? a } or raise
|
401
|
+
server_host_key_algorithm_name = @remote_server_host_key_algorithms.find{ |a| @local_server_host_key_algorithms.include? a } or raise
|
402
|
+
when HrrRbSsh::Transport::Mode::CLIENT
|
403
|
+
kex_algorithm_name = @local_kex_algorithms.find{ |a| @remote_kex_algorithms.include? a } or raise
|
404
|
+
server_host_key_algorithm_name = @local_server_host_key_algorithms.find{ |a| @remote_server_host_key_algorithms.include? a } or raise
|
405
|
+
end
|
406
|
+
|
407
|
+
@kex_algorithm = HrrRbSsh::Transport::KexAlgorithm[kex_algorithm_name].new
|
408
|
+
@server_host_key_algorithm = HrrRbSsh::Transport::ServerHostKeyAlgorithm[server_host_key_algorithm_name].new
|
409
|
+
end
|
410
|
+
|
411
|
+
def update_encryption_mac_compression_algorithms
|
412
|
+
update_encryption_algorithm
|
413
|
+
update_mac_algorithm
|
414
|
+
update_compression_algorithm
|
415
|
+
end
|
416
|
+
|
417
|
+
def update_encryption_algorithm
|
418
|
+
case @mode
|
419
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
420
|
+
encryption_algorithm_c_to_s_name = @remote_encryption_algorithms_client_to_server.find{ |a| @local_encryption_algorithms_client_to_server.include? a } or raise
|
421
|
+
encryption_algorithm_s_to_c_name = @remote_encryption_algorithms_server_to_client.find{ |a| @local_encryption_algorithms_server_to_client.include? a } or raise
|
422
|
+
incoming_encryption_algorithm_name = encryption_algorithm_c_to_s_name
|
423
|
+
outgoing_encryption_algorithm_name = encryption_algorithm_s_to_c_name
|
424
|
+
incoming_crpt_iv = @kex_algorithm.iv_c_to_s self, incoming_encryption_algorithm_name
|
425
|
+
outgoing_crpt_iv = @kex_algorithm.iv_s_to_c self, outgoing_encryption_algorithm_name
|
426
|
+
incoming_crpt_key = @kex_algorithm.key_c_to_s self, incoming_encryption_algorithm_name
|
427
|
+
outgoing_crpt_key = @kex_algorithm.key_s_to_c self, outgoing_encryption_algorithm_name
|
428
|
+
end
|
429
|
+
@incoming_encryption_algorithm = HrrRbSsh::Transport::EncryptionAlgorithm[incoming_encryption_algorithm_name].new incoming_crpt_iv, incoming_crpt_key
|
430
|
+
@outgoing_encryption_algorithm = HrrRbSsh::Transport::EncryptionAlgorithm[outgoing_encryption_algorithm_name].new outgoing_crpt_iv, outgoing_crpt_key
|
431
|
+
end
|
432
|
+
|
433
|
+
def update_mac_algorithm
|
434
|
+
case @mode
|
435
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
436
|
+
mac_algorithm_c_to_s_name = @remote_mac_algorithms_client_to_server.find{ |a| @local_mac_algorithms_client_to_server.include? a } or raise
|
437
|
+
mac_algorithm_s_to_c_name = @remote_mac_algorithms_server_to_client.find{ |a| @local_mac_algorithms_server_to_client.include? a } or raise
|
438
|
+
incoming_mac_algorithm_name = mac_algorithm_c_to_s_name
|
439
|
+
outgoing_mac_algorithm_name = mac_algorithm_s_to_c_name
|
440
|
+
incoming_mac_key = @kex_algorithm.mac_c_to_s self, incoming_mac_algorithm_name
|
441
|
+
outgoing_mac_key = @kex_algorithm.mac_s_to_c self, outgoing_mac_algorithm_name
|
442
|
+
end
|
443
|
+
@incoming_mac_algorithm = HrrRbSsh::Transport::MacAlgorithm[incoming_mac_algorithm_name].new incoming_mac_key
|
444
|
+
@outgoing_mac_algorithm = HrrRbSsh::Transport::MacAlgorithm[outgoing_mac_algorithm_name].new outgoing_mac_key
|
445
|
+
end
|
446
|
+
|
447
|
+
def update_compression_algorithm
|
448
|
+
case @mode
|
449
|
+
when HrrRbSsh::Transport::Mode::SERVER
|
450
|
+
compression_algorithm_c_to_s_name = @remote_compression_algorithms_client_to_server.find{ |a| @local_compression_algorithms_client_to_server.include? a } or raise
|
451
|
+
compression_algorithm_s_to_c_name = @remote_compression_algorithms_server_to_client.find{ |a| @local_compression_algorithms_server_to_client.include? a } or raise
|
452
|
+
incoming_compression_algorithm_name = compression_algorithm_c_to_s_name
|
453
|
+
outgoing_compression_algorithm_name = compression_algorithm_s_to_c_name
|
454
|
+
end
|
455
|
+
@incoming_compression_algorithm = HrrRbSsh::Transport::CompressionAlgorithm[incoming_compression_algorithm_name].new
|
456
|
+
@outgoing_compression_algorithm = HrrRbSsh::Transport::CompressionAlgorithm[outgoing_compression_algorithm_name].new
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
data/lib/hrr_rb_ssh.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require "hrr_rb_ssh/version"
|
5
|
+
require "hrr_rb_ssh/compat"
|
6
|
+
require "hrr_rb_ssh/logger"
|
7
|
+
require "hrr_rb_ssh/transport"
|
8
|
+
require "hrr_rb_ssh/authentication"
|
9
|
+
require "hrr_rb_ssh/connection"
|
10
|
+
|
11
|
+
module HrrRbSsh
|
12
|
+
# Your code goes here...
|
13
|
+
end
|