#!/usr/bin/env bash # emudhra-check.sh: read-only check for the eMudhra intermediate CA compromise (Oct 2026) # https://scan.theadminstack.com/emudhra/ # # What it does (Linux and macOS): # * finds the CA bundles on this machine (system, Python certifi, Node, Java cacerts, git) and # tests each one with openssl: does it accept a compromised intermediate via the emSign root, # or via the Sectigo (AAA Certificate Services) cross-sign? # * reports the ca-certificates package and runtime versions (Go, Node, Python, Java, pip) # * looks for TLS certificates issued by eMudhra / emSign in common web-server and ACME paths # * with --online, also tries two of the public test sites with this machine's curl # * with --deep, also sweeps the usual app folders (/opt, /srv, /usr/local, /usr/lib, # /usr/share, /var/lib, /home, /root, /Applications): every CA bundle an app ships (every # Python certifi included), every Java cacerts and its JDK version, Electron apps, and # Go programs, with the Go version each one was built with. It only reads files; it never # runs what it finds. Expect a few minutes. # # It changes nothing, installs nothing, and sends nothing anywhere unless you pass --online. # Copy the output and paste it into the page. Run with sudo to read certificate directories. # # Usage: bash emudhra-check.sh [--deep] [--online] [--no-cert-scan] [--path DIR ...] set -u LC_ALL=C export LC_ALL ONLINE=0; CERTSCAN=1; DEEP=0; EXTRA_PATHS=() while [ $# -gt 0 ]; do case "$1" in --online) ONLINE=1 ;; --deep) DEEP=1 ;; --no-cert-scan) CERTSCAN=0 ;; --path) shift; [ $# -gt 0 ] && EXTRA_PATHS+=("$1") ;; -h|--help) sed -n '2,/^set -u/p' "$0" | sed '$d'; exit 0 ;; esac shift done TMP=$(mktemp -d 2>/dev/null || mktemp -d -t emudhra) trap 'rm -rf "$TMP"' EXIT have() { command -v "$1" >/dev/null 2>&1; } # ---------------------------------------------------------------- test certificates (public) # EM DV TLS CA - G3B issued by emSign Root TLS CA - G3 (SHA-256 42C4ACCD12C4F595…) cat > "$TMP/dv_emsign.pem" <<'PEM' -----BEGIN CERTIFICATE----- MIIDaDCCAu6gAwIBAgIQAKSu5njyM2K0wORgt3LyFDAKBggqhkjOPQQDAzBWMQsw CQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl ZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTU1 NDA4WhcNNDAwNDI4MTgzMDAwWjA/MQswCQYDVQQGEwJOTDETMBEGA1UECgwKZU11 ZGhyYSBCVjEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNCMHYwEAYHKoZIzj0C AQYFK4EEACIDYgAE557ZwEqXc8RzXlERHahawr1eZKs3S4DhLLwgOOweTw47gS/J hZ4eZ6+wTi+LyX90hbplqpEe6KTkW2LVzAPVa+Z4s8OMtNyTfKJGDiOEU8iSk10p 1E+R4VYGtjzO2rRoo4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L WUIgkFQwHQYDVR0OBBYEFOIeL+ccGTkOVUtiwSAkVzUkiiUUMB0GA1UdJQQWMBQG CCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB BAGDjiEBAmQwCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY aHR0cDovL29jc3AtYi5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRFZUTFNDQUczQi5wN2MwNwYDVR0f BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E AwMDaAAwZQIwLmsGXi6oFMJ2klkcEMXMug6gvxQWZWkewiDWVzQyl2L6z6DuQfPO Ibh1grvdULDCAjEAxZvLlxUavG69J6wnSjC8XBbqNIQsJ6CE2QdNOe84Ptxb6VG8 gt2TlfJbHii+KNvK -----END CERTIFICATE----- PEM # EM DV TLS CA - G3B cross-signed by AAA Certificate Services (SHA-256 45A883F349AEDFD5…) cat > "$TMP/dv_aaa.pem" <<'PEM' -----BEGIN CERTIFICATE----- MIIDzjCCAragAwIBAgIRAKAk/fS6Zlx4XOSgIEg4WLcwDQYJKoZIhvcNAQELBQAw ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y ODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW MRswGQYDVQQDDBJFTSBEViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA IgNiAATnntnASpdzxHNeUREdqFrCvV5kqzdLgOEsvCA47B5PDjuBL8mFnh5nr7BO L4vJf3SFumWqkR7opORbYtXMA9Vr5nizw4y03JN8okYOI4RTyJKTXSnUT5HhVga2 PM7atGijggE1MIIBMTAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd BgNVHQ4EFgQU4h4v5xwZOQ5VS2LBICRXNSSKJRQwDgYDVR0PAQH/BAQDAgEGMBIG A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC MDEGA1UdIAQqMCgwCAYGZ4EMAQIBMA0GCysGAQQBg44hAQABMA0GCysGAQQBg44h AQJkMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FB QUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEF BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IB AQBzQ1IoqpdotM6gFrR4Q3YrKifOLeaj5XXunldpVE/vZn2TxC2y0s8oYiLR5ICE wqVXSB7N6BM1Mp+Nif7Jbhb2IswAfSR4Iik8+B0iRmi7lYoNlycCgHjmsjOmZaxM QkPmjXAJbG5f4V41OsvJZmS7lI3r0IuvEDVX3GAZ13on6IImSs9ybNpHQ7NCqkMQ 7wH4uAO1dtpFycgJSdHaCOhHjrox2BTYGLdpUrYwY3EOAjlZ8UGgsxs7mG+xquMl bN1LFURwfnkVMP3T/DBIZ/ys2+lNFyxK3FlArrV4/000uTnVB1lO8oGORYdXoT5V GUyRCEIDhF5TBUuKF4E3kf1h -----END CERTIFICATE----- PEM # emSign Root TLS CA - G3 cross-signed by emSign ECC Root CA - G3 (SHA-256 3094025792039A68…) cat > "$TMP/cross.pem" <<'PEM' -----BEGIN CERTIFICATE----- MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290 IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4 aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290 Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0 1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV -----END CERTIFICATE----- PEM # SHA-256 fingerprints (colon-free, upper case) of the roots that matter FP_ECC=86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B FP_AAA=D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4 FP_TLSG3=7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D # ---------------------------------------------------------------- host echo "#emudhra-check v1 bash" echo "[host]" echo "hostname=$(hostname 2>/dev/null || uname -n)" OS_ID=""; OS_VER="" if [ "$(uname -s)" = "Darwin" ]; then echo "platform=macos"; OS_ID=macos; OS_VER=$(sw_vers -productVersion 2>/dev/null) echo "os_id=macos"; echo "os_version=$OS_VER"; echo "os_name=macOS $OS_VER" else echo "platform=linux" if [ -r /etc/os-release ]; then OS_ID=$(. /etc/os-release; echo "${ID:-}"); OS_VER=$(. /etc/os-release; echo "${VERSION_ID:-}") echo "os_id=$OS_ID"; echo "os_version=$OS_VER"; echo "os_name=$(. /etc/os-release; echo "${PRETTY_NAME:-}")" fi fi echo "elevated=$([ "$(id -u)" = 0 ] && echo 1 || echo 0)" echo "deep=$DEEP" echo "openssl=$(have openssl && openssl version 2>/dev/null | head -1)" if ! have openssl; then echo "# openssl not found: CA bundles can't be tested. Install openssl and re-run." fi # ---------------------------------------------------------------- CA bundles # Lists the subject of every certificate in a PEM bundle (one openssl pass). Presence is only a # hint; the openssl verify tests below are the evidence. subjects() { openssl crl2pkcs7 -nocrl -certfile "$1" 2>/dev/null | openssl pkcs7 -print_certs -noout 2>/dev/null | sed -n 's/^subject=//p' } S_ECC='CN *= *emSign ECC Root CA - G3$' S_AAA='CN *= *AAA Certificate Services$' S_TLSG3='CN *= *emSign Root TLS CA - G3$' SEEN="" check_bundle() { # $1=name $2=path $3=kind local name=$1 path=$2 kind=$3 real [ -r "$path" ] || return 0 real=$(cd "$(dirname "$path")" 2>/dev/null && echo "$(pwd -P)/$(basename "$path")") [ -L "$path" ] && real=$(readlink -f "$path" 2>/dev/null || echo "$real") case " $SEEN " in *" $real "*) return 0 ;; esac SEEN="$SEEN $real" grep -q "BEGIN CERTIFICATE" "$path" 2>/dev/null || return 0 echo "[store]" echo "name=$name"; echo "path=$path"; echo "kind=$kind" if have openssl; then local subs; subs=$(subjects "$path") echo "certs=$(printf '%s\n' "$subs" | grep -c .)" echo "has_ecc_g3=$(printf '%s\n' "$subs" | grep -qE "$S_ECC" && echo 1 || echo 0)" echo "has_aaa=$(printf '%s\n' "$subs" | grep -qE "$S_AAA" && echo 1 || echo 0)" echo "has_root_tls_g3=$(printf '%s\n' "$subs" | grep -qE "$S_TLSG3" && echo 1 || echo 0)" if openssl verify -CAfile "$path" -untrusted "$TMP/cross.pem" "$TMP/dv_emsign.pem" >/dev/null 2>&1; then echo "verify_tls_g3=ok"; else echo "verify_tls_g3=fail"; fi if openssl verify -CAfile "$path" "$TMP/dv_aaa.pem" >/dev/null 2>&1; then echo "verify_aaa=ok"; else echo "verify_aaa=fail"; fi fi } for b in /etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \ /etc/ssl/ca-bundle.pem /var/lib/ca-certificates/ca-bundle.pem /etc/ssl/cert.pem /usr/local/etc/openssl/cert.pem \ /usr/local/etc/openssl@3/cert.pem /opt/homebrew/etc/openssl@3/cert.pem /opt/homebrew/etc/ca-certificates/cert.pem /etc/ca-certificates/extracted/tls-ca-bundle.pem; do check_bundle "system" "$b" "pem-bundle" done if have openssl; then od=$(openssl version -d 2>/dev/null | sed 's/^OPENSSLDIR: "\(.*\)"$/\1/') [ -n "$od" ] && check_bundle "openssl default" "$od/cert.pem" "pem-bundle" fi [ -n "${SSL_CERT_FILE:-}" ] && check_bundle "SSL_CERT_FILE" "$SSL_CERT_FILE" "pem-bundle" [ -n "${CURL_CA_BUNDLE:-}" ] && check_bundle "CURL_CA_BUNDLE" "$CURL_CA_BUNDLE" "pem-bundle" [ -n "${REQUESTS_CA_BUNDLE:-}" ] && check_bundle "REQUESTS_CA_BUNDLE" "$REQUESTS_CA_BUNDLE" "pem-bundle" [ -n "${NODE_EXTRA_CA_CERTS:-}" ] && check_bundle "NODE_EXTRA_CA_CERTS" "$NODE_EXTRA_CA_CERTS" "pem-bundle" if have git; then gb=$(git config --global http.sslCAInfo 2>/dev/null || git config --system http.sslCAInfo 2>/dev/null) [ -n "$gb" ] && check_bundle "git http.sslCAInfo" "$gb" "pem-bundle" fi # ---------------------------------------------------------------- package if have dpkg-query; then v=$(dpkg-query -W -f='${Version}' ca-certificates 2>/dev/null) [ -n "$v" ] && { echo "[package]"; echo "key=$([ "$OS_ID" = ubuntu ] && echo ubuntu || echo debian)"; echo "name=ca-certificates"; echo "version=$v"; } elif have rpm; then v=$(rpm -q --qf '%{VERSION}-%{RELEASE}' ca-certificates 2>/dev/null) case "$OS_ID" in fedora) k=fedora ;; *) k=rhel ;; esac [ -n "$v" ] && case "$v" in *"not installed"*) ;; *) echo "[package]"; echo "key=$k"; echo "name=ca-certificates"; echo "version=$v" ;; esac elif have apk; then v=$(apk info -v ca-certificates 2>/dev/null | head -1 | sed 's/^ca-certificates-//') [ -n "$v" ] && { echo "[package]"; echo "key=alpine"; echo "name=ca-certificates"; echo "version=$v"; } elif have pacman; then v=$(pacman -Q ca-certificates-mozilla 2>/dev/null | awk '{print $2}') [ -n "$v" ] && { echo "[package]"; echo "key=arch"; echo "name=ca-certificates-mozilla"; echo "version=$v"; } fi # ---------------------------------------------------------------- runtimes runtime() { echo "[runtime]"; echo "key=$1"; echo "version=$2"; echo "path=$3"; [ -n "${4:-}" ] && echo "store=$4"; } if have go; then v=$(go version 2>/dev/null | awk '{print $3}' | sed 's/^go//'); runtime go "$v" "$(command -v go)" "embedded"; fi if have node; then v=$(node -v 2>/dev/null | sed 's/^v//') uses=$(node -p "process.execArgv.concat((process.env.NODE_OPTIONS||'').split(' ')).some(a=>a.includes('use-openssl-ca')||a.includes('use-system-ca'))?'system':'bundled'" 2>/dev/null) runtime node "$v" "$(command -v node)" "${uses:-bundled}" hit=$(node -e " const {X509Certificate}=require('crypto');const tls=require('tls'); const want={ecc:'$FP_ECC',aaa:'$FP_AAA',tls:'$FP_TLSG3'};const got={ecc:0,aaa:0,tls:0}; for(const p of tls.rootCertificates){try{const f=new X509Certificate(p).fingerprint256.replace(/:/g,'');for(const k in want)if(f===want[k])got[k]=1;}catch{}} console.log(got.ecc+' '+got.aaa+' '+got.tls+' '+tls.rootCertificates.length)" 2>/dev/null) if [ -n "$hit" ]; then set -- $hit echo "[store]"; echo "name=Node.js built-in roots ($v)"; echo "path=$(command -v node)"; echo "kind=node"; echo "certs=$4" echo "has_ecc_g3=$1"; echo "has_aaa=$2"; echo "has_root_tls_g3=$3" fi fi for py in python3 python; do have "$py" || continue pv=$("$py" -c 'import sys;print("%d.%d.%d"%sys.version_info[:3])' 2>/dev/null) || continue runtime python "$pv" "$(command -v "$py")" cf=$("$py" -c 'import certifi;print(certifi.__version__);print(certifi.where())' 2>/dev/null) if [ -n "$cf" ]; then cv=$(printf '%s\n' "$cf" | sed -n 1p); cw=$(printf '%s\n' "$cf" | sed -n 2p) runtime certifi "$cv" "$cw" check_bundle "Python certifi $cv" "$cw" "certifi" fi pipv=$("$py" -m pip --version 2>/dev/null | awk '{print $2}') [ -n "$pipv" ] && runtime pip "$pipv" "$py -m pip" break done if have java; then jv=$(java -version 2>&1 | sed -n 's/.*version "\([^"]*\)".*/\1/p' | head -1) runtime java "$jv" "$(command -v java)" if have keytool; then ks=$(keytool -list -cacerts -storepass changeit 2>/dev/null) || ks="" if [ -n "$ks" ]; then fl=$(printf '%s\n' "$ks" | sed -n 's/.*SHA-256): *//p; s/.*SHA256: *//p' | tr -d ':' | tr 'a-f' 'A-F') echo "[store]"; echo "name=Java cacerts ($jv)"; echo "path=keytool -cacerts"; echo "kind=java-keystore" echo "has_ecc_g3=$(printf '%s\n' "$fl" | grep -qx "$FP_ECC" && echo 1 || echo 0)" echo "has_aaa=$(printf '%s\n' "$fl" | grep -qx "$FP_AAA" && echo 1 || echo 0)" echo "has_root_tls_g3=$(printf '%s\n' "$fl" | grep -qx "$FP_TLSG3" && echo 1 || echo 0)" fi fi fi for t in uv conda; do have "$t" && runtime "$t" "$("$t" --version 2>/dev/null | grep -oE '[0-9]+(\.[0-9]+)+' | head -1)" "$(command -v "$t")" done have bun && runtime bun "$(bun --version 2>/dev/null)" "$(command -v bun)" have deno && runtime deno "$(deno --version 2>/dev/null | head -1 | awk '{print $2}')" "$(command -v deno)" # ---------------------------------------------------------------- deep sweep of app folders (--deep) if [ "$DEEP" = 1 ]; then roots=() for r in /opt /srv /usr/local /usr/lib /usr/share /var/lib /home /root /Applications "/Library/Application Support" "${EXTRA_PATHS[@]}"; do [ -d "$r" ] && roots+=("$r") done # Skip pseudo filesystems, container layers and VCS metadata. sweep() { # extra find predicates follow find "${roots[@]}" \( -path /proc -o -path /sys -o -path /var/lib/docker -o -path /var/lib/containerd \ -o -path /var/lib/containers -o -path /var/lib/snapd -o -path '*/.git' -o -path '*/.cache' \) -prune -o "$@" 2>/dev/null } # 1. CA bundles shipped by apps (curl/Mozilla-style PEM bundles with 20+ certificates) nb=0 while IFS= read -r f; do c=$(grep -c "BEGIN CERTIFICATE" "$f" 2>/dev/null); [ "${c:-0}" -ge 20 ] || continue nb=$((nb+1)); [ $nb -gt 500 ] && { echo "#deep_bundle_cap=500"; break; } case "$f" in */certifi/cacert.pem) cv=$(sed -n 's/^__version__ *= *["'\'']\([^"'\'']*\)["'\''].*/\1/p' "$(dirname "$f")/__init__.py" 2>/dev/null | head -1) check_bundle "Python certifi ${cv:-?} (${f%/certifi/cacert.pem})" "$f" "certifi" ;; */pip/_vendor/certifi/cacert.pem) check_bundle "pip's vendored certifi (${f%/pip/_vendor/*})" "$f" "certifi" ;; *) check_bundle "App bundle: $(printf '%s' "$f" | cut -d/ -f2-4)" "$f" "app-bundle" ;; esac done < <(sweep -type f \( -name 'cacert*.pem' -o -name 'ca-bundle*.crt' -o -name 'ca-bundle*.pem' -o -name 'ca-certificates.crt' \ -o -name 'cert.pem' -o -name 'tls-ca-bundle.pem' -o -name 'roots.pem' -o -name 'curl-ca-bundle.crt' -o -name 'trusted*.pem' \ -o \( -name '*.pem' -size +60k -size -4M \) -o \( -name '*.crt' -size +60k -size -4M \) \) -print) echo "#deep_bundles_tested=$nb" # 2. Every Java runtime's cacerts, with that runtime's version nj=0; JSEEN="" while IFS= read -r ks; do jh=${ks%/lib/security/cacerts}; jh=${jh%/jre} jreal=$(readlink -f "$jh" 2>/dev/null || echo "$jh") case " $JSEEN " in *" $jreal "*) continue ;; esac # same JDK reached through a symlink JSEEN="$JSEEN $jreal" nj=$((nj+1)); [ $nj -gt 50 ] && break jv=$(sed -n 's/^JAVA_VERSION="\(.*\)"/\1/p' "$jh/release" 2>/dev/null | head -1) [ -n "$jv" ] && runtime java "$jv" "$jh" real=$(readlink -f "$ks" 2>/dev/null || echo "$ks") case " $SEEN " in *" $real "*) continue ;; esac # cacerts shared with a store already tested SEEN="$SEEN $real" kt=$jh/bin/keytool; [ -x "$kt" ] || kt=$(command -v keytool 2>/dev/null) if [ -n "$kt" ]; then fl=$("$kt" -list -keystore "$ks" -storepass changeit 2>/dev/null | sed -n 's/.*SHA-256): *//p; s/.*SHA256: *//p' | tr -d ':' | tr 'a-f' 'A-F') if [ -n "$fl" ]; then echo "[store]"; echo "name=Java cacerts ${jv:+($jv) }$jh"; echo "path=$ks"; echo "kind=java-keystore" echo "has_ecc_g3=$(printf '%s\n' "$fl" | grep -qx "$FP_ECC" && echo 1 || echo 0)" echo "has_aaa=$(printf '%s\n' "$fl" | grep -qx "$FP_AAA" && echo 1 || echo 0)" echo "has_root_tls_g3=$(printf '%s\n' "$fl" | grep -qx "$FP_TLSG3" && echo 1 || echo 0)" fi fi done < <(sweep \( -type f -o -type l \) -name cacerts -path '*/lib/security/*' -print) echo "#deep_java_runtimes=$nj" # 3. Electron apps (their Chromium network stack ships its own root list) ne=0 while IFS= read -r asar; do d=${asar%/resources/app.asar} v=$(head -c 32 "$d/version" 2>/dev/null | tr -d ' \r\n') [ -n "$v" ] || continue ne=$((ne+1)); [ $ne -gt 200 ] && break runtime electron "$v" "$d" done < <(sweep -type f -path '*/resources/app.asar' -print) echo "#deep_electron_apps=$ne" # 4. Go programs: the fix is compiled in, so each binary carries the Go version it was built with. # Every Go binary carries a "Go buildinf:" block; grep stops at the first match. ng=0 goroot=$(have go && go env GOROOT 2>/dev/null) while IFS= read -r b; do case "$b" in */pkg/tool/*|*/bin/gofmt|"$goroot"/bin/*) continue ;; esac # Go toolchains themselves [ -n "$goroot" ] && case "$b" in "$goroot"/*) continue ;; esac LC_ALL=C grep -a -q -F 'Go buildinf:' "$b" 2>/dev/null || continue if have go; then gv=$(go version "$b" 2>/dev/null | awk '{print $2}' | sed 's/^go//') else gv=$(LC_ALL=C grep -a -o -m1 -E 'Go buildinf:[^g]{0,40}go1\.[0-9]+(\.[0-9]+)?' "$b" 2>/dev/null | grep -o -E 'go1\.[0-9]+(\.[0-9]+)?' | head -1 | sed 's/^go//'); fi [ -n "$gv" ] || continue ng=$((ng+1)); [ $ng -gt 300 ] && { echo "#deep_go_cap=300"; break; } echo "[runtime]"; echo "key=go"; echo "version=$gv"; echo "path=$b"; echo "store=embedded"; echo "kind=binary" done < <(find "${roots[@]}" /usr/bin /usr/sbin /usr/libexec \( -path /proc -o -path /sys -o -path /var/lib/docker -o -path /var/lib/containerd \ -o -path /var/lib/containers -o -path /var/lib/snapd -o -path '*/.git' \) -prune -o -type f -perm -u+x -size +1M -size -400M -print 2>/dev/null | sort -u) echo "#deep_go_binaries=$ng" fi # ---------------------------------------------------------------- certificates this machine serves if [ "$CERTSCAN" = 1 ] && have openssl; then dirs=(/etc/nginx /etc/apache2 /etc/httpd /etc/letsencrypt/live /etc/ssl/private /etc/ssl/certs/local /etc/pki/tls/certs /etc/haproxy /etc/lighttpd /etc/caddy /var/lib/caddy /usr/local/etc/nginx /opt/homebrew/etc/nginx /etc/postfix /etc/dovecot "${EXTRA_PATHS[@]}") n=0 for d in "${dirs[@]}"; do [ -d "$d" ] || continue while IFS= read -r f; do [ -r "$f" ] || continue grep -q "BEGIN CERTIFICATE" "$f" 2>/dev/null || continue n=$((n+1)); [ $n -gt 2000 ] && break iss=$(openssl x509 -in "$f" -noout -issuer -nameopt RFC2253 2>/dev/null | sed 's/^issuer=//') case "$iss" in *eMudhra*|*emSign*|*"InCommon Intermediate CA - "?"VG3C"*|*"InCommon Intermediate CA - EVG3C"*) echo "[servercert]" echo "location=$f" echo "subject=$(openssl x509 -in "$f" -noout -subject -nameopt RFC2253 2>/dev/null | sed 's/^subject=//')" echo "issuer=$iss" echo "aki=$(openssl x509 -in "$f" -noout -ext authorityKeyIdentifier 2>/dev/null | tail -1 | sed 's/keyid://; s/[ :]//g')" echo "not_after=$(openssl x509 -in "$f" -noout -enddate 2>/dev/null | cut -d= -f2)" echo "sha256=$(openssl x509 -in "$f" -noout -fingerprint -sha256 2>/dev/null | sed 's/.*=//; s/://g')" ;; esac done < <(find -L "$d" -maxdepth 4 -type f \( -name '*.pem' -o -name '*.crt' -o -name '*.cer' -o -name 'fullchain*' -o -name 'cert*' \) 2>/dev/null) done echo "#certificate_files_scanned=$n" fi # ---------------------------------------------------------------- optional live test if [ "$ONLINE" = 1 ] && have curl; then for h in dv-g3b-emudhra.we-have-a-problem.com dv-g3b-aaa-emudhra.we-have-a-problem.com dv-g3b-noct-emudhra.we-have-a-problem.com; do body=$(curl -sS --max-time 12 "https://$h/" 2>"$TMP/err"); rc=$? echo "[probe]"; echo "host=$h"; echo "client=curl" if [ $rc -eq 0 ] && printf '%s' "$body" | grep -q "successfully connected"; then echo "result=connected" elif [ $rc -eq 60 ] || [ $rc -eq 35 ] || [ $rc -eq 51 ] || [ $rc -eq 77 ]; then echo "result=blocked" else echo "result=error"; echo "detail=curl exit $rc"; fi done fi echo "#end"