Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(487)

Delta Between Two Patch Sets: ssh/handshake.go

Issue 14494058: code review 14494058: go.crypto/ssh: support rekeying in both directions. (Closed)
Left Patch Set: diff -r 5ff5636e18c9 https://code.google.com/p/go.crypto Created 10 years, 5 months ago
Right Patch Set: diff -r cd1eea1eb828 https://code.google.com/p/go.crypto Created 10 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« ssh/common.go ('K') | « ssh/common.go ('k') | ssh/handshake_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2011 The Go Authors. All rights reserved. 1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package ssh 5 package ssh
6 6
7 import ( 7 import (
8 "errors" 8 "errors"
9 "fmt" 9 "fmt"
10 "io" 10 "io"
11 "log" 11 "log"
12 "net" 12 "net"
13 "sync" 13 "sync"
14 ) 14 )
15 15
16 // If set, debug will log print messages sent and received. 16 // If set, debug will log print messages sent and received.
17 //const debug = false
18 const debug = false 17 const debug = false
19 18
20 // keyingTransport is packet based transport that supports key 19 // keyingTransport is a packet based transport that supports key
21 // changes. It need not be thread-safe. It should pass through 20 // changes. It need not be thread-safe. It should pass through
22 // msgNewKeys in both directions. 21 // msgNewKeys in both directions.
23 type keyingTransport interface { 22 type keyingTransport interface {
24 packetConn 23 packetConn
25 24
26 // prepareKeyChange sets up a key change. The key change for a 25 // prepareKeyChange sets up a key change. The key change for a
27 // direction will be effected if a msgNewKeys message is sent 26 // direction will be effected if a msgNewKeys message is sent
28 // or received. 27 // or received.
29 prepareKeyChange(*algorithms, *kexResult) error 28 prepareKeyChange(*algorithms, *kexResult) error
30 29
31 » // Returns the session ID. prepareKeyChange must have been 30 » // getSessionID returns the session ID. prepareKeyChange must
32 » // called once. 31 » // have been called once.
33 » SessionID() []byte 32 » getSessionID() []byte
33 }
34
35 // rekeyingTransport is the interface of handshakeTransport that we
36 // (internally) expose to ClientConn and ServerConn.
37 type rekeyingTransport interface {
38 » packetConn
39
40 » // requestKeyChange asks the remote side to change keys. All
41 » // writes are blocked until the key change succeeds, which is
42 » // signaled by reading a msgNewKeys.
43 » requestKeyChange() error
44
45 » // getSessionID returns the session ID. This is only valid
46 » // after the first key change has completed.
47 » getSessionID() []byte
34 } 48 }
35 49
36 // handshakeTransport implements rekeying on top of a keyingTransport 50 // handshakeTransport implements rekeying on top of a keyingTransport
37 // and offers a thread-safe writePacket() interface. 51 // and offers a thread-safe writePacket() interface.
38 type handshakeTransport struct { 52 type handshakeTransport struct {
39 conn keyingTransport 53 conn keyingTransport
40 config *CryptoConfig 54 config *CryptoConfig
41 55
42 » // (why is Rand not part of CryptoConfig?) 56 » // TODO(hanwen): move Rand into CryptoConfig.
43 rand func() io.Reader 57 rand func() io.Reader
44 58
45 serverVersion []byte 59 serverVersion []byte
46 clientVersion []byte 60 clientVersion []byte
47 61
48 hostKeys []Signer // If hostKeys are given, we are the server. 62 hostKeys []Signer // If hostKeys are given, we are the server.
49 63
50 // On read error, incoming is closed, and readError is set. 64 // On read error, incoming is closed, and readError is set.
51 incoming chan []byte 65 incoming chan []byte
52 readError error 66 readError error
53 67
54 // data for host key checking 68 // data for host key checking
55 checker HostKeyChecker 69 checker HostKeyChecker
56 dialAddress string 70 dialAddress string
57 remoteAddr net.Addr 71 remoteAddr net.Addr
58 72
59 » kexLimit uint64 // rekey after sending/receiving this much data. 73 » rekeyThreshold uint64 // rekey after sending/receiving this much data.
60 » readSinceKex uint64 74 » readSinceKex uint64
61 75
62 // Protects the writing side of the connection 76 // Protects the writing side of the connection
63 mu sync.Mutex 77 mu sync.Mutex
64 cond *sync.Cond 78 cond *sync.Cond
65 sentInitPacket []byte 79 sentInitPacket []byte
66 sentInitMsg *kexInitMsg 80 sentInitMsg *kexInitMsg
67 writtenSinceKex uint64 81 writtenSinceKex uint64
68 writeError error 82 writeError error
69 } 83 }
70 84
(...skipping 20 matching lines...) Expand all
91 105
92 func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt e, config *ServerConfig) *handshakeTransport { 106 func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt e, config *ServerConfig) *handshakeTransport {
93 t := newHandshakeTransport(conn, clientVersion, serverVersion) 107 t := newHandshakeTransport(conn, clientVersion, serverVersion)
94 t.setCryptoConfig(&config.Crypto) 108 t.setCryptoConfig(&config.Crypto)
95 t.hostKeys = config.hostKeys 109 t.hostKeys = config.hostKeys
96 t.rand = config.rand 110 t.rand = config.rand
97 go t.readLoop() 111 go t.readLoop()
98 return t 112 return t
99 } 113 }
100 114
101 func (t *handshakeTransport) SessionID() []byte { 115 func (t *handshakeTransport) getSessionID() []byte {
102 » return t.conn.SessionID() 116 » return t.conn.getSessionID()
103 } 117 }
104 118
105 func (t *handshakeTransport) setCryptoConfig(c *CryptoConfig) { 119 func (t *handshakeTransport) setCryptoConfig(c *CryptoConfig) {
106 t.config = c 120 t.config = c
107 » t.kexLimit = t.config.RekeyThreshold 121 » t.rekeyThreshold = t.config.RekeyThreshold
108 » if t.kexLimit == 0 { 122 » if t.rekeyThreshold == 0 {
109 // RFC 4253, section 9 suggests rekeying after 1G. 123 // RFC 4253, section 9 suggests rekeying after 1G.
110 » » t.kexLimit = 1 << 30 124 » » t.rekeyThreshold = 1 << 30
111 } 125 }
112 } 126 }
113 127
114 func (t *handshakeTransport) id() string { 128 func (t *handshakeTransport) id() string {
115 if len(t.hostKeys) > 0 { 129 if len(t.hostKeys) > 0 {
116 return "server" 130 return "server"
117 } 131 }
118 return "client" 132 return "client"
119 } 133 }
120 134
(...skipping 14 matching lines...) Expand all
135 break 149 break
136 } 150 }
137 if p[0] == msgIgnore || p[0] == msgDebug { 151 if p[0] == msgIgnore || p[0] == msgDebug {
138 continue 152 continue
139 } 153 }
140 t.incoming <- p 154 t.incoming <- p
141 } 155 }
142 } 156 }
143 157
144 func (t *handshakeTransport) readOnePacket() ([]byte, error) { 158 func (t *handshakeTransport) readOnePacket() ([]byte, error) {
145 » if t.readSinceKex > t.kexLimit { 159 » if t.readSinceKex > t.rekeyThreshold {
146 » » _, _, err := t.sendKexInit() 160 » » if err := t.requestKeyChange(); err != nil {
147 » » if err != nil {
148 return nil, err 161 return nil, err
149 } 162 }
150 } 163 }
151 164
152 p, err := t.conn.readPacket() 165 p, err := t.conn.readPacket()
153 if err != nil { 166 if err != nil {
154 return nil, err 167 return nil, err
155 } 168 }
156 169
157 t.readSinceKex += uint64(len(p)) 170 t.readSinceKex += uint64(len(p))
158 if debug { 171 if debug {
159 msg, err := decode(p) 172 msg, err := decode(p)
160 log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err) 173 log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err)
161 } 174 }
162 » if p[0] == msgKexInit { 175 » if p[0] != msgKexInit {
163 » » err = t.enterKeyExchange(p)
164
165 » » t.mu.Lock()
166 » » if err != nil {
167 » » » // drop connection
168 » » » t.conn.Close()
169 » » » t.writeError = err
170 » » }
171
172 » » if debug {
173 » » » log.Printf("%s exited key exchange, err %v", t.id(), err )
174 » » }
175
176 » » // Unblock writers.
177 » » t.sentInitMsg = nil
178 » » t.sentInitPacket = nil
179 » » t.cond.Broadcast()
180 » » t.writtenSinceKex = 0
181 » » t.mu.Unlock()
182
183 » » if err != nil {
184 » » » return nil, err
185 » » }
186
187 » » t.readSinceKex = 0
188 » » return []byte{msgNewKeys}, nil
189 » } else {
190 return p, nil 176 return p, nil
191 } 177 }
178 err = t.enterKeyExchange(p)
179
180 t.mu.Lock()
181 if err != nil {
182 // drop connection
183 t.conn.Close()
184 t.writeError = err
185 }
186
187 if debug {
188 log.Printf("%s exited key exchange, err %v", t.id(), err)
189 }
190
191 // Unblock writers.
192 t.sentInitMsg = nil
193 t.sentInitPacket = nil
194 t.cond.Broadcast()
195 t.writtenSinceKex = 0
196 t.mu.Unlock()
197
198 if err != nil {
199 return nil, err
200 }
201
202 t.readSinceKex = 0
203 return []byte{msgNewKeys}, nil
192 } 204 }
193 205
194 // sendKexInit sends a key change message, and returns the message 206 // sendKexInit sends a key change message, and returns the message
195 // that was sent. A failed key change will close the underlying 207 // that was sent. After initiating the key change, all writes will be
196 // transport. This function is safe for concurrent use by multiple 208 // blocked until the change is done, and a failed key change will
197 // goroutines. 209 // close the underlying transport. This function is safe for
210 // concurrent use by multiple goroutines.
198 func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) { 211 func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) {
199 t.mu.Lock() 212 t.mu.Lock()
200 defer t.mu.Unlock() 213 defer t.mu.Unlock()
201 » return t.unlockedSendKexInit() 214 » return t.sendKexInitLocked()
202 } 215 }
203 216
204 func (t *handshakeTransport) unlockedSendKexInit() (*kexInitMsg, []byte, error) { 217 func (t *handshakeTransport) requestKeyChange() error {
218 » _, _, err := t.sendKexInit()
219 » return err
220 }
221
222 // sendKexInitLocked sends a key change message. t.mu must be locked
223 // while this happens.
224 func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) {
225 » // kexInits may be sent either in response to the other side,
226 » // or because our side wants to initiate a key change, so we
227 » // may have already sent a kexInit. In that case, don't send a
228 » // second kexInit.
205 if t.sentInitMsg != nil { 229 if t.sentInitMsg != nil {
206 // Since send and receive of kex inits are not
207 // ordered, so it may happen that another key change
208 // already was underway and we sent an init already.
209 return t.sentInitMsg, t.sentInitPacket, nil 230 return t.sentInitMsg, t.sentInitPacket, nil
210 } 231 }
211 msg := &kexInitMsg{ 232 msg := &kexInitMsg{
212 KexAlgos: t.config.kexes(), 233 KexAlgos: t.config.kexes(),
213 CiphersClientServer: t.config.ciphers(), 234 CiphersClientServer: t.config.ciphers(),
214 CiphersServerClient: t.config.ciphers(), 235 CiphersServerClient: t.config.ciphers(),
215 MACsClientServer: t.config.macs(), 236 MACsClientServer: t.config.macs(),
216 MACsServerClient: t.config.macs(), 237 MACsServerClient: t.config.macs(),
217 CompressionClientServer: supportedCompressions, 238 CompressionClientServer: supportedCompressions,
218 CompressionServerClient: supportedCompressions, 239 CompressionServerClient: supportedCompressions,
219 } 240 }
241
242 // TODO(hanwen): add random bits to kexInit.Cookie.
243
220 if len(t.hostKeys) > 0 { 244 if len(t.hostKeys) > 0 {
221 for _, k := range t.hostKeys { 245 for _, k := range t.hostKeys {
222 msg.ServerHostKeyAlgos = append( 246 msg.ServerHostKeyAlgos = append(
223 msg.ServerHostKeyAlgos, k.PublicKey().PublicKeyA lgo()) 247 msg.ServerHostKeyAlgos, k.PublicKey().PublicKeyA lgo())
224 } 248 }
225 } else { 249 } else {
226 msg.ServerHostKeyAlgos = supportedHostKeyAlgos 250 msg.ServerHostKeyAlgos = supportedHostKeyAlgos
227 } 251 }
228 packet := marshal(msgKexInit, *msg) 252 packet := marshal(msgKexInit, *msg)
229 253
230 // writePacket destroys the contents, so save a copy. 254 // writePacket destroys the contents, so save a copy.
231 packetCopy := make([]byte, len(packet)) 255 packetCopy := make([]byte, len(packet))
232 copy(packetCopy, packet) 256 copy(packetCopy, packet)
233 257
234 if err := t.conn.writePacket(packetCopy); err != nil { 258 if err := t.conn.writePacket(packetCopy); err != nil {
235 return nil, nil, err 259 return nil, nil, err
236 } 260 }
237 261
238 t.sentInitMsg = msg 262 t.sentInitMsg = msg
239 t.sentInitPacket = packet 263 t.sentInitPacket = packet
240 return msg, packet, nil 264 return msg, packet, nil
241 } 265 }
242 266
243 func (t *handshakeTransport) writePacket(p []byte) error { 267 func (t *handshakeTransport) writePacket(p []byte) error {
244 t.mu.Lock() 268 t.mu.Lock()
245 » if t.writtenSinceKex > t.kexLimit { 269 » if t.writtenSinceKex > t.rekeyThreshold {
246 » » t.unlockedSendKexInit() 270 » » t.sendKexInitLocked()
247 } 271 }
248 for t.sentInitMsg != nil { 272 for t.sentInitMsg != nil {
249 t.cond.Wait() 273 t.cond.Wait()
250 } 274 }
251 if t.writeError != nil { 275 if t.writeError != nil {
252 return t.writeError 276 return t.writeError
253 } 277 }
254 t.writtenSinceKex += uint64(len(p)) 278 t.writtenSinceKex += uint64(len(p))
255 279
256 var err error 280 var err error
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
300 324
301 magics.clientKexInit = myInitPacket 325 magics.clientKexInit = myInitPacket
302 magics.serverKexInit = otherInitPacket 326 magics.serverKexInit = otherInitPacket
303 } 327 }
304 328
305 algs := findAgreedAlgorithms(clientInit, serverInit) 329 algs := findAgreedAlgorithms(clientInit, serverInit)
306 if algs == nil { 330 if algs == nil {
307 return errors.New("ssh: no common algorithms") 331 return errors.New("ssh: no common algorithms")
308 } 332 }
309 333
310 » // TODO(hanwen): add random bits to kexInit.Cookie. 334 » // We don't send FirstKexFollows, but we handle receiving it.
311
312 » // We don't send FirstKexFollows, but we handle receiving it
313 if otherInit.FirstKexFollows && algs.kex != otherInit.KexAlgos[0] { 335 if otherInit.FirstKexFollows && algs.kex != otherInit.KexAlgos[0] {
314 // other side sent a kex message for the wrong algorithm, 336 // other side sent a kex message for the wrong algorithm,
315 // which we have to ignore. 337 // which we have to ignore.
316 if _, err := t.conn.readPacket(); err != nil { 338 if _, err := t.conn.readPacket(); err != nil {
317 return err 339 return err
318 } 340 }
319 } 341 }
320 342
321 kex, ok := kexAlgoMap[algs.kex] 343 kex, ok := kexAlgoMap[algs.kex]
322 if !ok { 344 if !ok {
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 392
371 if t.checker != nil { 393 if t.checker != nil {
372 err = t.checker.Check(t.dialAddress, t.remoteAddr, algs.hostKey, result.HostKey) 394 err = t.checker.Check(t.dialAddress, t.remoteAddr, algs.hostKey, result.HostKey)
373 if err != nil { 395 if err != nil {
374 return nil, err 396 return nil, err
375 } 397 }
376 } 398 }
377 399
378 return result, nil 400 return result, nil
379 } 401 }
380
381 // verifyHostKeySignature verifies the host key obtained in the key
382 // exchange.
383 func verifyHostKeySignature(hostKeyAlgo string, result *kexResult) error {
384 hostKey, rest, ok := ParsePublicKey(result.HostKey)
385 if len(rest) > 0 || !ok {
386 return errors.New("ssh: could not parse hostkey")
387 }
388
389 sig, rest, ok := parseSignatureBody(result.Signature)
390 if len(rest) > 0 || !ok {
391 return errors.New("ssh: signature parse error")
392 }
393 if sig.Format != hostKeyAlgo {
394 return fmt.Errorf("ssh: got signature type %q, want %q", sig.For mat, hostKeyAlgo)
395 }
396
397 if !hostKey.Verify(result.H, sig.Blob) {
398 return errors.New("ssh: host key signature error")
399 }
400 return nil
401 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b