1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
// Copyright (c) 2018 The truststore Authors. All rights reserved.
// Copyright (c) 2018 The mkcert Authors. All rights reserved.
package truststore
import (
"bytes"
"crypto/x509"
"encoding/asn1"
"fmt"
"os"
"os/exec"
plist "howett.net/plist"
)
var (
// NSSProfile is the path of the Firefox profiles.
NSSProfile = os.Getenv("HOME") + "/Library/Application Support/Firefox/Profiles/*"
// CertutilInstallHelp is the command to run on macOS to add NSS support.
CertutilInstallHelp = "brew install nss"
)
// https://github.com/golang/go/issues/24652#issuecomment-399826583
var trustSettings []interface{}
var _, _ = plist.Unmarshal(trustSettingsData, &trustSettings)
var trustSettingsData = []byte(`
<array>
<dict>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAED
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>sslServer</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
<dict>
<key>kSecTrustSettingsPolicy</key>
<data>
KoZIhvdjZAEC
</data>
<key>kSecTrustSettingsPolicyName</key>
<string>basicX509</string>
<key>kSecTrustSettingsResult</key>
<integer>1</integer>
</dict>
</array>
`)
func installPlatform(filename string, cert *x509.Certificate) error {
cmd := exec.Command("sudo", "security", "add-trusted-cert", "-d", "-k", "/Library/Keychains/System.keychain", filename)
out, err := cmd.CombinedOutput()
if err != nil {
return NewCmdError(err, cmd, out)
}
// Make trustSettings explicit, as older Go does not know the defaults.
// https://github.com/golang/go/issues/24652
plistFile, err := os.CreateTemp("", "trust-settings")
if err != nil {
return wrapError(err, "failed to create temp file")
}
defer os.Remove(plistFile.Name())
//nolint:gosec // tolerable risk necessary for function
cmd = exec.Command("sudo", "security", "trust-settings-export", "-d", plistFile.Name())
out, err = cmd.CombinedOutput()
if err != nil {
return NewCmdError(err, cmd, out)
}
plistData, err := os.ReadFile(plistFile.Name())
if err != nil {
return wrapError(err, "failed to read trust settings")
}
var plistRoot map[string]interface{}
_, err = plist.Unmarshal(plistData, &plistRoot)
if err != nil {
return wrapError(err, "failed to parse trust settings")
}
if v, ok := plistRoot["trustVersion"].(uint64); v != 1 || !ok {
return fmt.Errorf("unsupported trust settings version: %v", plistRoot["trustVersion"])
}
trustList := plistRoot["trustList"].(map[string]interface{})
rootSubjectASN1, _ := asn1.Marshal(cert.Subject.ToRDNSequence())
for key := range trustList {
entry := trustList[key].(map[string]interface{})
if _, ok := entry["issuerName"]; !ok {
continue
}
issuerName := entry["issuerName"].([]byte)
if !bytes.Equal(rootSubjectASN1, issuerName) {
continue
}
entry["trustSettings"] = trustSettings
break
}
plistData, err = plist.MarshalIndent(plistRoot, plist.XMLFormat, "\t")
if err != nil {
return wrapError(err, "failed to serialize trust settings")
}
err = os.WriteFile(plistFile.Name(), plistData, 0600)
if err != nil {
return wrapError(err, "failed to write trust settings")
}
//nolint:gosec // tolerable risk necessary for function
cmd = exec.Command("sudo", "security", "trust-settings-import", "-d", plistFile.Name())
out, err = cmd.CombinedOutput()
if err != nil {
return NewCmdError(err, cmd, out)
}
debug("certificate installed properly in macOS keychain")
return nil
}
func uninstallPlatform(filename string, cert *x509.Certificate) error {
cmd := exec.Command("sudo", "security", "remove-trusted-cert", "-d", filename)
out, err := cmd.CombinedOutput()
if err != nil {
return NewCmdError(err, cmd, out)
}
debug("certificate uninstalled properly from macOS keychain")
return nil
}
|