1
2
3
4
5 package ssh
6
7 import (
8 "bufio"
9 "bytes"
10 "errors"
11 "io"
12 "log"
13 )
14
15
16
17 const debugTransport = false
18
19 const (
20 gcm128CipherID = "aes128-gcm@openssh.com"
21 gcm256CipherID = "aes256-gcm@openssh.com"
22 aes128cbcID = "aes128-cbc"
23 tripledescbcID = "3des-cbc"
24 )
25
26
27
28 type packetConn interface {
29
30 writePacket(packet []byte) error
31
32
33
34
35 readPacket() ([]byte, error)
36
37
38 Close() error
39 }
40
41
42
43 type transport struct {
44 reader connectionState
45 writer connectionState
46
47 bufReader *bufio.Reader
48 bufWriter *bufio.Writer
49 rand io.Reader
50 isClient bool
51 io.Closer
52
53 strictMode bool
54 initialKEXDone bool
55 }
56
57
58
59 type packetCipher interface {
60
61
62 writeCipherPacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
63
64
65
66
67 readCipherPacket(seqnum uint32, r io.Reader) ([]byte, error)
68 }
69
70
71
72
73 type connectionState struct {
74 packetCipher
75 seqNum uint32
76 dir direction
77 pendingKeyChange chan packetCipher
78 }
79
80 func (t *transport) setStrictMode() error {
81 if t.reader.seqNum != 1 {
82 return errors.New("ssh: sequence number != 1 when strict KEX mode requested")
83 }
84 t.strictMode = true
85 return nil
86 }
87
88 func (t *transport) setInitialKEXDone() {
89 t.initialKEXDone = true
90 }
91
92
93
94
95 func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
96 ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult)
97 if err != nil {
98 return err
99 }
100 t.reader.pendingKeyChange <- ciph
101
102 ciph, err = newPacketCipher(t.writer.dir, algs.w, kexResult)
103 if err != nil {
104 return err
105 }
106 t.writer.pendingKeyChange <- ciph
107
108 return nil
109 }
110
111 func (t *transport) printPacket(p []byte, write bool) {
112 if len(p) == 0 {
113 return
114 }
115 who := "server"
116 if t.isClient {
117 who = "client"
118 }
119 what := "read"
120 if write {
121 what = "write"
122 }
123
124 log.Println(what, who, p[0])
125 }
126
127
128 func (t *transport) readPacket() (p []byte, err error) {
129 for {
130 p, err = t.reader.readPacket(t.bufReader, t.strictMode)
131 if err != nil {
132 break
133 }
134
135 if len(p) == 0 || (t.strictMode && !t.initialKEXDone) || (p[0] != msgIgnore && p[0] != msgDebug) {
136 break
137 }
138 }
139 if debugTransport {
140 t.printPacket(p, false)
141 }
142
143 return p, err
144 }
145
146 func (s *connectionState) readPacket(r *bufio.Reader, strictMode bool) ([]byte, error) {
147 packet, err := s.packetCipher.readCipherPacket(s.seqNum, r)
148 s.seqNum++
149 if err == nil && len(packet) == 0 {
150 err = errors.New("ssh: zero length packet")
151 }
152
153 if len(packet) > 0 {
154 switch packet[0] {
155 case msgNewKeys:
156 select {
157 case cipher := <-s.pendingKeyChange:
158 s.packetCipher = cipher
159 if strictMode {
160 s.seqNum = 0
161 }
162 default:
163 return nil, errors.New("ssh: got bogus newkeys message")
164 }
165
166 case msgDisconnect:
167
168
169
170
171
172 var msg disconnectMsg
173 if err := Unmarshal(packet, &msg); err != nil {
174 return nil, err
175 }
176 return nil, &msg
177 }
178 }
179
180
181
182 fresh := make([]byte, len(packet))
183 copy(fresh, packet)
184
185 return fresh, err
186 }
187
188 func (t *transport) writePacket(packet []byte) error {
189 if debugTransport {
190 t.printPacket(packet, true)
191 }
192 return t.writer.writePacket(t.bufWriter, t.rand, packet, t.strictMode)
193 }
194
195 func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte, strictMode bool) error {
196 changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
197
198 err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet)
199 if err != nil {
200 return err
201 }
202 if err = w.Flush(); err != nil {
203 return err
204 }
205 s.seqNum++
206 if changeKeys {
207 select {
208 case cipher := <-s.pendingKeyChange:
209 s.packetCipher = cipher
210 if strictMode {
211 s.seqNum = 0
212 }
213 default:
214 panic("ssh: no key material for msgNewKeys")
215 }
216 }
217 return err
218 }
219
220 func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport {
221 t := &transport{
222 bufReader: bufio.NewReader(rwc),
223 bufWriter: bufio.NewWriter(rwc),
224 rand: rand,
225 reader: connectionState{
226 packetCipher: &streamPacketCipher{cipher: noneCipher{}},
227 pendingKeyChange: make(chan packetCipher, 1),
228 },
229 writer: connectionState{
230 packetCipher: &streamPacketCipher{cipher: noneCipher{}},
231 pendingKeyChange: make(chan packetCipher, 1),
232 },
233 Closer: rwc,
234 }
235 t.isClient = isClient
236
237 if isClient {
238 t.reader.dir = serverKeys
239 t.writer.dir = clientKeys
240 } else {
241 t.reader.dir = clientKeys
242 t.writer.dir = serverKeys
243 }
244
245 return t
246 }
247
248 type direction struct {
249 ivTag []byte
250 keyTag []byte
251 macKeyTag []byte
252 }
253
254 var (
255 serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
256 clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
257 )
258
259
260
261
262 func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
263 cipherMode := cipherModes[algs.Cipher]
264
265 iv := make([]byte, cipherMode.ivSize)
266 key := make([]byte, cipherMode.keySize)
267
268 generateKeyMaterial(iv, d.ivTag, kex)
269 generateKeyMaterial(key, d.keyTag, kex)
270
271 var macKey []byte
272 if !aeadCiphers[algs.Cipher] {
273 macMode := macModes[algs.MAC]
274 macKey = make([]byte, macMode.keySize)
275 generateKeyMaterial(macKey, d.macKeyTag, kex)
276 }
277
278 return cipherModes[algs.Cipher].create(key, iv, macKey, algs)
279 }
280
281
282
283 func generateKeyMaterial(out, tag []byte, r *kexResult) {
284 var digestsSoFar []byte
285
286 h := r.Hash.New()
287 for len(out) > 0 {
288 h.Reset()
289 h.Write(r.K)
290 h.Write(r.H)
291
292 if len(digestsSoFar) == 0 {
293 h.Write(tag)
294 h.Write(r.SessionID)
295 } else {
296 h.Write(digestsSoFar)
297 }
298
299 digest := h.Sum(nil)
300 n := copy(out, digest)
301 out = out[n:]
302 if len(out) > 0 {
303 digestsSoFar = append(digestsSoFar, digest...)
304 }
305 }
306 }
307
308 const packageVersion = "SSH-2.0-Go"
309
310
311
312
313 func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) {
314
315
316
317 for _, c := range versionLine {
318
319
320 if c < 32 {
321 return nil, errors.New("ssh: junk character in version line")
322 }
323 }
324 if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
325 return
326 }
327
328 them, err = readVersion(rw)
329 return them, err
330 }
331
332
333
334
335 const maxVersionStringBytes = 255
336
337
338 func readVersion(r io.Reader) ([]byte, error) {
339 versionString := make([]byte, 0, 64)
340 var ok bool
341 var buf [1]byte
342
343 for length := 0; length < maxVersionStringBytes; length++ {
344 _, err := io.ReadFull(r, buf[:])
345 if err != nil {
346 return nil, err
347 }
348
349
350 if buf[0] == '\n' {
351 if !bytes.HasPrefix(versionString, []byte("SSH-")) {
352
353
354
355 versionString = versionString[:0]
356 continue
357 }
358 ok = true
359 break
360 }
361
362
363
364
365
366
367
368 versionString = append(versionString, buf[0])
369 }
370
371 if !ok {
372 return nil, errors.New("ssh: overflow reading version string")
373 }
374
375
376 if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' {
377 versionString = versionString[:len(versionString)-1]
378 }
379 return versionString, nil
380 }
381
View as plain text