#!/bin/bash # CRL (Certificate Revocation List) Update Script for SAW # Version: 1.0 # Date: 2026-04-02 # # This script downloads, verifies, and installs the Certificate Revocation List (CRL) # from your Certificate Authority (CA) server. It ensures that packages signed with # revoked certificates are not installed. # # Usage: sudo ./verify-crl.sh [--download] [--verify] [--update] [--status] # # SECURITY RATIONALE: # CRL checking is critical for security because it: # 1. Detects compromised CA keys # 2. Revokes access for compromised certificates # 3. Prevents installation of packages signed with revoked keys # 4. Provides a mechanism to respond to security incidents # 5. Meets compliance requirements (PCI-DSS, HIPAA, etc.) # # How CRL works: # 1. CA maintains a list of revoked certificates # 2. CRL is signed by CA to prevent tampering # 3. System downloads CRL regularly # 4. Before installing package, checks if certificate is revoked # 5. If revoked, installation is blocked # # Reference: https://www.openssl.org/docs/man1.1.1/man1/crl.html set -e # Configuration CRL_URL="https://your-ca-server.com/crl.pem" CRL_FILE="/etc/pki/ca-trust/source/anchors/crl.pem" CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca" CRL_CACHE_FILE="/var/cache/crl/crl.pem" LOG_FILE="/var/log/crl-update.log" CRL_UPDATE_SCRIPT="/usr/local/bin/daily-crl-update.sh" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Print functions log_info() { local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${GREEN}[$timestamp]${NC} ${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE" } log_warning() { local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${YELLOW}[$timestamp]${NC} ${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE" } log_error() { local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${RED}[$timestamp]${NC} ${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE" } # Check if running as root check_root() { if [ "$EUID" -ne 0 ]; then log_error "This script must be run as root" exit 1 fi } # Download CRL from CA server download_crl() { log_info "Downloading CRL from: $CRL_URL" # Check if URL is configured if [ "$CRL_URL" = "https://your-ca-server.com/crl.pem" ]; then log_error "CRL URL not configured" log_info "Edit the script and set CRL_URL to your CA server" exit 1 fi # Create cache directory mkdir -p /var/cache/crl # Download CRL if ! curl -s -o "$CRL_CACHE_FILE" "$CRL_URL"; then log_error "Failed to download CRL" exit 1 fi log_info "CRL downloaded to: $CRL_CACHE_FILE" # Verify download was successful if [ ! -f "$CRL_CACHE_FILE" ]; then log_error "CRL file not created" exit 1 fi if [ ! -s "$CRL_CACHE_FILE" ]; then log_error "CRL file is empty" exit 1 fi log_info "CRL file size: $(du -h "$CRL_CACHE_FILE" | cut -f1)" } # Verify CRL signature # # SECURITY RATIONALE: # Verifying the CRL signature ensures: # 1. CRL hasn't been tampered with during download # 2. CRL comes from your trusted CA # 3. CRL is authentic and hasn't been replaced by attacker # 4. CRL is valid and hasn't expired # # The verification process: # 1. Check if CRL is properly formatted # 2. Verify CRL signature using CA public key # 3. Check CRL validity period # 4. Ensure CRL hasn't expired verify_crl() { log_info "Verifying CRL signature..." # Check if CA key exists if [ ! -f "$CA_KEY_PATH" ]; then log_error "CA key not found: $CA_KEY_PATH" exit 1 fi # Check if CRL exists if [ ! -f "$CRL_CACHE_FILE" ]; then log_error "CRL not found: $CRL_CACHE_FILE" log_info "Run with --download first" exit 1 fi # Verify CRL format if ! openssl crl -in "$CRL_CACHE_FILE" -noout 2>/dev/null; then log_error "CRL format is invalid" exit 1 fi # Verify CRL signature if ! openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then log_error "CRL signature verification failed" exit 1 fi # Get CRL information local crl_info=$(openssl crl -in "$CRL_CACHE_FILE" -noout -text 2>/dev/null) # Extract CRL dates local this_update=$(echo "$crl_info" | grep "Last Update:" | head -1) local next_update=$(echo "$crl_info" | grep "Next Update:" | head -1) log_info "CRL Last Update: $this_update" log_info "CRL Next Update: $next_update" # Check if CRL is expired local next_update_date=$(echo "$next_update" | awk '{print $4, $5, $6, $7, $8, $9}') local current_date=$(date) if [[ "$current_date" > "$next_update_date" ]]; then log_warning "CRL has expired!" log_warning "CRL should be updated immediately" else log_info "CRL is valid" fi log_info "CRL signature verified successfully" } # Update CA trust with CRL update_ca_trust() { log_info "Updating CA trust database..." # Copy CRL to CA trust source mkdir -p /etc/pki/ca-trust/source/anchors cp "$CRL_CACHE_FILE" /etc/pki/ca-trust/source/anchors/crl.pem # Update CA trust update-ca-trust extract log_info "CA trust database updated" } # Check revoked certificates check_revoked() { log_info "Checking for revoked certificates..." # Check if CRL exists if [ ! -f "$CRL_FILE" ]; then log_warning "CRL not found: $CRL_FILE" log_info "Run with --download and --verify first" return 1 fi # Check if CA key exists if [ ! -f "$CA_KEY_PATH" ]; then log_error "CA key not found: $CA_KEY_PATH" exit 1 fi # Get list of installed packages with signatures local revoked_count=0 local total_count=0 log_info "Scanning installed packages..." # Get all installed packages local packages=$(rpm -qa --queryformat='%{NAME} %{SIGPGP:pgpsig}\n' 2>/dev/null | head -100) for pkg in $packages; do # Extract certificate ID from signature local cert_id=$(echo "$pkg" | awk '{print $2}' | cut -d: -f2) if [ -n "$cert_id" ]; then # Check if certificate is in CRL if openssl crl -in "$CRL_FILE" -noout -text 2>/dev/null | grep -q "$cert_id"; then log_error "REVOKED: Package signed with revoked certificate: $cert_id" ((revoked_count++)) fi fi ((total_count++)) done echo "" echo "=== Revocation Check Summary ===" echo "Total packages checked: $total_count" echo "Revoked certificates found: $revoked_count" echo "" if [ $revoked_count -gt 0 ]; then log_error "WARNING: $revoked_count packages have revoked certificates!" log_info "These packages should be removed and reinstalled" return 1 fi log_info "No revoked certificates found" return 0 } # Display CRL status show_status() { echo "" echo "=== CRL Status ===" echo "" # Check if CRL exists if [ -f "$CRL_CACHE_FILE" ]; then echo "CRL Cache File: $CRL_CACHE_FILE" echo "Size: $(du -h "$CRL_CACHE_FILE" | cut -f1)" echo "Modified: $(stat -c %y "$CRL_CACHE_FILE")" echo "" # Show CRL information if openssl crl -in "$CRL_CACHE_FILE" -noout -text 2>/dev/null | head -10; then echo "" fi # Check if valid if openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then echo "Status: VALID" echo "Signed by: $CA_KEY_PATH" else echo "Status: INVALID" fi else echo "CRL Cache File: NOT FOUND" echo "Status: CRL has not been downloaded" fi echo "" echo "CA Key: $CA_KEY_PATH" if [ -f "$CA_KEY_PATH" ]; then echo "Status: FOUND" else echo "Status: NOT FOUND" fi echo "" } # Create daily CRL update script create_daily_script() { log_info "Creating daily CRL update script..." cat > "$CRL_UPDATE_SCRIPT" << 'DAILYSCHEDULE' #!/bin/bash # Daily CRL update script for SAW # This script is run by cron to keep CRL up to date CRL_URL="https://your-ca-server.com/crl.pem" CRL_CACHE_FILE="/var/cache/crl/crl.pem" CA_KEY_PATH="/etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca" LOG_FILE="/var/log/crl-update.log" # Function to log log_info() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" } log_error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" >> "$LOG_FILE" } log_warning() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $1" >> "$LOG_FILE" } log_info "Starting CRL update" # Check if CRL URL is configured if [ "$CRL_URL" = "https://your-ca-server.com/crl.pem" ]; then log_error "CRL URL not configured" exit 1 fi # Download CRL if ! curl -s -o "$CRL_CACHE_FILE" "$CRL_URL"; then log_error "Failed to download CRL" exit 1 fi log_info "CRL downloaded successfully" # Verify CRL if ! openssl crl -in "$CRL_CACHE_FILE" -CAfile "$CA_KEY_PATH" -noout 2>/dev/null; then log_error "CRL verification failed" exit 1 fi log_info "CRL verified successfully" # Update CA trust update-ca-trust extract log_info "CRL update completed successfully" DAILYSCHEDULE chmod +x "$CRL_UPDATE_SCRIPT" log_info "Daily update script created: $CRL_UPDATE_SCRIPT" } # Set up cron job for daily updates setup_cron() { log_info "Setting up cron job for daily CRL updates..." # Create cron job cat > /etc/cron.d/saw-crl-update << 'CRONJOB' # Daily CRL update for SAW # Run at 2:00 AM 0 2 * * * root /usr/local/bin/daily-crl-update.sh >> /var/log/crl-update.log 2>&1 CRONJOB chmod 644 /etc/cron.d/saw-crl-update log_info "Cron job created" log_info "CRL will be updated daily at 2:00 AM" } # Display help show_help() { cat << 'HELP' CRL Update Script for SAW ========================= Usage: ./verify-crl.sh [OPTIONS] Options: --download Download CRL from CA server --verify Verify CRL signature --update Update CA trust database --check Check for revoked certificates --status Show CRL status --cron Set up daily cron job --help Show this help message Examples: # Download and verify CRL ./verify-crl.sh --download --verify --update # Check status ./verify-crl.sh --status # Check for revoked certificates ./verify-crl.sh --check Security Notes: - CRL should be updated daily - CRL is signed by CA to prevent tampering - Packages signed with revoked certificates will be blocked - CRL expiration should be monitored Reference: https://www.openssl.org/docs/man1.1.1/man1/crl.html HELP } # Main execution main() { echo "" echo "==========================================" echo " CRL Update Script" echo " Fedora Kinoite SAW" echo "==========================================" echo "" check_root case "${1:-}" in --download) download_crl ;; --verify) check_root verify_crl ;; --update) check_root verify_crl update_ca_trust ;; --check) check_root check_revoked ;; --status) show_status ;; --cron) check_root create_daily_script setup_cron ;; --help|-h) show_help ;; *) show_help ;; esac } # Run main function main "$@"