minmb-net-ssh 2.5.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.
Files changed (137) hide show
  1. data/CHANGELOG.rdoc +291 -0
  2. data/Manifest +132 -0
  3. data/README.rdoc +184 -0
  4. data/Rakefile +86 -0
  5. data/Rudyfile +96 -0
  6. data/THANKS.rdoc +19 -0
  7. data/lib/net/ssh.rb +223 -0
  8. data/lib/net/ssh/authentication/agent.rb +23 -0
  9. data/lib/net/ssh/authentication/agent/java_pageant.rb +85 -0
  10. data/lib/net/ssh/authentication/agent/socket.rb +170 -0
  11. data/lib/net/ssh/authentication/constants.rb +18 -0
  12. data/lib/net/ssh/authentication/key_manager.rb +253 -0
  13. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  14. data/lib/net/ssh/authentication/methods/hostbased.rb +75 -0
  15. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +70 -0
  16. data/lib/net/ssh/authentication/methods/password.rb +43 -0
  17. data/lib/net/ssh/authentication/methods/publickey.rb +96 -0
  18. data/lib/net/ssh/authentication/pageant.rb +301 -0
  19. data/lib/net/ssh/authentication/session.rb +154 -0
  20. data/lib/net/ssh/buffer.rb +350 -0
  21. data/lib/net/ssh/buffered_io.rb +207 -0
  22. data/lib/net/ssh/config.rb +207 -0
  23. data/lib/net/ssh/connection/channel.rb +630 -0
  24. data/lib/net/ssh/connection/constants.rb +33 -0
  25. data/lib/net/ssh/connection/session.rb +603 -0
  26. data/lib/net/ssh/connection/term.rb +178 -0
  27. data/lib/net/ssh/errors.rb +88 -0
  28. data/lib/net/ssh/key_factory.rb +107 -0
  29. data/lib/net/ssh/known_hosts.rb +141 -0
  30. data/lib/net/ssh/loggable.rb +61 -0
  31. data/lib/net/ssh/packet.rb +102 -0
  32. data/lib/net/ssh/prompt.rb +93 -0
  33. data/lib/net/ssh/proxy/command.rb +75 -0
  34. data/lib/net/ssh/proxy/errors.rb +14 -0
  35. data/lib/net/ssh/proxy/http.rb +94 -0
  36. data/lib/net/ssh/proxy/socks4.rb +70 -0
  37. data/lib/net/ssh/proxy/socks5.rb +142 -0
  38. data/lib/net/ssh/ruby_compat.rb +77 -0
  39. data/lib/net/ssh/service/forward.rb +327 -0
  40. data/lib/net/ssh/test.rb +89 -0
  41. data/lib/net/ssh/test/channel.rb +129 -0
  42. data/lib/net/ssh/test/extensions.rb +152 -0
  43. data/lib/net/ssh/test/kex.rb +44 -0
  44. data/lib/net/ssh/test/local_packet.rb +51 -0
  45. data/lib/net/ssh/test/packet.rb +81 -0
  46. data/lib/net/ssh/test/remote_packet.rb +38 -0
  47. data/lib/net/ssh/test/script.rb +157 -0
  48. data/lib/net/ssh/test/socket.rb +64 -0
  49. data/lib/net/ssh/transport/algorithms.rb +407 -0
  50. data/lib/net/ssh/transport/cipher_factory.rb +106 -0
  51. data/lib/net/ssh/transport/constants.rb +32 -0
  52. data/lib/net/ssh/transport/ctr.rb +95 -0
  53. data/lib/net/ssh/transport/hmac.rb +45 -0
  54. data/lib/net/ssh/transport/hmac/abstract.rb +79 -0
  55. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  56. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  57. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  58. data/lib/net/ssh/transport/hmac/ripemd160.rb +13 -0
  59. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  60. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  61. data/lib/net/ssh/transport/hmac/sha2_256.rb +15 -0
  62. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +13 -0
  63. data/lib/net/ssh/transport/hmac/sha2_512.rb +14 -0
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +13 -0
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -0
  66. data/lib/net/ssh/transport/kex.rb +28 -0
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +44 -0
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +216 -0
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +80 -0
  70. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +15 -0
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +93 -0
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +13 -0
  73. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +13 -0
  74. data/lib/net/ssh/transport/key_expander.rb +26 -0
  75. data/lib/net/ssh/transport/openssl.rb +237 -0
  76. data/lib/net/ssh/transport/packet_stream.rb +235 -0
  77. data/lib/net/ssh/transport/server_version.rb +71 -0
  78. data/lib/net/ssh/transport/session.rb +278 -0
  79. data/lib/net/ssh/transport/state.rb +206 -0
  80. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  81. data/lib/net/ssh/verifiers/null.rb +12 -0
  82. data/lib/net/ssh/verifiers/strict.rb +53 -0
  83. data/lib/net/ssh/version.rb +62 -0
  84. data/net-ssh.gemspec +164 -0
  85. data/setup.rb +1585 -0
  86. data/support/arcfour_check.rb +20 -0
  87. data/support/ssh_tunnel_bug.rb +65 -0
  88. data/test/authentication/methods/common.rb +28 -0
  89. data/test/authentication/methods/test_abstract.rb +51 -0
  90. data/test/authentication/methods/test_hostbased.rb +114 -0
  91. data/test/authentication/methods/test_keyboard_interactive.rb +100 -0
  92. data/test/authentication/methods/test_password.rb +52 -0
  93. data/test/authentication/methods/test_publickey.rb +148 -0
  94. data/test/authentication/test_agent.rb +205 -0
  95. data/test/authentication/test_key_manager.rb +218 -0
  96. data/test/authentication/test_session.rb +106 -0
  97. data/test/common.rb +107 -0
  98. data/test/configs/eqsign +3 -0
  99. data/test/configs/exact_match +8 -0
  100. data/test/configs/host_plus +10 -0
  101. data/test/configs/multihost +4 -0
  102. data/test/configs/wild_cards +14 -0
  103. data/test/connection/test_channel.rb +467 -0
  104. data/test/connection/test_session.rb +488 -0
  105. data/test/known_hosts/github +1 -0
  106. data/test/test_all.rb +9 -0
  107. data/test/test_buffer.rb +426 -0
  108. data/test/test_buffered_io.rb +63 -0
  109. data/test/test_config.rb +120 -0
  110. data/test/test_key_factory.rb +121 -0
  111. data/test/test_known_hosts.rb +13 -0
  112. data/test/transport/hmac/test_md5.rb +39 -0
  113. data/test/transport/hmac/test_md5_96.rb +25 -0
  114. data/test/transport/hmac/test_none.rb +34 -0
  115. data/test/transport/hmac/test_ripemd160.rb +34 -0
  116. data/test/transport/hmac/test_sha1.rb +34 -0
  117. data/test/transport/hmac/test_sha1_96.rb +25 -0
  118. data/test/transport/hmac/test_sha2_256.rb +35 -0
  119. data/test/transport/hmac/test_sha2_256_96.rb +25 -0
  120. data/test/transport/hmac/test_sha2_512.rb +35 -0
  121. data/test/transport/hmac/test_sha2_512_96.rb +25 -0
  122. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +13 -0
  123. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  124. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  125. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +33 -0
  126. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +161 -0
  127. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +37 -0
  128. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +37 -0
  129. data/test/transport/test_algorithms.rb +330 -0
  130. data/test/transport/test_cipher_factory.rb +441 -0
  131. data/test/transport/test_hmac.rb +34 -0
  132. data/test/transport/test_identity_cipher.rb +40 -0
  133. data/test/transport/test_packet_stream.rb +1745 -0
  134. data/test/transport/test_server_version.rb +78 -0
  135. data/test/transport/test_session.rb +315 -0
  136. data/test/transport/test_state.rb +179 -0
  137. metadata +208 -0
