hrr_rb_ssh 0.1.0
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 +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
|