diff options
Diffstat (limited to 'libgo/go/crypto/tls/handshake_client.go')
-rw-r--r-- | libgo/go/crypto/tls/handshake_client.go | 243 |
1 files changed, 169 insertions, 74 deletions
diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go index 3c996ac..f789e6f 100644 --- a/libgo/go/crypto/tls/handshake_client.go +++ b/libgo/go/crypto/tls/handshake_client.go @@ -16,6 +16,7 @@ import ( "io" "net" "strconv" + "strings" ) type clientHandshakeState struct { @@ -28,11 +29,16 @@ type clientHandshakeState struct { session *ClientSessionState } +// c.out.Mutex <= L; c.handshakeMutex <= L. func (c *Conn) clientHandshake() error { if c.config == nil { c.config = defaultConfig() } + // This may be a renegotiation handshake, in which case some fields + // need to be reset. + c.didResume = false + if len(c.config.ServerName) == 0 && !c.config.InsecureSkipVerify { return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") } @@ -49,25 +55,22 @@ func (c *Conn) clientHandshake() error { return errors.New("tls: NextProtos values too large") } - sni := c.config.ServerName - // IP address literals are not permitted as SNI values. See - // https://tools.ietf.org/html/rfc6066#section-3. - if net.ParseIP(sni) != nil { - sni = "" + hello := &clientHelloMsg{ + vers: c.config.maxVersion(), + compressionMethods: []uint8{compressionNone}, + random: make([]byte, 32), + ocspStapling: true, + scts: true, + serverName: hostnameInSNI(c.config.ServerName), + supportedCurves: c.config.curvePreferences(), + supportedPoints: []uint8{pointFormatUncompressed}, + nextProtoNeg: len(c.config.NextProtos) > 0, + secureRenegotiationSupported: true, + alpnProtocols: c.config.NextProtos, } - hello := &clientHelloMsg{ - vers: c.config.maxVersion(), - compressionMethods: []uint8{compressionNone}, - random: make([]byte, 32), - ocspStapling: true, - scts: true, - serverName: sni, - supportedCurves: c.config.curvePreferences(), - supportedPoints: []uint8{pointFormatUncompressed}, - nextProtoNeg: len(c.config.NextProtos) > 0, - secureRenegotiation: true, - alpnProtocols: c.config.NextProtos, + if c.handshakes > 0 { + hello.secureRenegotiation = c.clientFinished[:] } possibleCipherSuites := c.config.cipherSuites() @@ -108,7 +111,12 @@ NextCipherSuite: if sessionCache != nil { hello.ticketSupported = true + } + // Session resumption is not allowed if renegotiating because + // renegotiation is primarily used to allow a client to send a client + // certificate, which would be skipped if session resumption occured. + if sessionCache != nil && c.handshakes == 0 { // Try to resume a previously negotiated TLS session, if // available. cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) @@ -144,7 +152,9 @@ NextCipherSuite: } } - c.writeRecord(recordTypeHandshake, hello.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil { + return err + } msg, err := c.readHandshake() if err != nil { @@ -196,6 +206,7 @@ NextCipherSuite: hs.finishedHash.Write(hs.hello.marshal()) hs.finishedHash.Write(hs.serverHello.marshal()) + c.buffering = true if isResume { if err := hs.establishKeys(); err != nil { return err @@ -203,10 +214,14 @@ NextCipherSuite: if err := hs.readSessionTicket(); err != nil { return err } - if err := hs.readFinished(c.firstFinished[:]); err != nil { + if err := hs.readFinished(c.serverFinished[:]); err != nil { + return err + } + c.clientFinishedIsFirst = false + if err := hs.sendFinished(c.clientFinished[:]); err != nil { return err } - if err := hs.sendFinished(nil); err != nil { + if _, err := c.flush(); err != nil { return err } } else { @@ -216,13 +231,17 @@ NextCipherSuite: if err := hs.establishKeys(); err != nil { return err } - if err := hs.sendFinished(c.firstFinished[:]); err != nil { + if err := hs.sendFinished(c.clientFinished[:]); err != nil { return err } + if _, err := c.flush(); err != nil { + return err + } + c.clientFinishedIsFirst = true if err := hs.readSessionTicket(); err != nil { return err } - if err := hs.readFinished(nil); err != nil { + if err := hs.readFinished(c.serverFinished[:]); err != nil { return err } } @@ -251,47 +270,62 @@ func (hs *clientHandshakeState) doFullHandshake() error { } hs.finishedHash.Write(certMsg.marshal()) - certs := make([]*x509.Certificate, len(certMsg.certificates)) - for i, asn1Data := range certMsg.certificates { - cert, err := x509.ParseCertificate(asn1Data) - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to parse certificate from server: " + err.Error()) + if c.handshakes == 0 { + // If this is the first handshake on a connection, process and + // (optionally) verify the server's certificates. + certs := make([]*x509.Certificate, len(certMsg.certificates)) + for i, asn1Data := range certMsg.certificates { + cert, err := x509.ParseCertificate(asn1Data) + if err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("tls: failed to parse certificate from server: " + err.Error()) + } + certs[i] = cert } - certs[i] = cert - } - if !c.config.InsecureSkipVerify { - opts := x509.VerifyOptions{ - Roots: c.config.RootCAs, - CurrentTime: c.config.time(), - DNSName: c.config.ServerName, - Intermediates: x509.NewCertPool(), - } + if !c.config.InsecureSkipVerify { + opts := x509.VerifyOptions{ + Roots: c.config.RootCAs, + CurrentTime: c.config.time(), + DNSName: c.config.ServerName, + Intermediates: x509.NewCertPool(), + } - for i, cert := range certs { - if i == 0 { - continue + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + c.verifiedChains, err = certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return err } - opts.Intermediates.AddCert(cert) } - c.verifiedChains, err = certs[0].Verify(opts) - if err != nil { - c.sendAlert(alertBadCertificate) - return err + + switch certs[0].PublicKey.(type) { + case *rsa.PublicKey, *ecdsa.PublicKey: + break + default: + c.sendAlert(alertUnsupportedCertificate) + return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) } - } - switch certs[0].PublicKey.(type) { - case *rsa.PublicKey, *ecdsa.PublicKey: - break - default: - c.sendAlert(alertUnsupportedCertificate) - return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) + c.peerCertificates = certs + } else { + // This is a renegotiation handshake. We require that the + // server's identity (i.e. leaf certificate) is unchanged and + // thus any previous trust decision is still valid. + // + // See https://mitls.org/pages/attacks/3SHAKE for the + // motivation behind this requirement. + if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) { + c.sendAlert(alertBadCertificate) + return errors.New("tls: server's identity changed during renegotiation") + } } - c.peerCertificates = certs - if hs.serverHello.ocspStapling { msg, err = c.readHandshake() if err != nil { @@ -319,7 +353,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { skx, ok := msg.(*serverKeyExchangeMsg) if ok { hs.finishedHash.Write(skx.marshal()) - err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, certs[0], skx) + err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx) if err != nil { c.sendAlert(alertUnexpectedMessage) return err @@ -425,17 +459,21 @@ func (hs *clientHandshakeState) doFullHandshake() error { certMsg.certificates = chainToSend.Certificate } hs.finishedHash.Write(certMsg.marshal()) - c.writeRecord(recordTypeHandshake, certMsg.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { + return err + } } - preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, certs[0]) + preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0]) if err != nil { c.sendAlert(alertInternalError) return err } if ckx != nil { hs.finishedHash.Write(ckx.marshal()) - c.writeRecord(recordTypeHandshake, ckx.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil { + return err + } } if chainToSend != nil { @@ -477,7 +515,9 @@ func (hs *clientHandshakeState) doFullHandshake() error { } hs.finishedHash.Write(certVerify.marshal()) - c.writeRecord(recordTypeHandshake, certVerify.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil { + return err + } } hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) @@ -524,6 +564,24 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { return false, errors.New("tls: server selected unsupported compression format") } + if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported { + c.secureRenegotiation = true + if len(hs.serverHello.secureRenegotiation) != 0 { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: initial handshake had non-empty renegotiation extension") + } + } + + if c.handshakes > 0 && c.secureRenegotiation { + var expectedSecureRenegotiation [24]byte + copy(expectedSecureRenegotiation[:], c.clientFinished[:]) + copy(expectedSecureRenegotiation[12:], c.serverFinished[:]) + if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: incorrect renegotiation extension contents") + } + } + clientDidNPN := hs.hello.nextProtoNeg clientDidALPN := len(hs.hello.alpnProtocols) > 0 serverHasNPN := hs.serverHello.nextProtoNeg @@ -531,17 +589,17 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { if !clientDidNPN && serverHasNPN { c.sendAlert(alertHandshakeFailure) - return false, errors.New("server advertised unrequested NPN extension") + return false, errors.New("tls: server advertised unrequested NPN extension") } if !clientDidALPN && serverHasALPN { c.sendAlert(alertHandshakeFailure) - return false, errors.New("server advertised unrequested ALPN extension") + return false, errors.New("tls: server advertised unrequested ALPN extension") } if serverHasNPN && serverHasALPN { c.sendAlert(alertHandshakeFailure) - return false, errors.New("server advertised both NPN and ALPN extensions") + return false, errors.New("tls: server advertised both NPN and ALPN extensions") } if serverHasALPN { @@ -550,22 +608,33 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { } c.scts = hs.serverHello.scts - if hs.serverResumedSession() { - // Restore masterSecret and peerCerts from previous state - hs.masterSecret = hs.session.masterSecret - c.peerCertificates = hs.session.serverCertificates - c.verifiedChains = hs.session.verifiedChains - return true, nil + if !hs.serverResumedSession() { + return false, nil + } + + if hs.session.vers != c.vers { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: server resumed a session with a different version") + } + + if hs.session.cipherSuite != hs.suite.id { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: server resumed a session with a different cipher suite") } - return false, nil + + // Restore masterSecret and peerCerts from previous state + hs.masterSecret = hs.session.masterSecret + c.peerCertificates = hs.session.serverCertificates + c.verifiedChains = hs.session.verifiedChains + return true, nil } func (hs *clientHandshakeState) readFinished(out []byte) error { c := hs.c c.readRecord(recordTypeChangeCipherSpec) - if err := c.in.error(); err != nil { - return err + if c.in.err != nil { + return c.in.err } msg, err := c.readHandshake() @@ -621,7 +690,9 @@ func (hs *clientHandshakeState) readSessionTicket() error { func (hs *clientHandshakeState) sendFinished(out []byte) error { c := hs.c - c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { + return err + } if hs.serverHello.nextProtoNeg { nextProto := new(nextProtoMsg) proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.nextProtos) @@ -630,13 +701,17 @@ func (hs *clientHandshakeState) sendFinished(out []byte) error { c.clientProtocolFallback = fallback hs.finishedHash.Write(nextProto.marshal()) - c.writeRecord(recordTypeHandshake, nextProto.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, nextProto.marshal()); err != nil { + return err + } } finished := new(finishedMsg) finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret) hs.finishedHash.Write(finished.marshal()) - c.writeRecord(recordTypeHandshake, finished.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { + return err + } copy(out, finished.verifyData) return nil } @@ -665,3 +740,23 @@ func mutualProtocol(protos, preferenceProtos []string) (string, bool) { return protos[0], true } + +// hostnameInSNI converts name into an approriate hostname for SNI. +// Literal IP addresses and absolute FQDNs are not permitted as SNI values. +// See https://tools.ietf.org/html/rfc6066#section-3. +func hostnameInSNI(name string) string { + host := name + if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { + host = host[1 : len(host)-1] + } + if i := strings.LastIndex(host, "%"); i > 0 { + host = host[:i] + } + if net.ParseIP(host) != nil { + return "" + } + if len(name) > 0 && name[len(name)-1] == '.' { + name = name[:len(name)-1] + } + return name +} |