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