#!/bin/bash # trivial check if server cert is OK incl. best effort to download # referenced certificates and CRLs in chain # # jpokorny@redhat.com # # TODO: # - currently, only cl[tl] files supported, not immediate PEM etc.; # also any reference to external resource has to start with URI # (is it a convention or a single case?) # - couldn't get rid of dependency on temporary file as it is read # twice in two substituted commands and neither env. variable nor # file descriptor sharing is suitable (stdin can be read only once, # generally, there is a race between the two?) # - wget vs. certificates? switch to curl? set -u set +e CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt HOMEBUNDLE=~/.pki/tls/certs/ca-bundle.crt #WGET="wget -nv --ca-certificate <(cat "${CA_BUNDLE}" "${HOMEBUNDLE}")" WGET="wget -nv" guess_inform() { case "{1##*.}" in crt) echo DER;; pem|*) echo PEM;; esac } cert_pick_file() { echo "Trying file" >&2 local inform=$(guess_inform "$1") [ -f "$1" ] && openssl x509 -inform "${inform}" -in "$1" } # when CA cert is hosted on https server signed by this very CA cert_pick_url_selfsigned() { [[ "$1" =~ https://.* ]] || return 1 echo "Trying self-signed" >&2 local ret= local start=${1##https://} local host=${start%%/*} local machine=${host%%:*} local port=${host#*:} [ "${port}" = "" ] && port=443 local cont=${start#*/} local inform=$(guess_inform "${cont}") ( echo -e "GET /${cont} /HTTP 1.0\n"; sleep 2 ) \ | openssl s_client -connect "${machine}:${port}" -crlf 2>/dev/null \ | sed -ne '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \ | ( local tmpfile=$(mktemp /tmp/.XXXXXX) cat >${tmpfile} openssl verify -CAfile \ <(awk '/-BEGIN CERTIFICATE-/{if(++i > 2){print; exit;}}{if(i == 2){print;}}' ${tmpfile} \ | cat "${CA_BUNDLE}" "${HOMEBUNDLE}" -) \ <(awk '/-BEGIN CERTIFICATE-/{if(++i > 2){exit;}}{if(i == 1){print;}}' ${tmpfile}) >&2; ret=$? [ $ret -eq 0 ] \ && openssl x509 -inform "${inform}" -in ${tmpfile} rm -- ${tmpfile} return $ret ) } cert_pick_url() { echo "Trying URL.." >&2 local inform=$(guess_inform "$1") (if ! ${WGET} "$1" -O- && [[ "$1" =~ https://.* ]]; then local start=${1##https://} local host=${start%%/*} local machine=${host%%:*} local port=${host#*:} [ "${port}" = "${machine}" ] && port=443 ( echo ">>> recursion" >&2 main "${machine}" "${port}" \ || main -nocrl "${machine}" "${port}" echo "<<< recursion" >&2 ) >&2 \ && ${WGET} --no-check-certificate "$1" -O- fi) | openssl x509 -inform "${inform}" } cert_pick_from_server() { echo "Trying from server.." >&2 local server=$1 local port=443 # https [ $# -ge 2 ] && port=$2 ( echo; sleep 2 ) \ | openssl s_client -connect "${server}:${port}" -crlf 2>/dev/null \ | sed -ne '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \ | awk '/-BEGIN CERTIFICATE-/{if(++i > 1){exit;}}{print;}' } cert_pick() { cert_pick_file "$@" \ || cert_pick_url_selfsigned "$@" \ || cert_pick_url "$@" \ || cert_pick_from_server "$@" } cert_check() { local ret= tmpfile=$(mktemp /tmp/.XXXXXX) cat >${tmpfile} openssl verify $([ "$1" != "0" ] && echo '-crl_check') -CAfile \ <( openssl x509 -noout -text -in ${tmpfile} \ | sed -n 's|.*URI:\(.\+\.cr[tl]\)|\1|p' \ | xargs -I '{}' bash -c " case '{}' in \ *crt) ${WGET} -O- '{}' | openssl x509 -inform DER -outform PEM;; \ *crl) ${WGET} -O- '{}' | openssl crl -inform DER -outform PEM;; \ *) echo 'Sorry, {} not supported' >&2; \ esac" \ | cat "${CA_BUNDLE}" "${HOMEBUNDLE}" - 2>/dev/null ) \ ${tmpfile} >&2 ret=$? [ $ret -eq 0 ] && cat ${tmpfile} rm -- ${tmpfile} echo "$ret" >&2 return $ret } colorize() { # last line = exitcode ( ( test -t 1 || [ $# -ge 1 ] ) \ && sed -u \ -e 's|\(^Trying.*$\)|\x1b[33m\1\x1b[0m|' \ -e 's|\(^Adding.*$\)|\x1b[33m\1\x1b[0m|' \ -e 's|\(^error\s\+.*\)|\x1b[31m\1\x1b[0m|' \ -e 's|\(^\S\+:.*\)|\x1b[32m\1\x1b[0m|' \ -e 's|\(^\S\+\s\+\S\+\s\+URL:.*\)|\x1b[36m\1\x1b[0m|' \ || cat ) | awk 'FNR == 1 { last=$1; while (getline) { print last; last=$0; } exit last}' } pseudo_return() { return $1 } main() { local crl=1 [ "$1" = "-nocrl" ] && shift && crl=0 cert_pick "$@" | cert_check $crl } [[ "${BASH_SOURCE[0]}" != "${0}" ]] || \ ( [ $# -lt 1 ] && echo "usage: $0 [-nocrl] file-or-url-or-server [server-port=443]" \ || ( main "$@"; echo $? ) |& colorize 1 && set +u || ( ret=$?; set +u; pseudo_return $ret ))