diff options
Diffstat (limited to 'libgo/go/crypto/tls/conn.go')
-rw-r--r-- | libgo/go/crypto/tls/conn.go | 214 |
1 files changed, 152 insertions, 62 deletions
diff --git a/libgo/go/crypto/tls/conn.go b/libgo/go/crypto/tls/conn.go index 87bef23d..03895a7 100644 --- a/libgo/go/crypto/tls/conn.go +++ b/libgo/go/crypto/tls/conn.go @@ -29,11 +29,15 @@ type Conn struct { // constant after handshake; protected by handshakeMutex handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex - handshakeErr error // error resulting from handshake - vers uint16 // TLS version - haveVers bool // version has been negotiated - config *Config // configuration passed to constructor - // handshakeComplete is true if the connection is currently transfering + // handshakeCond, if not nil, indicates that a goroutine is committed + // to running the handshake for this Conn. Other goroutines that need + // to wait for the handshake can wait on this, under handshakeMutex. + handshakeCond *sync.Cond + handshakeErr error // error resulting from handshake + vers uint16 // TLS version + haveVers bool // version has been negotiated + config *Config // configuration passed to constructor + // handshakeComplete is true if the connection is currently transferring // application data (i.e. is not currently processing a handshake). handshakeComplete bool // handshakes counts the number of handshakes performed on the @@ -60,6 +64,13 @@ type Conn struct { // the first transmitted Finished message is the tls-unique // channel-binding value. clientFinishedIsFirst bool + + // closeNotifyErr is any error from sending the alertCloseNotify record. + closeNotifyErr error + // closeNotifySent is true if the Conn attempted to send an + // alertCloseNotify record. + closeNotifySent bool + // clientFinished and serverFinished contain the Finished message sent // by the client or server in the most recent handshake. This is // retained to support the renegotiation extension and tls-unique @@ -189,18 +200,18 @@ func (hc *halfConn) incSeq() { panic("TLS: sequence number wraparound") } -// removePadding returns an unpadded slice, in constant time, which is a prefix -// of the input. It also returns a byte which is equal to 255 if the padding -// was valid and 0 otherwise. See RFC 2246, section 6.2.3.2 -func removePadding(payload []byte) ([]byte, byte) { +// extractPadding returns, in constant time, the length of the padding to remove +// from the end of payload. It also returns a byte which is equal to 255 if the +// padding was valid and 0 otherwise. See RFC 2246, section 6.2.3.2 +func extractPadding(payload []byte) (toRemove int, good byte) { if len(payload) < 1 { - return payload, 0 + return 0, 0 } paddingLen := payload[len(payload)-1] t := uint(len(payload)-1) - uint(paddingLen) // if len(payload) >= (paddingLen - 1) then the MSB of t is zero - good := byte(int32(^t) >> 31) + good = byte(int32(^t) >> 31) toCheck := 255 // the maximum possible padding length // The length of the padded data is public, so we can use an if here @@ -223,24 +234,24 @@ func removePadding(payload []byte) ([]byte, byte) { good &= good << 1 good = uint8(int8(good) >> 7) - toRemove := good&paddingLen + 1 - return payload[:len(payload)-int(toRemove)], good + toRemove = int(paddingLen) + 1 + return } -// removePaddingSSL30 is a replacement for removePadding in the case that the +// extractPaddingSSL30 is a replacement for extractPadding in the case that the // protocol version is SSLv3. In this version, the contents of the padding // are random and cannot be checked. -func removePaddingSSL30(payload []byte) ([]byte, byte) { +func extractPaddingSSL30(payload []byte) (toRemove int, good byte) { if len(payload) < 1 { - return payload, 0 + return 0, 0 } paddingLen := int(payload[len(payload)-1]) + 1 if paddingLen > len(payload) { - return payload, 0 + return 0, 0 } - return payload[:len(payload)-paddingLen], 255 + return paddingLen, 255 } func roundUp(a, b int) int { @@ -266,6 +277,7 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) } paddingGood := byte(255) + paddingLen := 0 explicitIVLen := 0 // decrypt @@ -273,13 +285,17 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) switch c := hc.cipher.(type) { case cipher.Stream: c.XORKeyStream(payload, payload) - case cipher.AEAD: - explicitIVLen = 8 + case aead: + explicitIVLen = c.explicitNonceLen() if len(payload) < explicitIVLen { return false, 0, alertBadRecordMAC } - nonce := payload[:8] - payload = payload[8:] + nonce := payload[:explicitIVLen] + payload = payload[explicitIVLen:] + + if len(nonce) == 0 { + nonce = hc.seq[:] + } copy(hc.additionalData[:], hc.seq[:]) copy(hc.additionalData[8:], b.data[:3]) @@ -308,22 +324,17 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) } c.CryptBlocks(payload, payload) if hc.version == VersionSSL30 { - payload, paddingGood = removePaddingSSL30(payload) + paddingLen, paddingGood = extractPaddingSSL30(payload) } else { - payload, paddingGood = removePadding(payload) + paddingLen, paddingGood = extractPadding(payload) + + // To protect against CBC padding oracles like Lucky13, the data + // past paddingLen (which is secret) is passed to the MAC + // function as extra data, to be fed into the HMAC after + // computing the digest. This makes the MAC constant time as + // long as the digest computation is constant time and does not + // affect the subsequent write. } - b.resize(recordHeaderLen + explicitIVLen + len(payload)) - - // note that we still have a timing side-channel in the - // MAC check, below. An attacker can align the record - // so that a correct padding will cause one less hash - // block to be calculated. Then they can iteratively - // decrypt a record by breaking each byte. See - // "Password Interception in a SSL/TLS Channel", Brice - // Canvel et al. - // - // However, our behavior matches OpenSSL, so we leak - // only as much as they do. default: panic("unknown cipher type") } @@ -336,17 +347,19 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) } // strip mac off payload, b.data - n := len(payload) - macSize + n := len(payload) - macSize - paddingLen + n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 } b.data[3] = byte(n >> 8) b.data[4] = byte(n) - b.resize(recordHeaderLen + explicitIVLen + n) - remoteMAC := payload[n:] - localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n]) + remoteMAC := payload[n : n+macSize] + localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n], payload[n+macSize:]) if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { return false, 0, alertBadRecordMAC } hc.inDigestBuf = localMAC + + b.resize(recordHeaderLen + explicitIVLen + n) } hc.incSeq() @@ -374,7 +387,7 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { // mac if hc.mac != nil { - mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:]) + mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:], nil) n := len(b.data) b.resize(n + len(mac)) @@ -389,10 +402,13 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { switch c := hc.cipher.(type) { case cipher.Stream: c.XORKeyStream(payload, payload) - case cipher.AEAD: + case aead: payloadLen := len(b.data) - recordHeaderLen - explicitIVLen b.resize(len(b.data) + c.Overhead()) nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + if len(nonce) == 0 { + nonce = hc.seq[:] + } payload := b.data[recordHeaderLen+explicitIVLen:] payload = payload[:payloadLen] @@ -628,9 +644,10 @@ Again: // Process message. b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) - ok, off, err := c.in.decrypt(b) + ok, off, alertValue := c.in.decrypt(b) if !ok { - c.in.setErrorLocked(c.sendAlert(err)) + c.in.freeBlock(b) + return c.in.setErrorLocked(c.sendAlert(alertValue)) } b.off = off data := b.data[b.off:] @@ -849,15 +866,16 @@ func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { } } if explicitIVLen == 0 { - if _, ok := c.out.cipher.(cipher.AEAD); ok { - explicitIVLen = 8 + if c, ok := c.out.cipher.(aead); ok { + explicitIVLen = c.explicitNonceLen() + // The AES-GCM construction in TLS has an // explicit nonce so that the nonce can be // random. However, the nonce is only 8 bytes // which is too small for a secure, random // nonce. Therefore we use the sequence number // as the nonce. - explicitIVIsSeq = true + explicitIVIsSeq = explicitIVLen > 0 } } m := len(data) @@ -978,7 +996,7 @@ func (c *Conn) readHandshake() (interface{}, error) { return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } - // The handshake message unmarshallers + // The handshake message unmarshalers // expect to be able to keep references to data, // so pass in a fresh copy that won't be overwritten. data = append([]byte(nil), data...) @@ -989,7 +1007,10 @@ func (c *Conn) readHandshake() (interface{}, error) { return m, nil } -var errClosed = errors.New("tls: use of closed connection") +var ( + errClosed = errors.New("tls: use of closed connection") + errShutdown = errors.New("tls: protocol is shutdown") +) // Write writes data to the connection. func (c *Conn) Write(b []byte) (int, error) { @@ -1020,6 +1041,10 @@ func (c *Conn) Write(b []byte) (int, error) { return 0, alertInternalError } + if c.closeNotifySent { + return 0, errShutdown + } + // 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 @@ -1183,7 +1208,7 @@ func (c *Conn) Close() error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if c.handshakeComplete { - alertErr = c.sendAlert(alertCloseNotify) + alertErr = c.closeNotify() } if err := c.conn.Close(); err != nil { @@ -1192,6 +1217,32 @@ func (c *Conn) Close() error { return alertErr } +var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete") + +// CloseWrite shuts down the writing side of the connection. It should only be +// called once the handshake has completed and does not call CloseWrite on the +// underlying connection. Most callers should just use Close. +func (c *Conn) CloseWrite() error { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if !c.handshakeComplete { + return errEarlyCloseWrite + } + + return c.closeNotify() +} + +func (c *Conn) closeNotify() error { + c.out.Lock() + defer c.out.Unlock() + + if !c.closeNotifySent { + c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify) + c.closeNotifySent = true + } + return c.closeNotifyErr +} + // Handshake runs the client or server handshake // protocol if it has not yet been run. // Most uses of this package need not call Handshake @@ -1206,26 +1257,50 @@ func (c *Conn) Handshake() error { // need to check whether a handshake is pending (such as Write) to // block. // - // Thus we take c.handshakeMutex first and, if we find that a handshake - // is needed, then we unlock, acquire c.in and c.handshakeMutex in the - // correct order, and check again. + // Thus we first take c.handshakeMutex to check whether a handshake is + // needed. + // + // If so then, previously, this code would unlock handshakeMutex and + // then lock c.in and handshakeMutex in the correct order to run the + // handshake. The problem was that it was possible for a Read to + // complete the handshake once handshakeMutex was unlocked and then + // keep c.in while waiting for network data. Thus a concurrent + // operation could be blocked on c.in. + // + // Thus handshakeCond is used to signal that a goroutine is committed + // to running the handshake and other goroutines can wait on it if they + // need. handshakeCond is protected by handshakeMutex. c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() - for i := 0; i < 2; i++ { - if i == 1 { - c.handshakeMutex.Unlock() - c.in.Lock() - defer c.in.Unlock() - c.handshakeMutex.Lock() - } - + for { if err := c.handshakeErr; err != nil { return err } if c.handshakeComplete { return nil } + if c.handshakeCond == nil { + break + } + + c.handshakeCond.Wait() + } + + // Set handshakeCond to indicate that this goroutine is committing to + // running the handshake. + c.handshakeCond = sync.NewCond(&c.handshakeMutex) + c.handshakeMutex.Unlock() + + c.in.Lock() + defer c.in.Unlock() + + c.handshakeMutex.Lock() + + // The handshake cannot have completed when handshakeMutex was unlocked + // because this goroutine set handshakeCond. + if c.handshakeErr != nil || c.handshakeComplete { + panic("handshake should not have been able to complete after handshakeCond was set") } if c.isClient { @@ -1235,7 +1310,21 @@ func (c *Conn) Handshake() error { } if c.handshakeErr == nil { c.handshakes++ + } else { + // If an error occurred during the hadshake try to flush the + // alert that might be left in the buffer. + c.flush() + } + + if c.handshakeErr == nil && !c.handshakeComplete { + panic("handshake should have had a result.") } + + // Wake any other goroutines that are waiting for this handshake to + // complete. + c.handshakeCond.Broadcast() + c.handshakeCond = nil + return c.handshakeErr } @@ -1246,6 +1335,8 @@ func (c *Conn) ConnectionState() ConnectionState { var state ConnectionState state.HandshakeComplete = c.handshakeComplete + state.ServerName = c.serverName + if c.handshakeComplete { state.Version = c.vers state.NegotiatedProtocol = c.clientProtocol @@ -1254,7 +1345,6 @@ func (c *Conn) ConnectionState() ConnectionState { state.CipherSuite = c.cipherSuite state.PeerCertificates = c.peerCertificates state.VerifiedChains = c.verifiedChains - state.ServerName = c.serverName state.SignedCertificateTimestamps = c.scts state.OCSPResponse = c.ocspResponse if !c.didResume { |