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:
parent
dfb1af4b97
commit
3133b14b30
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user