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