summaryrefslogtreecommitdiffstats
path: root/scripts/certs/cert-check
blob: 09b1a0f01a5b3024f3a35a9d4f64c39edd9c4ab0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/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 ))