diff options
Diffstat (limited to 'vendor/golang.org/x/crypto/ssh/transport.go')
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/transport.go | 375 |
1 files changed, 0 insertions, 375 deletions
diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go deleted file mode 100644 index f9780e0..0000000 --- a/vendor/golang.org/x/crypto/ssh/transport.go +++ /dev/null | |||
@@ -1,375 +0,0 @@ | |||
1 | // Copyright 2011 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package ssh | ||
6 | |||
7 | import ( | ||
8 | "bufio" | ||
9 | "errors" | ||
10 | "io" | ||
11 | "log" | ||
12 | ) | ||
13 | |||
14 | // debugTransport if set, will print packet types as they go over the | ||
15 | // wire. No message decoding is done, to minimize the impact on timing. | ||
16 | const debugTransport = false | ||
17 | |||
18 | const ( | ||
19 | gcmCipherID = "aes128-gcm@openssh.com" | ||
20 | aes128cbcID = "aes128-cbc" | ||
21 | tripledescbcID = "3des-cbc" | ||
22 | ) | ||
23 | |||
24 | // packetConn represents a transport that implements packet based | ||
25 | // operations. | ||
26 | type packetConn interface { | ||
27 | // Encrypt and send a packet of data to the remote peer. | ||
28 | writePacket(packet []byte) error | ||
29 | |||
30 | // Read a packet from the connection. The read is blocking, | ||
31 | // i.e. if error is nil, then the returned byte slice is | ||
32 | // always non-empty. | ||
33 | readPacket() ([]byte, error) | ||
34 | |||
35 | // Close closes the write-side of the connection. | ||
36 | Close() error | ||
37 | } | ||
38 | |||
39 | // transport is the keyingTransport that implements the SSH packet | ||
40 | // protocol. | ||
41 | type transport struct { | ||
42 | reader connectionState | ||
43 | writer connectionState | ||
44 | |||
45 | bufReader *bufio.Reader | ||
46 | bufWriter *bufio.Writer | ||
47 | rand io.Reader | ||
48 | isClient bool | ||
49 | io.Closer | ||
50 | } | ||
51 | |||
52 | // packetCipher represents a combination of SSH encryption/MAC | ||
53 | // protocol. A single instance should be used for one direction only. | ||
54 | type packetCipher interface { | ||
55 | // writePacket encrypts the packet and writes it to w. The | ||
56 | // contents of the packet are generally scrambled. | ||
57 | writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error | ||
58 | |||
59 | // readPacket reads and decrypts a packet of data. The | ||
60 | // returned packet may be overwritten by future calls of | ||
61 | // readPacket. | ||
62 | readPacket(seqnum uint32, r io.Reader) ([]byte, error) | ||
63 | } | ||
64 | |||
65 | // connectionState represents one side (read or write) of the | ||
66 | // connection. This is necessary because each direction has its own | ||
67 | // keys, and can even have its own algorithms | ||
68 | type connectionState struct { | ||
69 | packetCipher | ||
70 | seqNum uint32 | ||
71 | dir direction | ||
72 | pendingKeyChange chan packetCipher | ||
73 | } | ||
74 | |||
75 | // prepareKeyChange sets up key material for a keychange. The key changes in | ||
76 | // both directions are triggered by reading and writing a msgNewKey packet | ||
77 | // respectively. | ||
78 | func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { | ||
79 | if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { | ||
80 | return err | ||
81 | } else { | ||
82 | t.reader.pendingKeyChange <- ciph | ||
83 | } | ||
84 | |||
85 | if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil { | ||
86 | return err | ||
87 | } else { | ||
88 | t.writer.pendingKeyChange <- ciph | ||
89 | } | ||
90 | |||
91 | return nil | ||
92 | } | ||
93 | |||
94 | func (t *transport) printPacket(p []byte, write bool) { | ||
95 | if len(p) == 0 { | ||
96 | return | ||
97 | } | ||
98 | who := "server" | ||
99 | if t.isClient { | ||
100 | who = "client" | ||
101 | } | ||
102 | what := "read" | ||
103 | if write { | ||
104 | what = "write" | ||
105 | } | ||
106 | |||
107 | log.Println(what, who, p[0]) | ||
108 | } | ||
109 | |||
110 | // Read and decrypt next packet. | ||
111 | func (t *transport) readPacket() (p []byte, err error) { | ||
112 | for { | ||
113 | p, err = t.reader.readPacket(t.bufReader) | ||
114 | if err != nil { | ||
115 | break | ||
116 | } | ||
117 | if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { | ||
118 | break | ||
119 | } | ||
120 | } | ||
121 | if debugTransport { | ||
122 | t.printPacket(p, false) | ||
123 | } | ||
124 | |||
125 | return p, err | ||
126 | } | ||
127 | |||
128 | func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { | ||
129 | packet, err := s.packetCipher.readPacket(s.seqNum, r) | ||
130 | s.seqNum++ | ||
131 | if err == nil && len(packet) == 0 { | ||
132 | err = errors.New("ssh: zero length packet") | ||
133 | } | ||
134 | |||
135 | if len(packet) > 0 { | ||
136 | switch packet[0] { | ||
137 | case msgNewKeys: | ||
138 | select { | ||
139 | case cipher := <-s.pendingKeyChange: | ||
140 | s.packetCipher = cipher | ||
141 | default: | ||
142 | return nil, errors.New("ssh: got bogus newkeys message.") | ||
143 | } | ||
144 | |||
145 | case msgDisconnect: | ||
146 | // Transform a disconnect message into an | ||
147 | // error. Since this is lowest level at which | ||
148 | // we interpret message types, doing it here | ||
149 | // ensures that we don't have to handle it | ||
150 | // elsewhere. | ||
151 | var msg disconnectMsg | ||
152 | if err := Unmarshal(packet, &msg); err != nil { | ||
153 | return nil, err | ||
154 | } | ||
155 | return nil, &msg | ||
156 | } | ||
157 | } | ||
158 | |||
159 | // The packet may point to an internal buffer, so copy the | ||
160 | // packet out here. | ||
161 | fresh := make([]byte, len(packet)) | ||
162 | copy(fresh, packet) | ||
163 | |||
164 | return fresh, err | ||
165 | } | ||
166 | |||
167 | func (t *transport) writePacket(packet []byte) error { | ||
168 | if debugTransport { | ||
169 | t.printPacket(packet, true) | ||
170 | } | ||
171 | return t.writer.writePacket(t.bufWriter, t.rand, packet) | ||
172 | } | ||
173 | |||
174 | func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { | ||
175 | changeKeys := len(packet) > 0 && packet[0] == msgNewKeys | ||
176 | |||
177 | err := s.packetCipher.writePacket(s.seqNum, w, rand, packet) | ||
178 | if err != nil { | ||
179 | return err | ||
180 | } | ||
181 | if err = w.Flush(); err != nil { | ||
182 | return err | ||
183 | } | ||
184 | s.seqNum++ | ||
185 | if changeKeys { | ||
186 | select { | ||
187 | case cipher := <-s.pendingKeyChange: | ||
188 | s.packetCipher = cipher | ||
189 | default: | ||
190 | panic("ssh: no key material for msgNewKeys") | ||
191 | } | ||
192 | } | ||
193 | return err | ||
194 | } | ||
195 | |||
196 | func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport { | ||
197 | t := &transport{ | ||
198 | bufReader: bufio.NewReader(rwc), | ||
199 | bufWriter: bufio.NewWriter(rwc), | ||
200 | rand: rand, | ||
201 | reader: connectionState{ | ||
202 | packetCipher: &streamPacketCipher{cipher: noneCipher{}}, | ||
203 | pendingKeyChange: make(chan packetCipher, 1), | ||
204 | }, | ||
205 | writer: connectionState{ | ||
206 | packetCipher: &streamPacketCipher{cipher: noneCipher{}}, | ||
207 | pendingKeyChange: make(chan packetCipher, 1), | ||
208 | }, | ||
209 | Closer: rwc, | ||
210 | } | ||
211 | t.isClient = isClient | ||
212 | |||
213 | if isClient { | ||
214 | t.reader.dir = serverKeys | ||
215 | t.writer.dir = clientKeys | ||
216 | } else { | ||
217 | t.reader.dir = clientKeys | ||
218 | t.writer.dir = serverKeys | ||
219 | } | ||
220 | |||
221 | return t | ||
222 | } | ||
223 | |||
224 | type direction struct { | ||
225 | ivTag []byte | ||
226 | keyTag []byte | ||
227 | macKeyTag []byte | ||
228 | } | ||
229 | |||
230 | var ( | ||
231 | serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} | ||
232 | clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} | ||
233 | ) | ||
234 | |||
235 | // generateKeys generates key material for IV, MAC and encryption. | ||
236 | func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) { | ||
237 | cipherMode := cipherModes[algs.Cipher] | ||
238 | macMode := macModes[algs.MAC] | ||
239 | |||
240 | iv = make([]byte, cipherMode.ivSize) | ||
241 | key = make([]byte, cipherMode.keySize) | ||
242 | macKey = make([]byte, macMode.keySize) | ||
243 | |||
244 | generateKeyMaterial(iv, d.ivTag, kex) | ||
245 | generateKeyMaterial(key, d.keyTag, kex) | ||
246 | generateKeyMaterial(macKey, d.macKeyTag, kex) | ||
247 | return | ||
248 | } | ||
249 | |||
250 | // setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as | ||
251 | // described in RFC 4253, section 6.4. direction should either be serverKeys | ||
252 | // (to setup server->client keys) or clientKeys (for client->server keys). | ||
253 | func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { | ||
254 | iv, key, macKey := generateKeys(d, algs, kex) | ||
255 | |||
256 | if algs.Cipher == gcmCipherID { | ||
257 | return newGCMCipher(iv, key, macKey) | ||
258 | } | ||
259 | |||
260 | if algs.Cipher == aes128cbcID { | ||
261 | return newAESCBCCipher(iv, key, macKey, algs) | ||
262 | } | ||
263 | |||
264 | if algs.Cipher == tripledescbcID { | ||
265 | return newTripleDESCBCCipher(iv, key, macKey, algs) | ||
266 | } | ||
267 | |||
268 | c := &streamPacketCipher{ | ||
269 | mac: macModes[algs.MAC].new(macKey), | ||
270 | etm: macModes[algs.MAC].etm, | ||
271 | } | ||
272 | c.macResult = make([]byte, c.mac.Size()) | ||
273 | |||
274 | var err error | ||
275 | c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv) | ||
276 | if err != nil { | ||
277 | return nil, err | ||
278 | } | ||
279 | |||
280 | return c, nil | ||
281 | } | ||
282 | |||
283 | // generateKeyMaterial fills out with key material generated from tag, K, H | ||
284 | // and sessionId, as specified in RFC 4253, section 7.2. | ||
285 | func generateKeyMaterial(out, tag []byte, r *kexResult) { | ||
286 | var digestsSoFar []byte | ||
287 | |||
288 | h := r.Hash.New() | ||
289 | for len(out) > 0 { | ||
290 | h.Reset() | ||
291 | h.Write(r.K) | ||
292 | h.Write(r.H) | ||
293 | |||
294 | if len(digestsSoFar) == 0 { | ||
295 | h.Write(tag) | ||
296 | h.Write(r.SessionID) | ||
297 | } else { | ||
298 | h.Write(digestsSoFar) | ||
299 | } | ||
300 | |||
301 | digest := h.Sum(nil) | ||
302 | n := copy(out, digest) | ||
303 | out = out[n:] | ||
304 | if len(out) > 0 { | ||
305 | digestsSoFar = append(digestsSoFar, digest...) | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
310 | const packageVersion = "SSH-2.0-Go" | ||
311 | |||
312 | // Sends and receives a version line. The versionLine string should | ||
313 | // be US ASCII, start with "SSH-2.0-", and should not include a | ||
314 | // newline. exchangeVersions returns the other side's version line. | ||
315 | func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) { | ||
316 | // Contrary to the RFC, we do not ignore lines that don't | ||
317 | // start with "SSH-2.0-" to make the library usable with | ||
318 | // nonconforming servers. | ||
319 | for _, c := range versionLine { | ||
320 | // The spec disallows non US-ASCII chars, and | ||
321 | // specifically forbids null chars. | ||
322 | if c < 32 { | ||
323 | return nil, errors.New("ssh: junk character in version line") | ||
324 | } | ||
325 | } | ||
326 | if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil { | ||
327 | return | ||
328 | } | ||
329 | |||
330 | them, err = readVersion(rw) | ||
331 | return them, err | ||
332 | } | ||
333 | |||
334 | // maxVersionStringBytes is the maximum number of bytes that we'll | ||
335 | // accept as a version string. RFC 4253 section 4.2 limits this at 255 | ||
336 | // chars | ||
337 | const maxVersionStringBytes = 255 | ||
338 | |||
339 | // Read version string as specified by RFC 4253, section 4.2. | ||
340 | func readVersion(r io.Reader) ([]byte, error) { | ||
341 | versionString := make([]byte, 0, 64) | ||
342 | var ok bool | ||
343 | var buf [1]byte | ||
344 | |||
345 | for len(versionString) < maxVersionStringBytes { | ||
346 | _, err := io.ReadFull(r, buf[:]) | ||
347 | if err != nil { | ||
348 | return nil, err | ||
349 | } | ||
350 | // The RFC says that the version should be terminated with \r\n | ||
351 | // but several SSH servers actually only send a \n. | ||
352 | if buf[0] == '\n' { | ||
353 | ok = true | ||
354 | break | ||
355 | } | ||
356 | |||
357 | // non ASCII chars are disallowed, but we are lenient, | ||
358 | // since Go doesn't use null-terminated strings. | ||
359 | |||
360 | // The RFC allows a comment after a space, however, | ||
361 | // all of it (version and comments) goes into the | ||
362 | // session hash. | ||
363 | versionString = append(versionString, buf[0]) | ||
364 | } | ||
365 | |||
366 | if !ok { | ||
367 | return nil, errors.New("ssh: overflow reading version string") | ||
368 | } | ||
369 | |||
370 | // There might be a '\r' on the end which we should remove. | ||
371 | if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { | ||
372 | versionString = versionString[:len(versionString)-1] | ||
373 | } | ||
374 | return versionString, nil | ||
375 | } | ||