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