Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Changed

- Root privilege handling is improved
- Verification of Root CA certificate is more reliable

## 0.2.0 2026-01-18

### Added
Expand Down
63 changes: 47 additions & 16 deletions internal/ca/trust_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@ package ca
import (
"bytes"
"fmt"
"os"
"os/exec"
"strings"
)

// isRoot returns true if the current process is running as root.
func isRoot() bool {
return os.Geteuid() == 0
}

// InstallTrust adds the CA certificate to the macOS System Keychain.
// This requires sudo/admin privileges.
func InstallTrust() error {
Expand All @@ -29,12 +35,23 @@ func InstallTrust() error {
// -d: add to admin cert store
// -r trustRoot: trust as root certificate
// -k: keychain to add to
cmd := exec.Command("sudo", "security", "add-trusted-cert",
"-d",
"-r", "trustRoot",
"-k", "/Library/Keychains/System.keychain",
certPath,
)
var cmd *exec.Cmd
if isRoot() {
// Already running as root, no need for sudo
cmd = exec.Command("security", "add-trusted-cert",
"-d",
"-r", "trustRoot",
"-k", "/Library/Keychains/System.keychain",
certPath,
)
} else {
cmd = exec.Command("sudo", "security", "add-trusted-cert",
"-d",
"-r", "trustRoot",
"-k", "/Library/Keychains/System.keychain",
certPath,
)
}

var stderr bytes.Buffer
cmd.Stderr = &stderr
Expand All @@ -50,11 +67,19 @@ func InstallTrust() error {
// This requires sudo/admin privileges.
func UninstallTrust() error {
// Find and delete the certificate by name
// First, we need to find the SHA-1 hash of our certificate
cmd := exec.Command("sudo", "security", "delete-certificate",
"-c", caCommonName,
"/Library/Keychains/System.keychain",
)
var cmd *exec.Cmd
if isRoot() {
// Already running as root, no need for sudo
cmd = exec.Command("security", "delete-certificate",
"-c", caCommonName,
"/Library/Keychains/System.keychain",
)
} else {
cmd = exec.Command("sudo", "security", "delete-certificate",
"-c", caCommonName,
"/Library/Keychains/System.keychain",
)
}

var stderr bytes.Buffer
cmd.Stderr = &stderr
Expand All @@ -77,13 +102,19 @@ func IsTrusted() bool {
return false
}

// Use security verify-cert to check if the CA is actually trusted
// This verifies the trust chain, not just presence in keychain
cmd := exec.Command("security", "verify-cert",
"-c", CertPath(),
// Check if the certificate exists in the System Keychain
// This is the correct way to verify installation, not verify-cert
// (verify-cert returns success for self-signed CAs even if not installed)
cmd := exec.Command("security", "find-certificate",
"-c", caCommonName,
"/Library/Keychains/System.keychain",
)

// verify-cert returns 0 if trusted, non-zero otherwise
// Suppress output - we only care about the exit code
cmd.Stdout = nil
cmd.Stderr = nil

// find-certificate returns 0 if found, non-zero otherwise
return cmd.Run() == nil
}

Expand Down