summaryrefslogtreecommitdiff
path: root/libgo/go/crypto
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-03-30 21:27:11 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-03-30 21:27:11 +0000
commit29f40c0d4065a0f30bbd1d4c4c45e742d42cffb0 (patch)
tree9a0dfa827abe382ac0f44768e5365b87f00ac0a9 /libgo/go/crypto
parent9731acf5058127b5cd40d79134497c798f9188a1 (diff)
downloadgcc-29f40c0d4065a0f30bbd1d4c4c45e742d42cffb0.tar.gz
libgo: Update to weekly.2012-03-13.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@186023 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/crypto')
-rw-r--r--libgo/go/crypto/tls/common.go30
-rw-r--r--libgo/go/crypto/tls/handshake_client.go2
-rw-r--r--libgo/go/crypto/tls/handshake_server_test.go2
-rw-r--r--libgo/go/crypto/tls/root_test.go37
-rw-r--r--libgo/go/crypto/tls/root_windows.go47
-rw-r--r--libgo/go/crypto/tls/tls.go7
-rw-r--r--libgo/go/crypto/x509/pkcs1.go2
-rw-r--r--libgo/go/crypto/x509/root.go17
-rw-r--r--libgo/go/crypto/x509/root_darwin.go (renamed from libgo/go/crypto/tls/root_darwin.go)17
-rw-r--r--libgo/go/crypto/x509/root_stub.go (renamed from libgo/go/crypto/tls/root_stub.go)9
-rw-r--r--libgo/go/crypto/x509/root_unix.go (renamed from libgo/go/crypto/tls/root_unix.go)18
-rw-r--r--libgo/go/crypto/x509/root_windows.go226
-rw-r--r--libgo/go/crypto/x509/verify.go36
-rw-r--r--libgo/go/crypto/x509/verify_test.go53
-rw-r--r--libgo/go/crypto/x509/x509.go2
15 files changed, 377 insertions, 128 deletions
diff --git a/libgo/go/crypto/tls/common.go b/libgo/go/crypto/tls/common.go
index 25f7a920cd3..4ba0bf87481 100644
--- a/libgo/go/crypto/tls/common.go
+++ b/libgo/go/crypto/tls/common.go
@@ -198,14 +198,6 @@ func (c *Config) time() time.Time {
return t()
}
-func (c *Config) rootCAs() *x509.CertPool {
- s := c.RootCAs
- if s == nil {
- s = defaultRoots()
- }
- return s
-}
-
func (c *Config) cipherSuites() []uint16 {
s := c.CipherSuites
if s == nil {
@@ -311,28 +303,16 @@ func defaultConfig() *Config {
return &emptyConfig
}
-var once sync.Once
-
-func defaultRoots() *x509.CertPool {
- once.Do(initDefaults)
- return varDefaultRoots
-}
+var (
+ once sync.Once
+ varDefaultCipherSuites []uint16
+)
func defaultCipherSuites() []uint16 {
- once.Do(initDefaults)
+ once.Do(initDefaultCipherSuites)
return varDefaultCipherSuites
}
-func initDefaults() {
- initDefaultRoots()
- initDefaultCipherSuites()
-}
-
-var (
- varDefaultRoots *x509.CertPool
- varDefaultCipherSuites []uint16
-)
-
func initDefaultCipherSuites() {
varDefaultCipherSuites = make([]uint16, len(cipherSuites))
for i, suite := range cipherSuites {
diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go
index 0d7b806ff5b..266eb8f578d 100644
--- a/libgo/go/crypto/tls/handshake_client.go
+++ b/libgo/go/crypto/tls/handshake_client.go
@@ -102,7 +102,7 @@ func (c *Conn) clientHandshake() error {
if !c.config.InsecureSkipVerify {
opts := x509.VerifyOptions{
- Roots: c.config.rootCAs(),
+ Roots: c.config.RootCAs,
CurrentTime: c.config.time(),
DNSName: c.config.ServerName,
Intermediates: x509.NewCertPool(),
diff --git a/libgo/go/crypto/tls/handshake_server_test.go b/libgo/go/crypto/tls/handshake_server_test.go
index bd31d31ae1a..08a0ccb0980 100644
--- a/libgo/go/crypto/tls/handshake_server_test.go
+++ b/libgo/go/crypto/tls/handshake_server_test.go
@@ -143,7 +143,7 @@ func testServerScript(t *testing.T, name string, serverScript [][]byte, config *
if peers != nil {
gotpeers := <-pchan
if len(peers) == len(gotpeers) {
- for i, _ := range peers {
+ for i := range peers {
if !peers[i].Equal(gotpeers[i]) {
t.Fatalf("%s: mismatch on peer cert %d", name, i)
}
diff --git a/libgo/go/crypto/tls/root_test.go b/libgo/go/crypto/tls/root_test.go
index 95a89d843c8..e61c2185126 100644
--- a/libgo/go/crypto/tls/root_test.go
+++ b/libgo/go/crypto/tls/root_test.go
@@ -5,25 +5,25 @@
package tls
import (
+ "crypto/x509"
+ "runtime"
"testing"
)
var tlsServers = []string{
- "google.com:443",
- "github.com:443",
- "twitter.com:443",
+ "google.com",
+ "github.com",
+ "twitter.com",
}
func TestOSCertBundles(t *testing.T) {
- defaultRoots()
-
if testing.Short() {
t.Logf("skipping certificate tests in short mode")
return
}
for _, addr := range tlsServers {
- conn, err := Dial("tcp", addr, nil)
+ conn, err := Dial("tcp", addr+":443", &Config{ServerName: addr})
if err != nil {
t.Errorf("unable to verify %v: %v", addr, err)
continue
@@ -34,3 +34,28 @@ func TestOSCertBundles(t *testing.T) {
}
}
}
+
+func TestCertHostnameVerifyWindows(t *testing.T) {
+ if runtime.GOOS != "windows" {
+ return
+ }
+
+ if testing.Short() {
+ t.Logf("skipping certificate tests in short mode")
+ return
+ }
+
+ for _, addr := range tlsServers {
+ cfg := &Config{ServerName: "example.com"}
+ conn, err := Dial("tcp", addr+":443", cfg)
+ if err == nil {
+ conn.Close()
+ t.Errorf("should fail to verify for example.com: %v", addr)
+ continue
+ }
+ _, ok := err.(x509.HostnameError)
+ if !ok {
+ t.Errorf("error type mismatch, got: %v", err)
+ }
+ }
+}
diff --git a/libgo/go/crypto/tls/root_windows.go b/libgo/go/crypto/tls/root_windows.go
deleted file mode 100644
index 319309ae6e7..00000000000
--- a/libgo/go/crypto/tls/root_windows.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package tls
-
-import (
- "crypto/x509"
- "syscall"
- "unsafe"
-)
-
-func loadStore(roots *x509.CertPool, name string) {
- store, err := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name))
- if err != nil {
- return
- }
- defer syscall.CertCloseStore(store, 0)
-
- var cert *syscall.CertContext
- for {
- cert, err = syscall.CertEnumCertificatesInStore(store, cert)
- if err != nil {
- return
- }
-
- buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
- // ParseCertificate requires its own copy of certificate data to keep.
- buf2 := make([]byte, cert.Length)
- copy(buf2, buf)
- if c, err := x509.ParseCertificate(buf2); err == nil {
- roots.AddCert(c)
- }
- }
-}
-
-func initDefaultRoots() {
- roots := x509.NewCertPool()
-
- // Roots
- loadStore(roots, "ROOT")
-
- // Intermediates
- loadStore(roots, "CA")
-
- varDefaultRoots = roots
-}
diff --git a/libgo/go/crypto/tls/tls.go b/libgo/go/crypto/tls/tls.go
index 9184e8e8118..09df5ad445c 100644
--- a/libgo/go/crypto/tls/tls.go
+++ b/libgo/go/crypto/tls/tls.go
@@ -2,8 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package tls partially implements the TLS 1.1 protocol, as specified in RFC
-// 4346.
+// Package tls partially implements TLS 1.0, as specified in RFC 2246.
package tls
import (
@@ -98,7 +97,9 @@ func Dial(network, addr string, config *Config) (*Conn, error) {
if config == nil {
config = defaultConfig()
}
- if config.ServerName != "" {
+ // If no ServerName is set, infer the ServerName
+ // from the hostname we're connecting to.
+ if config.ServerName == "" {
// Make a copy to avoid polluting argument or default.
c := *config
c.ServerName = hostname
diff --git a/libgo/go/crypto/x509/pkcs1.go b/libgo/go/crypto/x509/pkcs1.go
index 3aaa8c5832a..873d3966eb5 100644
--- a/libgo/go/crypto/x509/pkcs1.go
+++ b/libgo/go/crypto/x509/pkcs1.go
@@ -24,7 +24,7 @@ type pkcs1PrivateKey struct {
Dq *big.Int `asn1:"optional"`
Qinv *big.Int `asn1:"optional"`
- AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
+ AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"`
}
type pkcs1AdditionalRSAPrime struct {
diff --git a/libgo/go/crypto/x509/root.go b/libgo/go/crypto/x509/root.go
new file mode 100644
index 00000000000..8aae14e09ee
--- /dev/null
+++ b/libgo/go/crypto/x509/root.go
@@ -0,0 +1,17 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+import "sync"
+
+var (
+ once sync.Once
+ systemRoots *CertPool
+)
+
+func systemRootsPool() *CertPool {
+ once.Do(initSystemRoots)
+ return systemRoots
+}
diff --git a/libgo/go/crypto/tls/root_darwin.go b/libgo/go/crypto/x509/root_darwin.go
index 911a9a62e3c..0f99581e8a7 100644
--- a/libgo/go/crypto/tls/root_darwin.go
+++ b/libgo/go/crypto/x509/root_darwin.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tls
+package x509
/*
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060
@@ -59,13 +59,14 @@ int FetchPEMRoots(CFDataRef *pemRoots) {
}
*/
import "C"
-import (
- "crypto/x509"
- "unsafe"
-)
+import "unsafe"
-func initDefaultRoots() {
- roots := x509.NewCertPool()
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+ return nil, nil
+}
+
+func initSystemRoots() {
+ roots := NewCertPool()
var data C.CFDataRef = nil
err := C.FetchPEMRoots(&data)
@@ -75,5 +76,5 @@ func initDefaultRoots() {
roots.AppendCertsFromPEM(buf)
}
- varDefaultRoots = roots
+ systemRoots = roots
}
diff --git a/libgo/go/crypto/tls/root_stub.go b/libgo/go/crypto/x509/root_stub.go
index ee2c3e01795..568004108b5 100644
--- a/libgo/go/crypto/tls/root_stub.go
+++ b/libgo/go/crypto/x509/root_stub.go
@@ -4,7 +4,12 @@
// +build plan9 darwin,!cgo
-package tls
+package x509
-func initDefaultRoots() {
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+ return nil, nil
+}
+
+func initSystemRoots() {
+ systemRoots = NewCertPool()
}
diff --git a/libgo/go/crypto/tls/root_unix.go b/libgo/go/crypto/x509/root_unix.go
index acaf3dd9d67..76e79f494f7 100644
--- a/libgo/go/crypto/tls/root_unix.go
+++ b/libgo/go/crypto/x509/root_unix.go
@@ -4,12 +4,9 @@
// +build freebsd linux openbsd netbsd
-package tls
+package x509
-import (
- "crypto/x509"
- "io/ioutil"
-)
+import "io/ioutil"
// Possible certificate files; stop after finding one.
var certFiles = []string{
@@ -20,8 +17,12 @@ var certFiles = []string{
"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD
}
-func initDefaultRoots() {
- roots := x509.NewCertPool()
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+ return nil, nil
+}
+
+func initSystemRoots() {
+ roots := NewCertPool()
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
@@ -29,5 +30,6 @@ func initDefaultRoots() {
break
}
}
- varDefaultRoots = roots
+
+ systemRoots = roots
}
diff --git a/libgo/go/crypto/x509/root_windows.go b/libgo/go/crypto/x509/root_windows.go
new file mode 100644
index 00000000000..7e8f2af4b0e
--- /dev/null
+++ b/libgo/go/crypto/x509/root_windows.go
@@ -0,0 +1,226 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+import (
+ "errors"
+ "syscall"
+ "unsafe"
+)
+
+// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
+// certificate store containing itself and all of the intermediate certificates specified
+// in the opts.Intermediates CertPool.
+//
+// A pointer to the in-memory store is available in the returned CertContext's Store field.
+// The store is automatically freed when the CertContext is freed using
+// syscall.CertFreeCertificateContext.
+func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
+ var storeCtx *syscall.CertContext
+
+ leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
+ if err != nil {
+ return nil, err
+ }
+ defer syscall.CertFreeCertificateContext(leafCtx)
+
+ handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
+ if err != nil {
+ return nil, err
+ }
+ defer syscall.CertCloseStore(handle, 0)
+
+ err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
+ if err != nil {
+ return nil, err
+ }
+
+ if opts.Intermediates != nil {
+ for _, intermediate := range opts.Intermediates.certs {
+ ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
+ if err != nil {
+ return nil, err
+ }
+
+ err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
+ syscall.CertFreeCertificateContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return storeCtx, nil
+}
+
+// extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
+func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
+ if simpleChain == nil || count == 0 {
+ return nil, errors.New("x509: invalid simple chain")
+ }
+
+ simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:]
+ lastChain := simpleChains[count-1]
+ elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:]
+ for i := 0; i < int(lastChain.NumElements); i++ {
+ // Copy the buf, since ParseCertificate does not create its own copy.
+ cert := elements[i].CertContext
+ encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
+ buf := make([]byte, cert.Length)
+ copy(buf, encodedCert[:])
+ parsedCert, err := ParseCertificate(buf)
+ if err != nil {
+ return nil, err
+ }
+ chain = append(chain, parsedCert)
+ }
+
+ return chain, nil
+}
+
+// checkChainTrustStatus checks the trust status of the certificate chain, translating
+// any errors it finds into Go errors in the process.
+func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
+ if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
+ status := chainCtx.TrustStatus.ErrorStatus
+ switch status {
+ case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
+ return CertificateInvalidError{c, Expired}
+ default:
+ return UnknownAuthorityError{c}
+ }
+ }
+ return nil
+}
+
+// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
+// use as a certificate chain for a SSL/TLS server.
+func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
+ sslPara := &syscall.SSLExtraCertChainPolicyPara{
+ AuthType: syscall.AUTHTYPE_SERVER,
+ ServerName: syscall.StringToUTF16Ptr(opts.DNSName),
+ }
+ sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
+
+ para := &syscall.CertChainPolicyPara{
+ ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
+ }
+ para.Size = uint32(unsafe.Sizeof(*para))
+
+ status := syscall.CertChainPolicyStatus{}
+ err := syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
+ if err != nil {
+ return err
+ }
+
+ // TODO(mkrautz): use the lChainIndex and lElementIndex fields
+ // of the CertChainPolicyStatus to provide proper context, instead
+ // using c.
+ if status.Error != 0 {
+ switch status.Error {
+ case syscall.CERT_E_EXPIRED:
+ return CertificateInvalidError{c, Expired}
+ case syscall.CERT_E_CN_NO_MATCH:
+ return HostnameError{c, opts.DNSName}
+ case syscall.CERT_E_UNTRUSTEDROOT:
+ return UnknownAuthorityError{c}
+ default:
+ return UnknownAuthorityError{c}
+ }
+ }
+
+ return nil
+}
+
+// systemVerify is like Verify, except that it uses CryptoAPI calls
+// to build certificate chains and verify them.
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+ hasDNSName := opts != nil && len(opts.DNSName) > 0
+
+ storeCtx, err := createStoreContext(c, opts)
+ if err != nil {
+ return nil, err
+ }
+ defer syscall.CertFreeCertificateContext(storeCtx)
+
+ para := new(syscall.CertChainPara)
+ para.Size = uint32(unsafe.Sizeof(*para))
+
+ // If there's a DNSName set in opts, assume we're verifying
+ // a certificate from a TLS server.
+ if hasDNSName {
+ oids := []*byte{
+ &syscall.OID_PKIX_KP_SERVER_AUTH[0],
+ // Both IE and Chrome allow certificates with
+ // Server Gated Crypto as well. Some certificates
+ // in the wild require them.
+ &syscall.OID_SERVER_GATED_CRYPTO[0],
+ &syscall.OID_SGC_NETSCAPE[0],
+ }
+ para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
+ para.RequestedUsage.Usage.Length = uint32(len(oids))
+ para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
+ } else {
+ para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
+ para.RequestedUsage.Usage.Length = 0
+ para.RequestedUsage.Usage.UsageIdentifiers = nil
+ }
+
+ var verifyTime *syscall.Filetime
+ if opts != nil && !opts.CurrentTime.IsZero() {
+ ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
+ verifyTime = &ft
+ }
+
+ // CertGetCertificateChain will traverse Windows's root stores
+ // in an attempt to build a verified certificate chain. Once
+ // it has found a verified chain, it stops. MSDN docs on
+ // CERT_CHAIN_CONTEXT:
+ //
+ // When a CERT_CHAIN_CONTEXT is built, the first simple chain
+ // begins with an end certificate and ends with a self-signed
+ // certificate. If that self-signed certificate is not a root
+ // or otherwise trusted certificate, an attempt is made to
+ // build a new chain. CTLs are used to create the new chain
+ // beginning with the self-signed certificate from the original
+ // chain as the end certificate of the new chain. This process
+ // continues building additional simple chains until the first
+ // self-signed certificate is a trusted certificate or until
+ // an additional simple chain cannot be built.
+ //
+ // The result is that we'll only get a single trusted chain to
+ // return to our caller.
+ var chainCtx *syscall.CertChainContext
+ err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
+ if err != nil {
+ return nil, err
+ }
+ defer syscall.CertFreeCertificateChain(chainCtx)
+
+ err = checkChainTrustStatus(c, chainCtx)
+ if err != nil {
+ return nil, err
+ }
+
+ if hasDNSName {
+ err = checkChainSSLServerPolicy(c, chainCtx, opts)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
+ if err != nil {
+ return nil, err
+ }
+
+ chains = append(chains, chain)
+
+ return chains, nil
+}
+
+func initSystemRoots() {
+ systemRoots = NewCertPool()
+}
diff --git a/libgo/go/crypto/x509/verify.go b/libgo/go/crypto/x509/verify.go
index 3859dd8d488..307c5ef0339 100644
--- a/libgo/go/crypto/x509/verify.go
+++ b/libgo/go/crypto/x509/verify.go
@@ -5,6 +5,7 @@
package x509
import (
+ "runtime"
"strings"
"time"
"unicode/utf8"
@@ -23,6 +24,9 @@ const (
// certificate has a name constraint which doesn't include the name
// being checked.
CANotAuthorizedForThisName
+ // TooManyIntermediates results when a path length constraint is
+ // violated.
+ TooManyIntermediates
)
// CertificateInvalidError results when an odd error occurs. Users of this
@@ -40,6 +44,8 @@ func (e CertificateInvalidError) Error() string {
return "x509: certificate has expired or is not yet valid"
case CANotAuthorizedForThisName:
return "x509: a root or intermediate certificate is not authorized to sign in this domain"
+ case TooManyIntermediates:
+ return "x509: too many intermediates for path length constraint"
}
return "x509: unknown error"
}
@@ -76,7 +82,7 @@ func (e UnknownAuthorityError) Error() string {
type VerifyOptions struct {
DNSName string
Intermediates *CertPool
- Roots *CertPool
+ Roots *CertPool // if nil, the system roots are used
CurrentTime time.Time // if zero, the current time is used
}
@@ -87,7 +93,7 @@ const (
)
// isValid performs validity checks on the c.
-func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
+func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
now := opts.CurrentTime
if now.IsZero() {
now = time.Now()
@@ -130,26 +136,44 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
return CertificateInvalidError{c, NotAuthorizedToSign}
}
+ if c.BasicConstraintsValid && c.MaxPathLen >= 0 {
+ numIntermediates := len(currentChain) - 1
+ if numIntermediates > c.MaxPathLen {
+ return CertificateInvalidError{c, TooManyIntermediates}
+ }
+ }
+
return nil
}
// Verify attempts to verify c by building one or more chains from c to a
-// certificate in opts.roots, using certificates in opts.Intermediates if
+// certificate in opts.Roots, using certificates in opts.Intermediates if
// needed. If successful, it returns one or more chains where the first
// element of the chain is c and the last element is from opts.Roots.
//
// WARNING: this doesn't do any revocation checking.
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
- err = c.isValid(leafCertificate, &opts)
+ // Use Windows's own verification and chain building.
+ if opts.Roots == nil && runtime.GOOS == "windows" {
+ return c.systemVerify(&opts)
+ }
+
+ if opts.Roots == nil {
+ opts.Roots = systemRootsPool()
+ }
+
+ err = c.isValid(leafCertificate, nil, &opts)
if err != nil {
return
}
+
if len(opts.DNSName) > 0 {
err = c.VerifyHostname(opts.DNSName)
if err != nil {
return
}
}
+
return c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
}
@@ -163,7 +187,7 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate
func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
for _, rootNum := range opts.Roots.findVerifiedParents(c) {
root := opts.Roots.certs[rootNum]
- err = root.isValid(rootCertificate, opts)
+ err = root.isValid(rootCertificate, currentChain, opts)
if err != nil {
continue
}
@@ -178,7 +202,7 @@ nextIntermediate:
continue nextIntermediate
}
}
- err = intermediate.isValid(intermediateCertificate, opts)
+ err = intermediate.isValid(intermediateCertificate, currentChain, opts)
if err != nil {
continue
}
diff --git a/libgo/go/crypto/x509/verify_test.go b/libgo/go/crypto/x509/verify_test.go
index 2cdd66a5589..7b171b291a4 100644
--- a/libgo/go/crypto/x509/verify_test.go
+++ b/libgo/go/crypto/x509/verify_test.go
@@ -8,6 +8,7 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"errors"
+ "runtime"
"strings"
"testing"
"time"
@@ -19,7 +20,7 @@ type verifyTest struct {
roots []string
currentTime int64
dnsName string
- nilRoots bool
+ systemSkip bool
errorCallback func(*testing.T, int, error) bool
expectedChains [][]string
@@ -60,14 +61,6 @@ var verifyTests = []verifyTest{
{
leaf: googleLeaf,
intermediates: []string{thawteIntermediate},
- nilRoots: true, // verifies that we don't crash
- currentTime: 1302726541,
- dnsName: "www.google.com",
- errorCallback: expectAuthorityUnknown,
- },
- {
- leaf: googleLeaf,
- intermediates: []string{thawteIntermediate},
roots: []string{verisignRoot},
currentTime: 1,
dnsName: "www.example.com",
@@ -80,6 +73,9 @@ var verifyTests = []verifyTest{
currentTime: 1302726541,
dnsName: "www.google.com",
+ // Skip when using systemVerify, since Windows
+ // *will* find the missing intermediate cert.
+ systemSkip: true,
errorCallback: expectAuthorityUnknown,
},
{
@@ -109,6 +105,9 @@ var verifyTests = []verifyTest{
roots: []string{startComRoot},
currentTime: 1302726541,
+ // Skip when using systemVerify, since Windows
+ // can only return a single chain to us (for now).
+ systemSkip: true,
expectedChains: [][]string{
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
@@ -148,23 +147,26 @@ func certificateFromPEM(pemBytes string) (*Certificate, error) {
return ParseCertificate(block.Bytes)
}
-func TestVerify(t *testing.T) {
+func testVerify(t *testing.T, useSystemRoots bool) {
for i, test := range verifyTests {
+ if useSystemRoots && test.systemSkip {
+ continue
+ }
+
opts := VerifyOptions{
- Roots: NewCertPool(),
Intermediates: NewCertPool(),
DNSName: test.dnsName,
CurrentTime: time.Unix(test.currentTime, 0),
}
- if test.nilRoots {
- opts.Roots = nil
- }
- for j, root := range test.roots {
- ok := opts.Roots.AppendCertsFromPEM([]byte(root))
- if !ok {
- t.Errorf("#%d: failed to parse root #%d", i, j)
- return
+ if !useSystemRoots {
+ opts.Roots = NewCertPool()
+ for j, root := range test.roots {
+ ok := opts.Roots.AppendCertsFromPEM([]byte(root))
+ if !ok {
+ t.Errorf("#%d: failed to parse root #%d", i, j)
+ return
+ }
}
}
@@ -225,6 +227,19 @@ func TestVerify(t *testing.T) {
}
}
+func TestGoVerify(t *testing.T) {
+ testVerify(t, false)
+}
+
+func TestSystemVerify(t *testing.T) {
+ if runtime.GOOS != "windows" {
+ t.Logf("skipping verify test using system APIs on %q", runtime.GOOS)
+ return
+ }
+
+ testVerify(t, true)
+}
+
func chainToDebugString(chain []*Certificate) string {
var chainStr string
for _, cert := range chain {
diff --git a/libgo/go/crypto/x509/x509.go b/libgo/go/crypto/x509/x509.go
index f5da86b54a0..8dae7e7fcf9 100644
--- a/libgo/go/crypto/x509/x509.go
+++ b/libgo/go/crypto/x509/x509.go
@@ -429,7 +429,7 @@ func (h UnhandledCriticalExtension) Error() string {
type basicConstraints struct {
IsCA bool `asn1:"optional"`
- MaxPathLen int `asn1:"optional"`
+ MaxPathLen int `asn1:"optional,default:-1"`
}
// RFC 5280 4.2.1.4