diff options
Diffstat (limited to 'libgo/go/crypto/tls/handshake_client.go')
-rw-r--r-- | libgo/go/crypto/tls/handshake_client.go | 437 |
1 files changed, 296 insertions, 141 deletions
diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go index 3c996acf87..6eda18dbfc 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 occurred. + 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 { @@ -189,13 +199,14 @@ NextCipherSuite: // Otherwise, in a full handshake, if we don't have any certificates // configured then we will never send a CertificateVerify message and // thus no signatures are needed in that case either. - if isResume || len(c.config.Certificates) == 0 { + if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) { hs.finishedHash.discardHandshakeBuffer() } 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,69 @@ 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 + } + opts.Intermediates.AddCert(cert) + } + c.verifiedChains, err = certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return err + } } - for i, cert := range certs { - if i == 0 { - continue + if c.config.VerifyPeerCertificate != nil { + if err := c.config.VerifyPeerCertificate(certMsg.certificates, c.verifiedChains); 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 +360,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 @@ -336,71 +377,11 @@ func (hs *clientHandshakeState) doFullHandshake() error { certReq, ok := msg.(*certificateRequestMsg) if ok { certRequested = true - - // RFC 4346 on the certificateAuthorities field: - // A list of the distinguished names of acceptable certificate - // authorities. These distinguished names may specify a desired - // distinguished name for a root CA or for a subordinate CA; - // thus, this message can be used to describe both known roots - // and a desired authorization space. If the - // certificate_authorities list is empty then the client MAY - // send any certificate of the appropriate - // ClientCertificateType, unless there is some external - // arrangement to the contrary. - hs.finishedHash.Write(certReq.marshal()) - var rsaAvail, ecdsaAvail bool - for _, certType := range certReq.certificateTypes { - switch certType { - case certTypeRSASign: - rsaAvail = true - case certTypeECDSASign: - ecdsaAvail = true - } - } - - // We need to search our list of client certs for one - // where SignatureAlgorithm is acceptable to the server and the - // Issuer is in certReq.certificateAuthorities - findCert: - for i, chain := range c.config.Certificates { - if !rsaAvail && !ecdsaAvail { - continue - } - - for j, cert := range chain.Certificate { - x509Cert := chain.Leaf - // parse the certificate if this isn't the leaf - // node, or if chain.Leaf was nil - if j != 0 || x509Cert == nil { - if x509Cert, err = x509.ParseCertificate(cert); err != nil { - c.sendAlert(alertInternalError) - return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) - } - } - - switch { - case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: - case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: - default: - continue findCert - } - - if len(certReq.certificateAuthorities) == 0 { - // they gave us an empty list, so just take the - // first cert from c.config.Certificates - chainToSend = &chain - break findCert - } - - for _, ca := range certReq.certificateAuthorities { - if bytes.Equal(x509Cert.RawIssuer, ca) { - chainToSend = &chain - break findCert - } - } - } + if chainToSend, err = hs.getCertificate(certReq); err != nil { + c.sendAlert(alertInternalError) + return err } msg, err = c.readHandshake() @@ -421,24 +402,26 @@ func (hs *clientHandshakeState) doFullHandshake() error { // certificate to send. if certRequested { certMsg = new(certificateMsg) - if chainToSend != nil { - certMsg.certificates = chainToSend.Certificate - } + 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 { + if chainToSend != nil && len(chainToSend.Certificate) > 0 { certVerify := &certificateVerifyMsg{ hasSignatureAndHash: c.vers >= VersionTLS12, } @@ -477,10 +460,16 @@ 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) + if err := c.config.writeKeyLog(hs.hello.random, hs.masterSecret); err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to write to key log: " + err.Error()) + } hs.finishedHash.discardHandshakeBuffer() @@ -524,6 +513,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 +538,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 +557,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 } - 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") + } + + // 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 +639,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,17 +650,132 @@ 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 } +// tls11SignatureSchemes contains the signature schemes that we synthesise for +// a TLS <= 1.1 connection, based on the supported certificate types. +var tls11SignatureSchemes = []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1} + +const ( + // tls11SignatureSchemesNumECDSA is the number of initial elements of + // tls11SignatureSchemes that use ECDSA. + tls11SignatureSchemesNumECDSA = 3 + // tls11SignatureSchemesNumRSA is the number of trailing elements of + // tls11SignatureSchemes that use RSA. + tls11SignatureSchemesNumRSA = 4 +) + +func (hs *clientHandshakeState) getCertificate(certReq *certificateRequestMsg) (*Certificate, error) { + c := hs.c + + var rsaAvail, ecdsaAvail bool + for _, certType := range certReq.certificateTypes { + switch certType { + case certTypeRSASign: + rsaAvail = true + case certTypeECDSASign: + ecdsaAvail = true + } + } + + if c.config.GetClientCertificate != nil { + var signatureSchemes []SignatureScheme + + if !certReq.hasSignatureAndHash { + // Prior to TLS 1.2, the signature schemes were not + // included in the certificate request message. In this + // case we use a plausible list based on the acceptable + // certificate types. + signatureSchemes = tls11SignatureSchemes + if !ecdsaAvail { + signatureSchemes = signatureSchemes[tls11SignatureSchemesNumECDSA:] + } + if !rsaAvail { + signatureSchemes = signatureSchemes[:len(signatureSchemes)-tls11SignatureSchemesNumRSA] + } + } else { + signatureSchemes = make([]SignatureScheme, 0, len(certReq.signatureAndHashes)) + for _, sah := range certReq.signatureAndHashes { + signatureSchemes = append(signatureSchemes, SignatureScheme(sah.hash)<<8+SignatureScheme(sah.signature)) + } + } + + return c.config.GetClientCertificate(&CertificateRequestInfo{ + AcceptableCAs: certReq.certificateAuthorities, + SignatureSchemes: signatureSchemes, + }) + } + + // RFC 4346 on the certificateAuthorities field: A list of the + // distinguished names of acceptable certificate authorities. + // These distinguished names may specify a desired + // distinguished name for a root CA or for a subordinate CA; + // thus, this message can be used to describe both known roots + // and a desired authorization space. If the + // certificate_authorities list is empty then the client MAY + // send any certificate of the appropriate + // ClientCertificateType, unless there is some external + // arrangement to the contrary. + + // We need to search our list of client certs for one + // where SignatureAlgorithm is acceptable to the server and the + // Issuer is in certReq.certificateAuthorities +findCert: + for i, chain := range c.config.Certificates { + if !rsaAvail && !ecdsaAvail { + continue + } + + for j, cert := range chain.Certificate { + x509Cert := chain.Leaf + // parse the certificate if this isn't the leaf + // node, or if chain.Leaf was nil + if j != 0 || x509Cert == nil { + var err error + if x509Cert, err = x509.ParseCertificate(cert); err != nil { + c.sendAlert(alertInternalError) + return nil, errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + } + } + + switch { + case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: + case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: + default: + continue findCert + } + + if len(certReq.certificateAuthorities) == 0 { + // they gave us an empty list, so just take the + // first cert from c.config.Certificates + return &chain, nil + } + + for _, ca := range certReq.certificateAuthorities { + if bytes.Equal(x509Cert.RawIssuer, ca) { + return &chain, nil + } + } + } + } + + // No acceptable certificate found. Don't send a certificate. + return new(Certificate), nil +} + // clientSessionCacheKey returns a key used to cache sessionTickets that could // be used to resume previously negotiated TLS sessions with a server. func clientSessionCacheKey(serverAddr net.Addr, config *Config) string { @@ -665,3 +800,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 +} |