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