@@ -0,0 +1,178 @@
1
+ module Net; module SSH; module Connection
2
+
3
+ # These constants are used when requesting a pseudo-terminal (via
4
+ # Net::SSH::Connection::Channel#request_pty). The descriptions for each are
5
+ # taken directly from RFC 4254 ("The Secure Shell (SSH) Connection Protocol"),
6
+ # http://tools.ietf.org/html/rfc4254.
7
+ module Term
8
+ # Interrupt character; 255 if none. Similarly for the other characters.
9
+ # Not all of these characters are supported on all systems.
10
+ VINTR = 1
11
+
12
+ # The quit character (sends SIGQUIT signal on POSIX systems).
13
+ VQUIT = 2
14
+
15
+ # Erase the character to left of the cursor.
16
+ VERASE = 3
17
+
18
+ # Kill the current input line.
19
+ VKILL = 4
20
+
21
+ # End-of-file character (sends EOF from the terminal).
22
+ VEOF = 5
23
+
24
+ # End-of-line character in addition to carriage return and/or linefeed.
25
+ VEOL = 6
26
+
27
+ # Additional end-of-line character.
28
+ VEOL2 = 7
29
+
30
+ # Continues paused output (normally control-Q).
31
+ VSTART = 8
32
+
33
+ # Pauses output (normally control-S).
34
+ VSTOP = 9
35
+
36
+ # Suspends the current program.
37
+ VSUSP = 10
38
+
39
+ # Another suspend character.
40
+ VDSUSP = 11
41
+
42
+ # Reprints the current input line.
43
+ VREPRINT = 12
44
+
45
+ # Erases a word left of cursor.
46
+ VWERASE = 13
47
+
48
+ # Enter the next character typed literally, even if it is a special
49
+ # character.
50
+ VLNEXT = 14
51
+
52
+ # Character to flush output.
53
+ VFLUSH = 15
54
+
55
+ # Switch to a different shell layer.
56
+ VSWITCH = 16
57
+
58
+ # Prints system status line (load, command, pid, etc).
59
+ VSTATUS = 17
60
+
61
+ # Toggles the flushing of terminal output.
62
+ VDISCARD = 18
63
+
64
+ # The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE,
65
+ # and 1 if it is TRUE.
66
+ IGNPAR = 30
67
+
68
+ # Mark parity and framing errors.
69
+ PARMRK = 31
70
+
71
+ # Enable checking of parity errors.
72
+ INPCK = 32
73
+
74
+ # Strip 8th bit off characters.
75
+ ISTRIP = 33
76
+
77
+ # Map NL into CR on input.
78
+ INCLR = 34
79
+
80
+ # Ignore CR on input.
81
+ IGNCR = 35
82
+
83
+ # Map CR to NL on input.
84
+ ICRNL = 36
85
+
86
+ # Translate uppercase characters to lowercase.
87
+ IUCLC = 37
88
+
89
+ # Enable output flow control.
90
+ IXON = 38
91
+
92
+ # Any char will restart after stop.
93
+ IXANY = 39
94
+
95
+ # Enable input flow control.
96
+ IXOFF = 40
97
+
98
+ # Ring bell on input queue full.
99
+ IMAXBEL = 41
100
+
101
+ # Enable signals INTR, QUIT, [D]SUSP.
102
+ ISIG = 50
103
+
104
+ # Canonicalize input lines.
105
+ ICANON = 51
106
+
107
+ # Enable input and output of uppercase characters by preceding their
108
+ # lowercase equivalents with "\".
109
+ XCASE = 52
110
+
111
+ # Enable echoing.
112
+ ECHO = 53
113
+
114
+ # Visually erase chars.
115
+ ECHOE = 54
116
+
117
+ # Kill character discards current line.
118
+ ECHOK = 55
119
+
120
+ # Echo NL even if ECHO is off.
121
+ ECHONL = 56
122
+
123
+ # Don't flush after interrupt.
124
+ NOFLSH = 57
125
+
126
+ # Stop background jobs from output.
127
+ TOSTOP= 58
128
+
129
+ # Enable extensions.
130
+ IEXTEN = 59
131
+
132
+ # Echo control characters as ^(Char).
133
+ ECHOCTL = 60
134
+
135
+ # Visual erase for line kill.
136
+ ECHOKE = 61
137
+
138
+ # Retype pending input.
139
+ PENDIN = 62
140
+
141
+ # Enable output processing.
142
+ OPOST = 70
143
+
144
+ # Convert lowercase to uppercase.
145
+ OLCUC = 71
146
+
147
+ # Map NL to CR-NL.
148
+ ONLCR = 72
149
+
150
+ # Translate carriage return to newline (output).
151
+ OCRNL = 73
152
+
153
+ # Translate newline to carriage return-newline (output).
154
+ ONOCR = 74
155
+
156
+ # Newline performs a carriage return (output).
157
+ ONLRET = 75
158
+
159
+ # 7 bit mode.
160
+ CS7 = 90
161
+
162
+ # 8 bit mode.
163
+ CS8 = 91
164
+
165
+ # Parity enable.
166
+ PARENB = 92
167
+
168
+ # Odd parity, else even.
169
+ PARODD = 93
170
+
171
+ # Specifies the input baud rate in bits per second.
172
+ TTY_OP_ISPEED = 128
173
+
174
+ # Specifies the output baud rate in bits per second.
175
+ TTY_OP_OSPEED = 129
176
+ end
177
+
178
+ end; end; end
@@ -0,0 +1,88 @@
1
+ module Net; module SSH
2
+ # A general exception class, to act as the ancestor of all other Net::SSH
3
+ # exception classes.
4
+ class Exception < ::RuntimeError; end
5
+
6
+ # This exception is raised when authentication fails (whether it be
7
+ # public key authentication, password authentication, or whatever).
8
+ class AuthenticationFailed < Exception; end
9
+
10
+ # This exception is raised when a connection attempt times out.
11
+ class ConnectionTimeout < Exception; end
12
+
13
+ # This exception is raised when the remote host has disconnected
14
+ # unexpectedly.
15
+ class Disconnect < Exception; end
16
+
17
+ # This exception is primarily used internally, but if you have a channel
18
+ # request handler (see Net::SSH::Connection::Channel#on_request) that you
19
+ # want to fail in such a way that the server knows it failed, you can
20
+ # raise this exception in the handler and Net::SSH will translate that into
21
+ # a "channel failure" message.
22
+ class ChannelRequestFailed < Exception; end
23
+
24
+ # This is exception is primarily used internally, but if you have a channel
25
+ # open handler (see Net::SSH::Connection::Session#on_open_channel) and you
26
+ # want to fail in such a way that the server knows it failed, you can
27
+ # raise this exception in the handler and Net::SSH will translate that into
28
+ # a "channel open failed" message.
29
+ class ChannelOpenFailed < Exception
30
+ attr_reader :code, :reason
31
+
32
+ def initialize(code, reason)
33
+ @code, @reason = code, reason
34
+ super "#{reason} (#{code})"
35
+ end
36
+ end
37
+
38
+ # Raised when the cached key for a particular host does not match the
39
+ # key given by the host, which can be indicative of a man-in-the-middle
40
+ # attack. When rescuing this exception, you can inspect the key fingerprint
41
+ # and, if you want to proceed anyway, simply call the remember_host!
42
+ # method on the exception, and then retry.
43
+ class HostKeyMismatch < Exception
44
+ # the callback to use when #remember_host! is called
45
+ attr_writer :callback #:nodoc:
46
+
47
+ # situation-specific data describing the host (see #host, #port, etc.)
48
+ attr_writer :data #:nodoc:
49
+
50
+ # An accessor for getting at the data that was used to look up the host
51
+ # (see also #fingerprint, #host, #port, #ip, and #key).
52
+ def [](key)
53
+ @data && @data[key]
54
+ end
55
+
56
+ # Returns the fingerprint of the key for the host, which either was not
57
+ # found or did not match.
58
+ def fingerprint
59
+ @data && @data[:fingerprint]
60
+ end
61
+
62
+ # Returns the host name for the remote host, as reported by the socket.
63
+ def host
64
+ @data && @data[:peer] && @data[:peer][:host]
65
+ end
66
+
67
+ # Returns the port number for the remote host, as reported by the socket.
68
+ def port
69
+ @data && @data[:peer] && @data[:peer][:port]
70
+ end
71
+
72
+ # Returns the IP address of the remote host, as reported by the socket.
73
+ def ip
74
+ @data && @data[:peer] && @data[:peer][:ip]
75
+ end
76
+
77
+ # Returns the key itself, as reported by the remote host.
78
+ def key
79
+ @data && @data[:key]
80
+ end
81
+
82
+ # Tell Net::SSH to record this host and key in the known hosts file, so
83
+ # that subsequent connections will remember them.
84
+ def remember_host!
85
+ @callback.call
86
+ end
87
+ end
88
+ end; end
@@ -0,0 +1,107 @@
1
+ require 'net/ssh/transport/openssl'
2
+ require 'net/ssh/prompt'
3
+
4
+ module Net; module SSH
5
+
6
+ # A factory class for returning new Key classes. It is used for obtaining
7
+ # OpenSSL key instances via their SSH names, and for loading both public and
8
+ # private keys. It used used primarily by Net::SSH itself, internally, and
9
+ # will rarely (if ever) be directly used by consumers of the library.
10
+ #
11
+ # klass = Net::SSH::KeyFactory.get("rsa")
12
+ # assert klass.is_a?(OpenSSL::PKey::RSA)
13
+ #
14
+ # key = Net::SSH::KeyFactory.load_public_key("~/.ssh/id_dsa.pub")
15
+ class KeyFactory
16
+ # Specifies the mapping of SSH names to OpenSSL key classes.
17
+ MAP = {
18
+ "dh" => OpenSSL::PKey::DH,
19
+ "rsa" => OpenSSL::PKey::RSA,
20
+ "dsa" => OpenSSL::PKey::DSA,
21
+ }
22
+ if defined?(OpenSSL::PKey::EC)
23
+ MAP["ecdsa"] = OpenSSL::PKey::EC
24
+ end
25
+
26
+ class <<self
27
+ include Prompt
28
+
29
+ # Fetch an OpenSSL key instance by its SSH name. It will be a new,
30
+ # empty key of the given type.
31
+ def get(name)
32
+ MAP.fetch(name).new
33
+ end
34
+
35
+ # Loads a private key from a file. It will correctly determine
36
+ # whether the file describes an RSA or DSA key, and will load it
37
+ # appropriately. The new key is returned. If the key itself is
38
+ # encrypted (requiring a passphrase to use), the user will be
39
+ # prompted to enter their password unless passphrase works.
40
+ def load_private_key(filename, passphrase=nil, ask_passphrase=true)
41
+ data = File.read(File.expand_path(filename))
42
+ load_data_private_key(data, passphrase, ask_passphrase, filename)
43
+ end
44
+
45
+ # Loads a private key. It will correctly determine
46
+ # whether the file describes an RSA or DSA key, and will load it
47
+ # appropriately. The new key is returned. If the key itself is
48
+ # encrypted (requiring a passphrase to use), the user will be
49
+ # prompted to enter their password unless passphrase works.
50
+ def load_data_private_key(data, passphrase=nil, ask_passphrase=true, filename="")
51
+ if data.match(/-----BEGIN DSA PRIVATE KEY-----/)
52
+ key_type = OpenSSL::PKey::DSA
53
+ elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/)
54
+ key_type = OpenSSL::PKey::RSA
55
+ elsif data.match(/-----BEGIN EC PRIVATE KEY-----/) && defined?(OpenSSL::PKey::EC)
56
+ key_type = OpenSSL::PKey::EC
57
+ elsif data.match(/-----BEGIN (.*) PRIVATE KEY-----/)
58
+ raise OpenSSL::PKey::PKeyError, "not a supported key type '#{$1}'"
59
+ else
60
+ raise OpenSSL::PKey::PKeyError, "not a private key (#{filename})"
61
+ end
62
+
63
+ encrypted_key = data.match(/ENCRYPTED/)
64
+ tries = 0
65
+
66
+ begin
67
+ return key_type.new(data, passphrase || 'invalid')
68
+ rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError => e
69
+ if encrypted_key && ask_passphrase
70
+ tries += 1
71
+ if tries <= 3
72
+ passphrase = prompt("Enter passphrase for #{filename}:", false)
73
+ retry
74
+ else
75
+ raise
76
+ end
77
+ else
78
+ raise
79
+ end
80
+ end
81
+ end
82
+
83
+ # Loads a public key from a file. It will correctly determine whether
84
+ # the file describes an RSA or DSA key, and will load it
85
+ # appropriately. The new public key is returned.
86
+ def load_public_key(filename)
87
+ data = File.read(File.expand_path(filename))
88
+ load_data_public_key(data, filename)
89
+ end
90
+
91
+ # Loads a public key. It will correctly determine whether
92
+ # the file describes an RSA or DSA key, and will load it
93
+ # appropriately. The new public key is returned.
94
+ def load_data_public_key(data, filename="")
95
+ type, blob = data.split(/ /)
96
+
97
+ raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil?
98
+
99
+ blob = blob.unpack("m*").first
100
+ reader = Net::SSH::Buffer.new(blob)
101
+ reader.read_key or raise OpenSSL::PKey::PKeyError, "not a public key #{filename.inspect}"
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ end; end
@@ -0,0 +1,141 @@
1
+ require 'strscan'
2
+ require 'net/ssh/buffer'
3
+
4
+ module Net; module SSH
5
+
6
+ # Searches an OpenSSH-style known-host file for a given host, and returns all
7
+ # matching keys. This is used to implement host-key verification, as well as
8
+ # to determine what key a user prefers to use for a given host.
9
+ #
10
+ # This is used internally by Net::SSH, and will never need to be used directly
11
+ # by consumers of the library.
12
+ class KnownHosts
13
+
14
+ if defined?(OpenSSL::PKey::EC)
15
+ SUPPORTED_TYPE = %w(ssh-rsa ssh-dss
16
+ ecdsa-sha2-nistp256
17
+ ecdsa-sha2-nistp384
18
+ ecdsa-sha2-nistp521)
19
+ else
20
+ SUPPORTED_TYPE = %w(ssh-rsa ssh-dss)
21
+ end
22
+
23
+
24
+ class <<self
25
+
26
+ # Searches all known host files (see KnownHosts.hostfiles) for all keys
27
+ # of the given host. Returns an array of keys found.
28
+ def search_for(host, options={})
29
+ search_in(hostfiles(options), host)
30
+ end
31
+
32
+ # Search for all known keys for the given host, in every file given in
33
+ # the +files+ array. Returns the list of keys.
34
+ def search_in(files, host)
35
+ files.map { |file| KnownHosts.new(file).keys_for(host) }.flatten
36
+ end
37
+
38
+ # Looks in the given +options+ hash for the :user_known_hosts_file and
39
+ # :global_known_hosts_file keys, and returns an array of all known
40
+ # hosts files. If the :user_known_hosts_file key is not set, the
41
+ # default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
42
+ # :global_known_hosts_file is not set, the default is used
43
+ # (/etc/ssh/known_hosts and /etc/ssh/known_hosts2).
44
+ #
45
+ # If you only want the user known host files, you can pass :user as
46
+ # the second option.
47
+ def hostfiles(options, which=:all)
48
+ files = []
49
+
50
+ if which == :all || which == :user
51
+ files += Array(options[:user_known_hosts_file] || %w(~/.ssh/known_hosts ~/.ssh/known_hosts2))
52
+ end
53
+
54
+ if which == :all || which == :global
55
+ files += Array(options[:global_known_hosts_file] || %w(/etc/ssh/known_hosts /etc/ssh/known_hosts2))
56
+ end
57
+
58
+ return files
59
+ end
60
+
61
+ # Looks in all user known host files (see KnownHosts.hostfiles) and tries to
62
+ # add an entry for the given host and key to the first file it is able
63
+ # to.
64
+ def add(host, key, options={})
65
+ hostfiles(options, :user).each do |file|
66
+ begin
67
+ KnownHosts.new(file).add(host, key)
68
+ return
69
+ rescue SystemCallError
70
+ # try the next hostfile
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ # The host-key file name that this KnownHosts instance will use to search
77
+ # for keys.
78
+ attr_reader :source
79
+
80
+ # Instantiate a new KnownHosts instance that will search the given known-hosts
81
+ # file. The path is expanded file File.expand_path.
82
+ def initialize(source)
83
+ @source = File.expand_path(source)
84
+ end
85
+
86
+ # Returns an array of all keys that are known to be associatd with the
87
+ # given host. The +host+ parameter is either the domain name or ip address
88
+ # of the host, or both (comma-separated). Additionally, if a non-standard
89
+ # port is being used, it may be specified by putting the host (or ip, or
90
+ # both) in square brackets, and appending the port outside the brackets
91
+ # after a colon. Possible formats for +host+, then, are;
92
+ #
93
+ # "net.ssh.test"
94
+ # "1.2.3.4"
95
+ # "net.ssh.test,1.2.3.4"
96
+ # "[net.ssh.test]:5555"
97
+ # "[1,2,3,4]:5555"
98
+ # "[net.ssh.test]:5555,[1.2.3.4]:5555
99
+ def keys_for(host)
100
+ keys = []
101
+ return keys unless File.readable?(source)
102
+
103
+ entries = host.split(/,/)
104
+
105
+ File.open(source) do |file|
106
+ scanner = StringScanner.new("")
107
+ file.each_line do |line|
108
+ scanner.string = line
109
+
110
+ scanner.skip(/\s*/)
111
+ next if scanner.match?(/$|#/)
112
+
113
+ hostlist = scanner.scan(/\S+/).split(/,/)
114
+ next unless entries.all? { |entry| hostlist.include?(entry) }
115
+
116
+ scanner.skip(/\s*/)
117
+ type = scanner.scan(/\S+/)
118
+
119
+ next unless SUPPORTED_TYPE.include?(type)
120
+
121
+ scanner.skip(/\s*/)
122
+ blob = scanner.rest.unpack("m*").first
123
+ keys << Net::SSH::Buffer.new(blob).read_key
124
+ end
125
+ end
126
+
127
+ keys
128
+ end
129
+
130
+ # Tries to append an entry to the current source file for the given host
131
+ # and key. If it is unable to (because the file is not writable, for
132
+ # instance), an exception will be raised.
133
+ def add(host, key)
134
+ File.open(source, "a") do |file|
135
+ blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
136
+ file.puts "#{host} #{key.ssh_type} #{blob}"
137
+ end
138
+ end
139
+
140
+ end
141
+ end; end