1// Copyright 2020 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
5package x509
6
7import (
8	macOS "crypto/x509/internal/macos"
9	"errors"
10	"fmt"
11)
12
13func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
14	certs := macOS.CFArrayCreateMutable()
15	defer macOS.ReleaseCFArray(certs)
16	leaf, err := macOS.SecCertificateCreateWithData(c.Raw)
17	if err != nil {
18		return nil, errors.New("invalid leaf certificate")
19	}
20	macOS.CFArrayAppendValue(certs, leaf)
21	if opts.Intermediates != nil {
22		for _, lc := range opts.Intermediates.lazyCerts {
23			c, err := lc.getCert()
24			if err != nil {
25				return nil, err
26			}
27			sc, err := macOS.SecCertificateCreateWithData(c.Raw)
28			if err != nil {
29				return nil, err
30			}
31			macOS.CFArrayAppendValue(certs, sc)
32		}
33	}
34
35	policies := macOS.CFArrayCreateMutable()
36	defer macOS.ReleaseCFArray(policies)
37	sslPolicy, err := macOS.SecPolicyCreateSSL(opts.DNSName)
38	if err != nil {
39		return nil, err
40	}
41	macOS.CFArrayAppendValue(policies, sslPolicy)
42
43	trustObj, err := macOS.SecTrustCreateWithCertificates(certs, policies)
44	if err != nil {
45		return nil, err
46	}
47	defer macOS.CFRelease(trustObj)
48
49	if !opts.CurrentTime.IsZero() {
50		dateRef := macOS.TimeToCFDateRef(opts.CurrentTime)
51		defer macOS.CFRelease(dateRef)
52		if err := macOS.SecTrustSetVerifyDate(trustObj, dateRef); err != nil {
53			return nil, err
54		}
55	}
56
57	// TODO(roland): we may want to allow passing in SCTs via VerifyOptions and
58	// set them via SecTrustSetSignedCertificateTimestamps, since Apple will
59	// always enforce its SCT requirements, and there are still _some_ people
60	// using TLS or OCSP for that.
61
62	if ret, err := macOS.SecTrustEvaluateWithError(trustObj); err != nil {
63		switch ret {
64		case macOS.ErrSecCertificateExpired:
65			return nil, CertificateInvalidError{c, Expired, err.Error()}
66		case macOS.ErrSecHostNameMismatch:
67			return nil, HostnameError{c, opts.DNSName}
68		case macOS.ErrSecNotTrusted:
69			return nil, UnknownAuthorityError{Cert: c}
70		default:
71			return nil, fmt.Errorf("x509: %s", err)
72		}
73	}
74
75	chain := [][]*Certificate{{}}
76	numCerts := macOS.SecTrustGetCertificateCount(trustObj)
77	for i := 0; i < numCerts; i++ {
78		certRef, err := macOS.SecTrustGetCertificateAtIndex(trustObj, i)
79		if err != nil {
80			return nil, err
81		}
82		cert, err := exportCertificate(certRef)
83		if err != nil {
84			return nil, err
85		}
86		chain[0] = append(chain[0], cert)
87	}
88	if len(chain[0]) == 0 {
89		// This should _never_ happen, but to be safe
90		return nil, errors.New("x509: macOS certificate verification internal error")
91	}
92
93	if opts.DNSName != "" {
94		// If we have a DNS name, apply our own name verification
95		if err := chain[0][0].VerifyHostname(opts.DNSName); err != nil {
96			return nil, err
97		}
98	}
99
100	keyUsages := opts.KeyUsages
101	if len(keyUsages) == 0 {
102		keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
103	}
104
105	// If any key usage is acceptable then we're done.
106	for _, usage := range keyUsages {
107		if usage == ExtKeyUsageAny {
108			return chain, nil
109		}
110	}
111
112	if !checkChainForKeyUsage(chain[0], keyUsages) {
113		return nil, CertificateInvalidError{c, IncompatibleUsage, ""}
114	}
115
116	return chain, nil
117}
118
119// exportCertificate returns a *Certificate for a SecCertificateRef.
120func exportCertificate(cert macOS.CFRef) (*Certificate, error) {
121	data, err := macOS.SecCertificateCopyData(cert)
122	if err != nil {
123		return nil, err
124	}
125	return ParseCertificate(data)
126}
127
128func loadSystemRoots() (*CertPool, error) {
129	return &CertPool{systemPool: true}, nil
130}
131