]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/golang.org/x/crypto/ssh/transport.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / golang.org / x / crypto / ssh / transport.go
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 }