#!/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"
