Files
homebrew-tap-ygg/Formula/ca-certificates.rb
T

178 lines
6.4 KiB
Ruby

class CaCertificates < Formula
desc "Mozilla CA certificate store"
homepage "https://curl.se/docs/caextract.html"
url "https://curl.se/ca/cacert-2026-07-16.pem"
sha256 "3ff344e30b9b1ed2971044eabb438a08f2e2245ddb5f8ab1a3ad8b63ab4eaf91"
license "MPL-2.0"
compatibility_version 1
livecheck do
url :homepage
regex(/href=.*?cacert[._-](\d{4}-\d{2}-\d{2})\.pem/i)
end
def install
pkgshare.install "cacert-#{version}.pem" => "cacert.pem"
post_install = libexec/"post-install"
post_install.write <<~'BASH'
#!/bin/bash
# Build a CA bundle from trusted system and Mozilla certificates.
set -euo pipefail
shopt -s nullglob
if [[ "$#" -ne 2 ]]; then
echo "usage: post-install SOURCE DESTINATION" >&2
exit 1
fi
source_file="$1"
destination="$2"
destination_dir="${destination%/*}"
mkdir -p "$destination_dir"
# Build beside the destination so the final rename cannot expose a partial bundle.
work_dir="$(mktemp -d "${destination_dir}/.ca-certificates.XXXXXX")"
trap 'rm -rf "$work_dir"' EXIT
fingerprints_file="$work_dir/fingerprints"
bundle_file="$work_dir/cert.pem"
: >"$fingerprints_file"
: >"$bundle_file"
# Split concatenated PEM bundles so each certificate can be validated independently.
split_certificates() {
local input_file="$1"
local output_dir="$2"
mkdir -p "$output_dir"
awk -v output_dir="$output_dir" '
/-----BEGIN CERTIFICATE-----/ {
certificate = ""
writing_certificate = 1
}
writing_certificate {
certificate = certificate $0 ORS
}
/-----END CERTIFICATE-----/ && writing_certificate {
output_file = output_dir "/" ++certificate_count ".pem"
printf "%s", certificate > output_file
close(output_file)
writing_certificate = 0
}
' "$input_file"
}
# Track fingerprints while appending to discard malformed and duplicate certificates.
append_unique_certificate() {
local certificate_file="$1"
local openssl_command="$2"
local fingerprint
fingerprint="$("$openssl_command" x509 -inform pem -fingerprint -sha256 -noout \
<"$certificate_file" 2>/dev/null)" || return 0
grep -Fqx "$fingerprint" "$fingerprints_file" && return
printf '%s\n' "$fingerprint" >>"$fingerprints_file"
cat "$certificate_file" >>"$bundle_file"
printf '\n' >>"$bundle_file"
}
# Append every valid, unique certificate from a PEM bundle.
append_bundle() {
local input_file="$1"
local output_dir="$2"
local openssl_command="$3"
local certificate_file
split_certificates "$input_file" "$output_dir"
for certificate_file in "$output_dir"/*.pem; do
append_unique_certificate "$certificate_file" "$openssl_command"
done
}
# Include only unexpired keychain certificates trusted for server TLS.
append_keychain() {
local keychain="$1"
local purpose="$2"
local output_dir="$work_dir/keychain-$purpose"
local certificate_file
/usr/bin/security find-certificate -a -p "$keychain" >"$work_dir/keychain.pem"
split_certificates "$work_dir/keychain.pem" "$output_dir"
for certificate_file in "$output_dir"/*.pem; do
/usr/bin/openssl x509 -inform pem -checkend 0 -noout <"$certificate_file" &>/dev/null || continue
/usr/bin/openssl x509 -inform pem -purpose -noout <"$certificate_file" 2>/dev/null |
grep -Fq "SSL server CA : Yes" || continue
/usr/bin/security verify-cert -l -L -c "$certificate_file" -p "$purpose" -R offline &>/dev/null ||
continue
append_unique_certificate "$certificate_file" /usr/bin/openssl
done
}
if [[ "$(uname -s)" == "Darwin" ]]; then
# Begin with system trust, then append Mozilla certificates not already present.
append_keychain "/Library/Keychains/System.keychain" ssl
append_keychain "/System/Library/Keychains/SystemRootCertificates.keychain" basic
append_bundle "$source_file" "$work_dir/mozilla" /usr/bin/openssl
else
# Merge the first standard system CA bundle found with the Mozilla bundle.
system_bundle=""
for candidate in \
/etc/ssl/certs/ca-certificates.crt \
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \
/etc/ssl/ca-bundle.pem; do
if [[ -f "$candidate" && -r "$candidate" ]]; then
system_bundle="$candidate"
break
fi
done
openssl_command="$(command -v openssl || true)"
if [[ -n "$system_bundle" && -n "$openssl_command" ]]; then
append_bundle "$system_bundle" "$work_dir/system" "$openssl_command"
append_bundle "$source_file" "$work_dir/mozilla" "$openssl_command"
else
echo "Cannot find a readable system CA bundle or OpenSSL; using Mozilla certificates only." >&2
output_dir="$work_dir/mozilla"
split_certificates "$source_file" "$output_dir"
for certificate_file in "$output_dir"/*.pem; do
cat "$certificate_file" >>"$bundle_file"
printf '\n' >>"$bundle_file"
done
fi
fi
chmod 0644 "$bundle_file"
mv -f "$bundle_file" "$destination"
BASH
chmod 0755, post_install
end
post_install_steps do
run "post-install", args: ["{{pkgshare}}/cacert.pem", "{{pkgetc}}/cert.pem"], base: :libexec
end
def caveats
on_macos do
<<~EOS
CA certificates have been bootstrapped using certificates from the system keychain.
EOS
end
on_linux do
<<~EOS
CA certificates have been bootstrapped from both the Mozilla CA store and the system CA store at
one of the following locations, depending on your distro:
/etc/ssl/certs/ca-certificates.crt # Debian/Ubuntu, Alpine Linux, Arch Linux
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem # RHEL/CentOS/Fedora, Amazon Linux
/etc/ssl/ca-bundle.pem # SUSE/openSUSE
EOS
end
end
test do
assert_path_exists pkgshare/"cacert.pem"
assert_path_exists pkgetc/"cert.pem"
assert_match "-----BEGIN CERTIFICATE-----", (pkgetc/"cert.pem").read
end
end