OLD | NEW |
(Empty) | |
| 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 "crypto" |
| 10 "crypto/cipher" |
| 11 "crypto/hmac" |
| 12 "crypto/sha1" |
| 13 "crypto/subtle" |
| 14 "errors" |
| 15 "hash" |
| 16 "io" |
| 17 "net" |
| 18 "sync" |
| 19 ) |
| 20 |
| 21 const ( |
| 22 packetSizeMultiple = 16 // TODO(huin) this should be determined by the c
ipher. |
| 23 minPacketSize = 16 |
| 24 maxPacketSize = 36000 |
| 25 minPaddingSize = 4 // TODO(huin) should this be configurable? |
| 26 ) |
| 27 |
| 28 // filteredConn reduces the set of methods exposed when embeddeding |
| 29 // a net.Conn inside ssh.transport. |
| 30 // TODO(dfc) suggestions for a better name will be warmly received. |
| 31 type filteredConn interface { |
| 32 // Close closes the connection. |
| 33 Close() error |
| 34 |
| 35 // LocalAddr returns the local network address. |
| 36 LocalAddr() net.Addr |
| 37 |
| 38 // RemoteAddr returns the remote network address. |
| 39 RemoteAddr() net.Addr |
| 40 } |
| 41 |
| 42 // Types implementing packetWriter provide the ability to send packets to |
| 43 // an SSH peer. |
| 44 type packetWriter interface { |
| 45 // Encrypt and send a packet of data to the remote peer. |
| 46 writePacket(packet []byte) error |
| 47 } |
| 48 |
| 49 // transport represents the SSH connection to the remote peer. |
| 50 type transport struct { |
| 51 reader |
| 52 writer |
| 53 |
| 54 filteredConn |
| 55 } |
| 56 |
| 57 // reader represents the incoming connection state. |
| 58 type reader struct { |
| 59 io.Reader |
| 60 common |
| 61 } |
| 62 |
| 63 // writer represnts the outgoing connection state. |
| 64 type writer struct { |
| 65 *sync.Mutex // protects writer.Writer from concurrent writes |
| 66 *bufio.Writer |
| 67 rand io.Reader |
| 68 common |
| 69 } |
| 70 |
| 71 // common represents the cipher state needed to process messages in a single |
| 72 // direction. |
| 73 type common struct { |
| 74 seqNum uint32 |
| 75 mac hash.Hash |
| 76 cipher cipher.Stream |
| 77 |
| 78 cipherAlgo string |
| 79 macAlgo string |
| 80 compressionAlgo string |
| 81 } |
| 82 |
| 83 // Read and decrypt a single packet from the remote peer. |
| 84 func (r *reader) readOnePacket() ([]byte, error) { |
| 85 var lengthBytes = make([]byte, 5) |
| 86 var macSize uint32 |
| 87 if _, err := io.ReadFull(r, lengthBytes); err != nil { |
| 88 return nil, err |
| 89 } |
| 90 |
| 91 r.cipher.XORKeyStream(lengthBytes, lengthBytes) |
| 92 |
| 93 if r.mac != nil { |
| 94 r.mac.Reset() |
| 95 seqNumBytes := []byte{ |
| 96 byte(r.seqNum >> 24), |
| 97 byte(r.seqNum >> 16), |
| 98 byte(r.seqNum >> 8), |
| 99 byte(r.seqNum), |
| 100 } |
| 101 r.mac.Write(seqNumBytes) |
| 102 r.mac.Write(lengthBytes) |
| 103 macSize = uint32(r.mac.Size()) |
| 104 } |
| 105 |
| 106 length := uint32(lengthBytes[0])<<24 | uint32(lengthBytes[1])<<16 | uint
32(lengthBytes[2])<<8 | uint32(lengthBytes[3]) |
| 107 paddingLength := uint32(lengthBytes[4]) |
| 108 |
| 109 if length <= paddingLength+1 { |
| 110 return nil, errors.New("invalid packet length") |
| 111 } |
| 112 if length > maxPacketSize { |
| 113 return nil, errors.New("packet too large") |
| 114 } |
| 115 |
| 116 packet := make([]byte, length-1+macSize) |
| 117 if _, err := io.ReadFull(r, packet); err != nil { |
| 118 return nil, err |
| 119 } |
| 120 mac := packet[length-1:] |
| 121 r.cipher.XORKeyStream(packet, packet[:length-1]) |
| 122 |
| 123 if r.mac != nil { |
| 124 r.mac.Write(packet[:length-1]) |
| 125 if subtle.ConstantTimeCompare(r.mac.Sum(nil), mac) != 1 { |
| 126 return nil, errors.New("ssh: MAC failure") |
| 127 } |
| 128 } |
| 129 |
| 130 r.seqNum++ |
| 131 return packet[:length-paddingLength-1], nil |
| 132 } |
| 133 |
| 134 // Read and decrypt next packet discarding debug and noop messages. |
| 135 func (t *transport) readPacket() ([]byte, error) { |
| 136 for { |
| 137 packet, err := t.readOnePacket() |
| 138 if err != nil { |
| 139 return nil, err |
| 140 } |
| 141 if packet[0] != msgIgnore && packet[0] != msgDebug { |
| 142 return packet, nil |
| 143 } |
| 144 } |
| 145 panic("unreachable") |
| 146 } |
| 147 |
| 148 // Encrypt and send a packet of data to the remote peer. |
| 149 func (w *writer) writePacket(packet []byte) error { |
| 150 w.Mutex.Lock() |
| 151 defer w.Mutex.Unlock() |
| 152 |
| 153 paddingLength := packetSizeMultiple - (5+len(packet))%packetSizeMultiple |
| 154 if paddingLength < 4 { |
| 155 paddingLength += packetSizeMultiple |
| 156 } |
| 157 |
| 158 length := len(packet) + 1 + paddingLength |
| 159 lengthBytes := []byte{ |
| 160 byte(length >> 24), |
| 161 byte(length >> 16), |
| 162 byte(length >> 8), |
| 163 byte(length), |
| 164 byte(paddingLength), |
| 165 } |
| 166 padding := make([]byte, paddingLength) |
| 167 _, err := io.ReadFull(w.rand, padding) |
| 168 if err != nil { |
| 169 return err |
| 170 } |
| 171 |
| 172 if w.mac != nil { |
| 173 w.mac.Reset() |
| 174 seqNumBytes := []byte{ |
| 175 byte(w.seqNum >> 24), |
| 176 byte(w.seqNum >> 16), |
| 177 byte(w.seqNum >> 8), |
| 178 byte(w.seqNum), |
| 179 } |
| 180 w.mac.Write(seqNumBytes) |
| 181 w.mac.Write(lengthBytes) |
| 182 w.mac.Write(packet) |
| 183 w.mac.Write(padding) |
| 184 } |
| 185 |
| 186 // TODO(dfc) lengthBytes, packet and padding should be |
| 187 // subslices of a single buffer |
| 188 w.cipher.XORKeyStream(lengthBytes, lengthBytes) |
| 189 w.cipher.XORKeyStream(packet, packet) |
| 190 w.cipher.XORKeyStream(padding, padding) |
| 191 |
| 192 if _, err := w.Write(lengthBytes); err != nil { |
| 193 return err |
| 194 } |
| 195 if _, err := w.Write(packet); err != nil { |
| 196 return err |
| 197 } |
| 198 if _, err := w.Write(padding); err != nil { |
| 199 return err |
| 200 } |
| 201 |
| 202 if w.mac != nil { |
| 203 if _, err := w.Write(w.mac.Sum(nil)); err != nil { |
| 204 return err |
| 205 } |
| 206 } |
| 207 |
| 208 if err := w.Flush(); err != nil { |
| 209 return err |
| 210 } |
| 211 w.seqNum++ |
| 212 return err |
| 213 } |
| 214 |
| 215 // Send a message to the remote peer |
| 216 func (t *transport) sendMessage(typ uint8, msg interface{}) error { |
| 217 packet := marshal(typ, msg) |
| 218 return t.writePacket(packet) |
| 219 } |
| 220 |
| 221 func newTransport(conn net.Conn, rand io.Reader) *transport { |
| 222 return &transport{ |
| 223 reader: reader{ |
| 224 Reader: bufio.NewReader(conn), |
| 225 common: common{ |
| 226 cipher: noneCipher{}, |
| 227 }, |
| 228 }, |
| 229 writer: writer{ |
| 230 Writer: bufio.NewWriter(conn), |
| 231 rand: rand, |
| 232 Mutex: new(sync.Mutex), |
| 233 common: common{ |
| 234 cipher: noneCipher{}, |
| 235 }, |
| 236 }, |
| 237 filteredConn: conn, |
| 238 } |
| 239 } |
| 240 |
| 241 type direction struct { |
| 242 ivTag []byte |
| 243 keyTag []byte |
| 244 macKeyTag []byte |
| 245 } |
| 246 |
| 247 // TODO(dfc) can this be made a constant ? |
| 248 var ( |
| 249 serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} |
| 250 clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} |
| 251 ) |
| 252 |
| 253 // setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as |
| 254 // described in RFC 4253, section 6.4. direction should either be serverKeys |
| 255 // (to setup server->client keys) or clientKeys (for client->server keys). |
| 256 func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.
Hash) error { |
| 257 cipherMode := cipherModes[c.cipherAlgo] |
| 258 |
| 259 macKeySize := 20 |
| 260 |
| 261 iv := make([]byte, cipherMode.ivSize) |
| 262 key := make([]byte, cipherMode.keySize) |
| 263 macKey := make([]byte, macKeySize) |
| 264 |
| 265 h := hashFunc.New() |
| 266 generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h) |
| 267 generateKeyMaterial(key, d.keyTag, K, H, sessionId, h) |
| 268 generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h) |
| 269 |
| 270 c.mac = truncatingMAC{12, hmac.New(sha1.New, macKey)} |
| 271 |
| 272 cipher, err := cipherMode.createCipher(key, iv) |
| 273 if err != nil { |
| 274 return err |
| 275 } |
| 276 |
| 277 c.cipher = cipher |
| 278 |
| 279 return nil |
| 280 } |
| 281 |
| 282 // generateKeyMaterial fills out with key material generated from tag, K, H |
| 283 // and sessionId, as specified in RFC 4253, section 7.2. |
| 284 func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) { |
| 285 var digestsSoFar []byte |
| 286 |
| 287 for len(out) > 0 { |
| 288 h.Reset() |
| 289 h.Write(K) |
| 290 h.Write(H) |
| 291 |
| 292 if len(digestsSoFar) == 0 { |
| 293 h.Write(tag) |
| 294 h.Write(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 // truncatingMAC wraps around a hash.Hash and truncates the output digest to |
| 309 // a given size. |
| 310 type truncatingMAC struct { |
| 311 length int |
| 312 hmac hash.Hash |
| 313 } |
| 314 |
| 315 func (t truncatingMAC) Write(data []byte) (int, error) { |
| 316 return t.hmac.Write(data) |
| 317 } |
| 318 |
| 319 func (t truncatingMAC) Sum(in []byte) []byte { |
| 320 out := t.hmac.Sum(in) |
| 321 return out[:len(in)+t.length] |
| 322 } |
| 323 |
| 324 func (t truncatingMAC) Reset() { |
| 325 t.hmac.Reset() |
| 326 } |
| 327 |
| 328 func (t truncatingMAC) Size() int { |
| 329 return t.length |
| 330 } |
| 331 |
| 332 func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } |
| 333 |
| 334 // maxVersionStringBytes is the maximum number of bytes that we'll accept as a |
| 335 // version string. In the event that the client is talking a different protocol |
| 336 // we need to set a limit otherwise we will keep using more and more memory |
| 337 // while searching for the end of the version handshake. |
| 338 const maxVersionStringBytes = 1024 |
| 339 |
| 340 // Read version string as specified by RFC 4253, section 4.2. |
| 341 func readVersion(r io.Reader) ([]byte, error) { |
| 342 versionString := make([]byte, 0, 64) |
| 343 var ok bool |
| 344 var buf [1]byte |
| 345 forEachByte: |
| 346 for len(versionString) < maxVersionStringBytes { |
| 347 _, err := io.ReadFull(r, buf[:]) |
| 348 if err != nil { |
| 349 return nil, err |
| 350 } |
| 351 // The RFC says that the version should be terminated with \r\n |
| 352 // but several SSH servers actually only send a \n. |
| 353 if buf[0] == '\n' { |
| 354 ok = true |
| 355 break forEachByte |
| 356 } |
| 357 versionString = append(versionString, buf[0]) |
| 358 } |
| 359 |
| 360 if !ok { |
| 361 return nil, errors.New("ssh: failed to read version string") |
| 362 } |
| 363 |
| 364 // There might be a '\r' on the end which we should remove. |
| 365 if len(versionString) > 0 && versionString[len(versionString)-1] == '\r'
{ |
| 366 versionString = versionString[:len(versionString)-1] |
| 367 } |
| 368 return versionString, nil |
| 369 } |
OLD | NEW |