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