Imported Upstream version 4.7.3
[platform/upstream/gcc48.git] / libgo / go / crypto / x509 / root_windows.go
1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package x509
6
7 import (
8         "errors"
9         "syscall"
10         "unsafe"
11 )
12
13 // Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
14 // certificate store containing itself and all of the intermediate certificates specified
15 // in the opts.Intermediates CertPool.
16 //
17 // A pointer to the in-memory store is available in the returned CertContext's Store field.
18 // The store is automatically freed when the CertContext is freed using
19 // syscall.CertFreeCertificateContext.
20 func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
21         var storeCtx *syscall.CertContext
22
23         leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
24         if err != nil {
25                 return nil, err
26         }
27         defer syscall.CertFreeCertificateContext(leafCtx)
28
29         handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
30         if err != nil {
31                 return nil, err
32         }
33         defer syscall.CertCloseStore(handle, 0)
34
35         err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
36         if err != nil {
37                 return nil, err
38         }
39
40         if opts.Intermediates != nil {
41                 for _, intermediate := range opts.Intermediates.certs {
42                         ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
43                         if err != nil {
44                                 return nil, err
45                         }
46
47                         err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
48                         syscall.CertFreeCertificateContext(ctx)
49                         if err != nil {
50                                 return nil, err
51                         }
52                 }
53         }
54
55         return storeCtx, nil
56 }
57
58 // extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
59 func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
60         if simpleChain == nil || count == 0 {
61                 return nil, errors.New("x509: invalid simple chain")
62         }
63
64         simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:]
65         lastChain := simpleChains[count-1]
66         elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:]
67         for i := 0; i < int(lastChain.NumElements); i++ {
68                 // Copy the buf, since ParseCertificate does not create its own copy.
69                 cert := elements[i].CertContext
70                 encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
71                 buf := make([]byte, cert.Length)
72                 copy(buf, encodedCert[:])
73                 parsedCert, err := ParseCertificate(buf)
74                 if err != nil {
75                         return nil, err
76                 }
77                 chain = append(chain, parsedCert)
78         }
79
80         return chain, nil
81 }
82
83 // checkChainTrustStatus checks the trust status of the certificate chain, translating
84 // any errors it finds into Go errors in the process.
85 func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
86         if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
87                 status := chainCtx.TrustStatus.ErrorStatus
88                 switch status {
89                 case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
90                         return CertificateInvalidError{c, Expired}
91                 default:
92                         return UnknownAuthorityError{c}
93                 }
94         }
95         return nil
96 }
97
98 // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
99 // use as a certificate chain for a SSL/TLS server.
100 func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
101         sslPara := &syscall.SSLExtraCertChainPolicyPara{
102                 AuthType:   syscall.AUTHTYPE_SERVER,
103                 ServerName: syscall.StringToUTF16Ptr(opts.DNSName),
104         }
105         sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
106
107         para := &syscall.CertChainPolicyPara{
108                 ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
109         }
110         para.Size = uint32(unsafe.Sizeof(*para))
111
112         status := syscall.CertChainPolicyStatus{}
113         err := syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
114         if err != nil {
115                 return err
116         }
117
118         // TODO(mkrautz): use the lChainIndex and lElementIndex fields
119         // of the CertChainPolicyStatus to provide proper context, instead
120         // using c.
121         if status.Error != 0 {
122                 switch status.Error {
123                 case syscall.CERT_E_EXPIRED:
124                         return CertificateInvalidError{c, Expired}
125                 case syscall.CERT_E_CN_NO_MATCH:
126                         return HostnameError{c, opts.DNSName}
127                 case syscall.CERT_E_UNTRUSTEDROOT:
128                         return UnknownAuthorityError{c}
129                 default:
130                         return UnknownAuthorityError{c}
131                 }
132         }
133
134         return nil
135 }
136
137 // systemVerify is like Verify, except that it uses CryptoAPI calls
138 // to build certificate chains and verify them.
139 func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
140         hasDNSName := opts != nil && len(opts.DNSName) > 0
141
142         storeCtx, err := createStoreContext(c, opts)
143         if err != nil {
144                 return nil, err
145         }
146         defer syscall.CertFreeCertificateContext(storeCtx)
147
148         para := new(syscall.CertChainPara)
149         para.Size = uint32(unsafe.Sizeof(*para))
150
151         // If there's a DNSName set in opts, assume we're verifying
152         // a certificate from a TLS server.
153         if hasDNSName {
154                 oids := []*byte{
155                         &syscall.OID_PKIX_KP_SERVER_AUTH[0],
156                         // Both IE and Chrome allow certificates with
157                         // Server Gated Crypto as well. Some certificates
158                         // in the wild require them.
159                         &syscall.OID_SERVER_GATED_CRYPTO[0],
160                         &syscall.OID_SGC_NETSCAPE[0],
161                 }
162                 para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
163                 para.RequestedUsage.Usage.Length = uint32(len(oids))
164                 para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
165         } else {
166                 para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
167                 para.RequestedUsage.Usage.Length = 0
168                 para.RequestedUsage.Usage.UsageIdentifiers = nil
169         }
170
171         var verifyTime *syscall.Filetime
172         if opts != nil && !opts.CurrentTime.IsZero() {
173                 ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
174                 verifyTime = &ft
175         }
176
177         // CertGetCertificateChain will traverse Windows's root stores
178         // in an attempt to build a verified certificate chain.  Once
179         // it has found a verified chain, it stops. MSDN docs on
180         // CERT_CHAIN_CONTEXT:
181         //
182         //   When a CERT_CHAIN_CONTEXT is built, the first simple chain
183         //   begins with an end certificate and ends with a self-signed
184         //   certificate. If that self-signed certificate is not a root
185         //   or otherwise trusted certificate, an attempt is made to
186         //   build a new chain. CTLs are used to create the new chain
187         //   beginning with the self-signed certificate from the original
188         //   chain as the end certificate of the new chain. This process
189         //   continues building additional simple chains until the first
190         //   self-signed certificate is a trusted certificate or until
191         //   an additional simple chain cannot be built.
192         //
193         // The result is that we'll only get a single trusted chain to
194         // return to our caller.
195         var chainCtx *syscall.CertChainContext
196         err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
197         if err != nil {
198                 return nil, err
199         }
200         defer syscall.CertFreeCertificateChain(chainCtx)
201
202         err = checkChainTrustStatus(c, chainCtx)
203         if err != nil {
204                 return nil, err
205         }
206
207         if hasDNSName {
208                 err = checkChainSSLServerPolicy(c, chainCtx, opts)
209                 if err != nil {
210                         return nil, err
211                 }
212         }
213
214         chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
215         if err != nil {
216                 return nil, err
217         }
218
219         chains = append(chains, chain)
220
221         return chains, nil
222 }
223
224 func initSystemRoots() {
225         systemRoots = NewCertPool()
226 }