Skip to content

Instantly share code, notes, and snippets.

@elimisteve
Created January 14, 2015 05:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save elimisteve/e3fea232c11fa08146d6 to your computer and use it in GitHub Desktop.
Save elimisteve/e3fea232c11fa08146d6 to your computer and use it in GitHub Desktop.
Patch to add support for SSLv2 handshakes to Go 1.4's standard library
diff -rupN src/crypto/tls/conn.go src/crypto/tls/conn.go
--- src/crypto/tls/conn.go 2014-12-10 17:19:32.000000000 -0800
+++ src/crypto/tls/conn.go 2015-01-13 19:10:01.567729660 -0800
@@ -56,6 +56,8 @@ type Conn struct {
hand bytes.Buffer // handshake data waiting to be read
tmp [16]byte
+
+ sslv2data []byte
}
// Access to net.Conn methods.
@@ -506,6 +508,107 @@ func (hc *halfConn) splitBlock(b *block,
return b, bb
}
+func convertSSLClientHandshake(c *Conn, b *block) error {
+
+ var recordLength uint16
+ // high bit must be 1 for a SSLv2 compatible client hello
+ if (uint8(b.data[0]) & 128) == 128 {
+ recordLength = (uint16(b.data[0]&0x7f) << 8) | uint16(b.data[1])
+ } else {
+ return c.sendAlert(alertUnexpectedMessage)
+ }
+
+ mtype := uint8(b.data[2])
+ majvers := uint8(b.data[3])
+ minvers := uint8(b.data[4])
+
+ // if this is an SSLv2 client-hello but TLS is supported, we can continue
+ if mtype == typeClientHello && majvers >= 3 && minvers >= 1 {
+
+ // read the rest of the bytes
+ if err := b.readFromUntil(c.conn, int(2+recordLength)); err != nil {
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ if e, ok := err.(net.Error); !ok || !e.Temporary() {
+ c.in.setErrorLocked(err)
+ }
+ return err
+ }
+
+ // get the cipher spec length
+ cipher_spec_length := uint16(b.data[5])<<8 | uint16(b.data[6])
+ if (cipher_spec_length % 3) != 0 {
+ return c.sendAlert(alertHandshakeFailure)
+ }
+
+ // session ID length MUST be zero for a SSLv2 client hello
+ session_id_length := uint16(b.data[7])<<8 | uint16(b.data[8])
+ if session_id_length != 0 {
+ return c.sendAlert(alertHandshakeFailure)
+ }
+
+ // according tochallenge_length must be 32 for a SSLv2 client hello
+ // challenge_length := uint16(b.data[9])<<8 | uint16(b.data[10])
+ // if challenge_length != 32 {
+ // return c.sendAlert(alertHandshakeFailure)
+ // }
+
+ // session_id_length should always be zero - we cannot resume a SSLv2 session
+ if session_id_length != 0 {
+ return c.sendAlert(alertHandshakeFailure)
+ }
+
+ // read the cipher specs
+ cipher_specs := b.data[11 : 11+cipher_spec_length]
+
+ // read the rest of the data - must have at least 16 bytes
+ challenge_data := b.data[11+cipher_spec_length:]
+ // the spec is contradictory here - it says challenge_length must be 32,
+ // but also specifies how to handle challenge lengths greater or less than that
+ // but according to rfc2246 must have at least 16 bytes of challenge data
+ if len(challenge_data) < 16 {
+ return c.sendAlert(alertHandshakeFailure)
+ }
+
+ // mark this data as read?
+ b, c.rawInput = c.in.splitBlock(b, int(2+recordLength))
+ b.off = 2
+
+ // create a v2 handshake message & write it to the buffer
+ helloMsg := clientHelloMsg{}
+ helloMsg.vers = uint16(majvers<<8) | uint16(minVersion)
+ // session ID must be zero
+ helloMsg.sessionId = []byte{0}
+ helloMsg.compressionMethods = []uint8{compressionNone}
+ // only use the last 32 bytes of the challenge data
+ if len(challenge_data) < 32 {
+ helloMsg.random = make([]byte, 32-len(challenge_data))
+ helloMsg.random = append(helloMsg.random, challenge_data...)
+ } else {
+ helloMsg.random = challenge_data[32-len(challenge_data):]
+ }
+
+ // translate the cipher suites
+ helloMsg.cipherSuites = make([]uint16, 0, 0)
+ for i := 0; i < len(cipher_specs); i += 3 {
+ // we can only support cipher specs starting with a high bit
+ if cipher_specs[i] == 0 {
+ cipher := uint16(cipher_specs[i+1])<<8 | uint16(cipher_specs[i+2])
+ helloMsg.cipherSuites = append(helloMsg.cipherSuites, cipher)
+ }
+ }
+
+ // write the client handshake to the handshake buffer
+ c.hand.Write(helloMsg.marshal())
+
+ c.sslv2data = b.data[2:]
+ c.in.freeBlock(b)
+
+ }
+ return nil
+}
+
// readRecord reads the next TLS record from the connection
// and updates the record layer state.
// c.in.Mutex <= L; c.input == nil.
@@ -548,15 +651,12 @@ Again:
}
return err
}
+
typ := recordType(b.data[0])
- // No valid TLS record has a type of 0x80, however SSLv2 handshakes
- // start with a uint16 length where the MSB is set and the first record
- // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
- // an SSLv2 client.
- if want == recordTypeHandshake && typ == 0x80 {
- c.sendAlert(alertProtocolVersion)
- return c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received"))
+ // if this is an SSLv2 header, lets see if we can upgrade to TLS
+ if !c.handshakeComplete && typ != recordTypeHandshake && typ != recordTypeChangeCipherSpec && typ != recordTypeAlert {
+ return convertSSLClientHandshake(c, b)
}
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
@@ -837,6 +937,7 @@ func (c *Conn) readHandshake() (interfac
if !m.unmarshal(data) {
return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
+
return m, nil
}
diff -rupN src/crypto/tls/handshake_server.go src/crypto/tls/handshake_server.go
--- src/crypto/tls/handshake_server.go 2014-12-10 17:19:32.000000000 -0800
+++ src/crypto/tls/handshake_server.go 2015-01-13 19:00:14.415708750 -0800
@@ -112,6 +112,11 @@ func (hs *serverHandshakeState) readClie
c.haveVers = true
hs.finishedHash = newFinishedHash(c.vers)
- hs.finishedHash.Write(hs.clientHello.marshal())
+ if len(c.sslv2data) > 0 {
+ hs.finishedHash.Write(c.sslv2data)
+ c.sslv2data = nil
+ } else {
+ hs.finishedHash.Write(hs.clientHello.marshal())
+ }
hs.hello = new(serverHelloMsg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment