Created
January 14, 2015 05:59
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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