1
0
mirror of https://github.com/golang/go synced 2024-11-18 00:14:47 -07:00

crypto/x509: allow server gated crypto in windows systemVerify

Also factors out some code into functions to make
systemVerify easier to read.

R=rsc, agl
CC=golang-dev
https://golang.org/cl/5781054
This commit is contained in:
Mikkel Krautz 2012-03-08 11:28:04 -05:00 committed by Adam Langley
parent dfb1af4b97
commit 3133b14b30
2 changed files with 105 additions and 66 deletions

View File

@ -5,6 +5,7 @@
package x509 package x509
import ( import (
"errors"
"syscall" "syscall"
"unsafe" "unsafe"
) )
@ -58,6 +59,87 @@ func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertCo
return storeCtx, nil 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) { func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
hasDNSName := opts != nil && len(opts.DNSName) > 0 hasDNSName := opts != nil && len(opts.DNSName) > 0
@ -69,15 +151,23 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
para := new(syscall.CertChainPara) para := new(syscall.CertChainPara)
para.Size = uint32(unsafe.Sizeof(*para)) para.Size = uint32(unsafe.Sizeof(*para))
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
// If there's a DNSName set in opts, assume we're verifying // If there's a DNSName set in opts, assume we're verifying
// a certificate from a TLS server. // a certificate from a TLS server.
if hasDNSName { if hasDNSName {
oids := []*byte{&syscall.OID_PKIX_KP_SERVER_AUTH[0]} 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.Length = uint32(len(oids))
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0] para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
} else { } else {
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
para.RequestedUsage.Usage.Length = 0 para.RequestedUsage.Usage.Length = 0
para.RequestedUsage.Usage.UsageIdentifiers = nil para.RequestedUsage.Usage.UsageIdentifiers = nil
} }
@ -113,77 +203,24 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
} }
defer syscall.CertFreeCertificateChain(chainCtx) defer syscall.CertFreeCertificateChain(chainCtx)
if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR { err = checkChainTrustStatus(c, chainCtx)
status := chainCtx.TrustStatus.ErrorStatus
switch status {
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
return nil, CertificateInvalidError{c, Expired}
default:
return nil, UnknownAuthorityError{c}
}
}
simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(chainCtx.Chains))[:]
if chainCtx.ChainCount == 0 {
return nil, UnknownAuthorityError{c}
}
verifiedChain := simpleChains[int(chainCtx.ChainCount)-1]
elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(verifiedChain.Elements))[:]
if verifiedChain.NumElements == 0 {
return nil, UnknownAuthorityError{c}
}
var chain []*Certificate
for i := 0; i < int(verifiedChain.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 { if err != nil {
return nil, err return nil, err
} }
chain = append(chain, parsedCert)
}
// Apply the system SSL policy if VerifyOptions dictates that we
// must check for a DNS name.
if hasDNSName { if hasDNSName {
sslPara := &syscall.SSLExtraCertChainPolicyPara{ err = checkChainSSLServerPolicy(c, chainCtx, opts)
AuthType: syscall.AUTHTYPE_SERVER, if err != nil {
ServerName: syscall.StringToUTF16Ptr(opts.DNSName), return nil, err
} }
sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
para := &syscall.CertChainPolicyPara{
ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
} }
para.Size = uint32(unsafe.Sizeof(*para))
status := syscall.CertChainPolicyStatus{} chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if status.Error != 0 { chains = append(chains, chain)
switch status.Error {
case syscall.CERT_E_EXPIRED:
return nil, CertificateInvalidError{c, Expired}
case syscall.CERT_E_CN_NO_MATCH:
return nil, HostnameError{c, opts.DNSName}
case syscall.CERT_E_UNTRUSTEDROOT:
return nil, UnknownAuthorityError{c}
default:
return nil, UnknownAuthorityError{c}
}
}
}
chains = make([][]*Certificate, 1)
chains[0] = chain
return chains, nil return chains, nil
} }

View File

@ -262,7 +262,9 @@ const (
) )
var ( var (
OID_PKIX_KP_SERVER_AUTH = []byte("1.3.6.1.5.5.7.3.1" + string([]byte{0})) OID_PKIX_KP_SERVER_AUTH = []byte("1.3.6.1.5.5.7.3.1\x00")
OID_SERVER_GATED_CRYPTO = []byte("1.3.6.1.4.1.311.10.3.3\x00")
OID_SGC_NETSCAPE = []byte("2.16.840.1.113730.4.1\x00")
) )
// Invented values to support what package os expects. // Invented values to support what package os expects.