haveVers bool // version has been negotiated
config *Config // configuration passed to constructor
handshakeComplete bool
+ didResume bool // whether this connection was a session resumption
cipherSuite uint16
ocspResponse []byte // stapled OCSP response
peerCertificates []*x509.Certificate
clientProtocolFallback bool
// first permanent error
- errMutex sync.Mutex
- err error
+ connErr
// input/output
in, out halfConn // in.Mutex < out.Mutex
tmp [16]byte
}
-func (c *Conn) setError(err error) error {
- c.errMutex.Lock()
- defer c.errMutex.Unlock()
+type connErr struct {
+ mu sync.Mutex
+ value error
+}
+
+func (e *connErr) setError(err error) error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
- if c.err == nil {
- c.err = err
+ if e.value == nil {
+ e.value = err
}
return err
}
-func (c *Conn) error() error {
- c.errMutex.Lock()
- defer c.errMutex.Unlock()
-
- return c.err
+func (e *connErr) error() error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ return e.value
}
// Access to net.Conn methods.
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 errors.New("tls: unsupported SSLv2 handshake received")
+ }
+
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
n := int(b.data[3])<<8 | int(b.data[4])
if c.haveVers && vers != c.vers {
// First message, be extra suspicious:
// this might not be a TLS client.
// Bail out before reading a full 'body', if possible.
- // The current max version is 3.1.
+ // The current max version is 3.1.
// If the version is >= 16.0, it's probably not real.
// Similarly, a clientHello message encodes in
// well under a kilobyte. If the length is >= 12 kB,
// sendAlert sends a TLS alert message.
// c.out.Mutex <= L.
func (c *Conn) sendAlertLocked(err alert) error {
- c.tmp[0] = alertLevelError
- if err == alertNoRenegotiation {
+ switch err {
+ case alertNoRenegotiation, alertCloseNotify:
c.tmp[0] = alertLevelWarning
+ default:
+ c.tmp[0] = alertLevelError
}
c.tmp[1] = byte(err)
c.writeRecord(recordTypeAlert, c.tmp[0:2])
c.tmp[0] = alertLevelError
c.tmp[1] = byte(err.(alert))
c.writeRecord(recordTypeAlert, c.tmp[0:2])
- c.err = &net.OpError{Op: "local error", Err: err}
- return n, c.err
+ return n, c.setError(&net.OpError{Op: "local error", Err: err})
}
}
return
// c.in.Mutex < L; c.out.Mutex < L.
func (c *Conn) readHandshake() (interface{}, error) {
for c.hand.Len() < 4 {
- if c.err != nil {
- return nil, c.err
+ if err := c.error(); err != nil {
+ return nil, err
}
if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
if n > maxHandshake {
c.sendAlert(alertInternalError)
- return nil, c.err
+ return nil, c.error()
}
for c.hand.Len() < 4+n {
- if c.err != nil {
- return nil, c.err
+ if err := c.error(); err != nil {
+ return nil, err
}
if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
// Write writes data to the connection.
func (c *Conn) Write(b []byte) (int, error) {
- if c.err != nil {
- return 0, c.err
+ if err := c.error(); err != nil {
+ return 0, err
}
- if c.err = c.Handshake(); c.err != nil {
- return 0, c.err
+ if err := c.Handshake(); err != nil {
+ return 0, c.setError(err)
}
c.out.Lock()
return 0, alertInternalError
}
- var n int
- n, c.err = c.writeRecord(recordTypeApplicationData, b)
- return n, c.err
+ // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
+ // attack when using block mode ciphers due to predictable IVs.
+ // This can be prevented by splitting each Application Data
+ // record into two records, effectively randomizing the IV.
+ //
+ // http://www.openssl.org/~bodo/tls-cbc.txt
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=665814
+ // http://www.imperialviolet.org/2012/01/15/beastfollowup.html
+
+ var m int
+ if len(b) > 1 && c.vers <= versionTLS10 {
+ if _, ok := c.out.cipher.(cipher.BlockMode); ok {
+ n, err := c.writeRecord(recordTypeApplicationData, b[:1])
+ if err != nil {
+ return n, c.setError(err)
+ }
+ m, b = 1, b[1:]
+ }
+ }
+
+ n, err := c.writeRecord(recordTypeApplicationData, b)
+ return n + m, c.setError(err)
}
// Read can be made to time out and return a net.Error with Timeout() == true
c.in.Lock()
defer c.in.Unlock()
- for c.input == nil && c.err == nil {
+ for c.input == nil && c.error() == nil {
if err := c.readRecord(recordTypeApplicationData); err != nil {
// Soft error, like EAGAIN
return 0, err
}
}
- if c.err != nil {
- return 0, c.err
+ if err := c.error(); err != nil {
+ return 0, err
}
n, err = c.input.Read(b)
if c.input.off >= len(c.input.data) {
state.HandshakeComplete = c.handshakeComplete
if c.handshakeComplete {
state.NegotiatedProtocol = c.clientProtocol
+ state.DidResume = c.didResume
state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates