mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-03 23:15:42 +00:00
3bab226299
Snapshotted from Go commit 619c7a48a38b28b521591b490fd14ccb7ea5e821 (https://go-review.googlesource.com/c/go/+/229762, "crypto/x509: add x509omitbundledroots build tag to not embed roots") With 975c01342a25899962969833d8b2873dc8856a4f (https://go-review.googlesource.com/c/go/+/220721) removed, because it depends on other stuff in Go std that doesn't yet exist in a Go release. Also, add a subset fork of Go's internal/testenv, for use by x509's tests.
185 lines
4.5 KiB
Go
185 lines
4.5 KiB
Go
// Copyright 2015 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.
|
|
|
|
// +build ignore
|
|
|
|
// Generates root_darwin_arm64.go.
|
|
//
|
|
// As of iOS 8, there is no API for querying the system trusted X.509 root
|
|
// certificates. We could use SecTrustEvaluate to verify that a trust chain
|
|
// exists for a certificate, but the x509 API requires returning the entire
|
|
// chain.
|
|
//
|
|
// Apple publishes the list of trusted root certificates for iOS on
|
|
// support.apple.com. So we parse the list and extract the certificates from
|
|
// an OS X machine and embed them into the x509 package.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"flag"
|
|
"fmt"
|
|
"go/format"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var output = flag.String("output", "root_darwin_arm64.go", "file name to write")
|
|
|
|
func main() {
|
|
certs, err := selectCerts()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
fmt.Fprintf(buf, "// Code generated by root_darwin_arm_gen --output %s; DO NOT EDIT.\n", *output)
|
|
fmt.Fprintf(buf, "%s", header)
|
|
|
|
fmt.Fprintf(buf, "const systemRootsPEM = `\n")
|
|
for _, cert := range certs {
|
|
b := &pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: cert.Raw,
|
|
}
|
|
if err := pem.Encode(buf, b); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
fmt.Fprintf(buf, "`")
|
|
|
|
source, err := format.Source(buf.Bytes())
|
|
if err != nil {
|
|
log.Fatal("source format error:", err)
|
|
}
|
|
if err := ioutil.WriteFile(*output, source, 0644); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func selectCerts() ([]*x509.Certificate, error) {
|
|
ids, err := fetchCertIDs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
scerts, err := sysCerts()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var certs []*x509.Certificate
|
|
for _, id := range ids {
|
|
if c, ok := scerts[id.fingerprint]; ok {
|
|
certs = append(certs, c)
|
|
} else {
|
|
fmt.Printf("WARNING: cannot find certificate: %s (fingerprint: %s)\n", id.name, id.fingerprint)
|
|
}
|
|
}
|
|
return certs, nil
|
|
}
|
|
|
|
func sysCerts() (certs map[string]*x509.Certificate, err error) {
|
|
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
|
|
data, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
certs = make(map[string]*x509.Certificate)
|
|
for len(data) > 0 {
|
|
var block *pem.Block
|
|
block, data = pem.Decode(data)
|
|
if block == nil {
|
|
break
|
|
}
|
|
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
|
continue
|
|
}
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
fingerprint := sha256.Sum256(cert.Raw)
|
|
certs[hex.EncodeToString(fingerprint[:])] = cert
|
|
}
|
|
return certs, nil
|
|
}
|
|
|
|
type certID struct {
|
|
name string
|
|
fingerprint string
|
|
}
|
|
|
|
// fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
|
|
func fetchCertIDs() ([]certID, error) {
|
|
// Download the iOS 11 support page. The index for all iOS versions is here:
|
|
// https://support.apple.com/en-us/HT204132
|
|
resp, err := http.Get("https://support.apple.com/en-us/HT208125")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
text := string(body)
|
|
text = text[strings.Index(text, "<div id=trusted"):]
|
|
text = text[:strings.Index(text, "</div>")]
|
|
|
|
var ids []certID
|
|
cols := make(map[string]int)
|
|
for i, rowmatch := range regexp.MustCompile("(?s)<tr>(.*?)</tr>").FindAllStringSubmatch(text, -1) {
|
|
row := rowmatch[1]
|
|
if i == 0 {
|
|
// Parse table header row to extract column names
|
|
for i, match := range regexp.MustCompile("(?s)<th>(.*?)</th>").FindAllStringSubmatch(row, -1) {
|
|
cols[match[1]] = i
|
|
}
|
|
continue
|
|
}
|
|
|
|
values := regexp.MustCompile("(?s)<td>(.*?)</td>").FindAllStringSubmatch(row, -1)
|
|
name := values[cols["Certificate name"]][1]
|
|
fingerprint := values[cols["Fingerprint (SHA-256)"]][1]
|
|
fingerprint = strings.ReplaceAll(fingerprint, "<br>", "")
|
|
fingerprint = strings.ReplaceAll(fingerprint, "\n", "")
|
|
fingerprint = strings.ReplaceAll(fingerprint, " ", "")
|
|
fingerprint = strings.ToLower(fingerprint)
|
|
|
|
ids = append(ids, certID{
|
|
name: name,
|
|
fingerprint: fingerprint,
|
|
})
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
const header = `
|
|
// Copyright 2015 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.
|
|
|
|
// +build !x509omitbundledroots
|
|
|
|
package x509
|
|
|
|
func loadSystemRoots() (*CertPool, error) {
|
|
p := NewCertPool()
|
|
p.AppendCertsFromPEM([]byte(systemRootsPEM))
|
|
return p, nil
|
|
}
|
|
`
|