mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* feat: Add opt-in support for proxy CA cert installation * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * docs: Add limitation for MacOS MDM script
101 lines
2.9 KiB
Go
101 lines
2.9 KiB
Go
//go:build darwin
|
|
// +build darwin
|
|
|
|
package truststore
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/safedep/dry/log"
|
|
)
|
|
|
|
const systemKeychainPath = "/Library/Keychains/System.keychain"
|
|
|
|
func userScopeSupportedPlatform() bool { return true }
|
|
|
|
func loginKeychainPath() (string, error) {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to resolve home dir: %w", err)
|
|
}
|
|
return filepath.Join(home, "Library", "Keychains", "login.keychain-db"), nil
|
|
}
|
|
|
|
func installPlatform(certPEM []byte, scope Scope) error {
|
|
path, cleanup, err := writeTempCert(certPEM)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cleanup()
|
|
|
|
var args []string
|
|
runner := commandRunner
|
|
if scope == ScopeSystem {
|
|
args = []string{"add-trusted-cert", "-d", "-r", "trustRoot", "-k", systemKeychainPath, path}
|
|
runner = runElevated
|
|
} else {
|
|
kc, err := loginKeychainPath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
args = []string{"add-trusted-cert", "-r", "trustRoot", "-k", kc, path}
|
|
}
|
|
|
|
if out, err := runner("security", args...); err != nil {
|
|
return fmt.Errorf("security add-trusted-cert failed: %w: %s", err, strings.TrimSpace(string(out)))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func uninstallPlatform(commonName string, scope Scope) error {
|
|
// security delete-certificate removes a single match; loop until none remain
|
|
// so a --force rotation that left two same-CN certs is fully cleaned.
|
|
for i := 0; i < 16; i++ {
|
|
args := []string{"delete-certificate", "-c", commonName}
|
|
runner := commandRunner
|
|
if scope == ScopeSystem {
|
|
// -t clears trust settings; the keychain is a positional argument for
|
|
// delete-certificate (it has no -k flag, unlike add-trusted-cert).
|
|
args = append(args, "-t", systemKeychainPath)
|
|
runner = runElevated
|
|
}
|
|
|
|
out, err := runner("security", args...)
|
|
if err == nil {
|
|
continue
|
|
}
|
|
|
|
// security reports no remaining match with "Unable to delete certificate
|
|
// matching ..." (older/other paths use "Could not find"). Either marks the
|
|
// terminal "nothing left to delete" case, so the loop ends successfully.
|
|
msg := strings.TrimSpace(string(out))
|
|
if strings.Contains(msg, "Unable to delete certificate matching") || strings.Contains(msg, "Could not find") {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("security delete-certificate failed: %w: %s", err, msg)
|
|
}
|
|
|
|
log.Warnf("security delete-certificate did not converge after 16 iterations; trust store cleanup may be incomplete")
|
|
return nil
|
|
}
|
|
|
|
func statusPlatform(commonName string) (bool, bool, error) {
|
|
lkc, err := loginKeychainPath()
|
|
if err != nil {
|
|
return false, certInKeychain(commonName, systemKeychainPath), nil
|
|
}
|
|
return certInKeychain(commonName, lkc), certInKeychain(commonName, systemKeychainPath), nil
|
|
}
|
|
|
|
func certInKeychain(commonName, keychain string) bool {
|
|
args := []string{"find-certificate", "-c", commonName}
|
|
if keychain != "" {
|
|
args = append(args, keychain)
|
|
}
|
|
_, err := commandRunner("security", args...)
|
|
return err == nil
|
|
}
|