2015-01-24 07:35:42 -05:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
# Common functions for ca-scripts.
|
2009-10-12 17:46:50 -04:00
|
|
|
|
|
|
|
|
|
PROGNAME=$( basename $0 )
|
|
|
|
|
CONFFILE="/etc/ca-scripts.conf"
|
2015-02-18 13:53:35 -05:00
|
|
|
|
SHAREDIR=$(dirname $(dirname $0))/../tpl
|
2009-10-12 17:46:50 -04:00
|
|
|
|
CRYPTKEY="-nodes"
|
|
|
|
|
|
|
|
|
|
INDEXTPL="index-html"
|
|
|
|
|
INDEXOUT=""
|
|
|
|
|
|
2009-10-17 09:18:35 -04:00
|
|
|
|
# ideally, run these scripts as an unprivileged "ssl" user/group
|
|
|
|
|
# and place users that need access to ssl certs into that group
|
|
|
|
|
# no world-readable stuff here
|
|
|
|
|
umask 027
|
|
|
|
|
|
2009-10-12 17:46:50 -04:00
|
|
|
|
error() {
|
|
|
|
|
usage >&2
|
|
|
|
|
echo -e "ERROR: $1\n" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ca_check_var() {
|
|
|
|
|
local varname vartest
|
|
|
|
|
|
|
|
|
|
varname="$1"
|
|
|
|
|
vartest="$2"
|
|
|
|
|
eval "if [ ! $vartest \"\$$varname\" ]; then
|
|
|
|
|
echo '$varname value \"\$$varname\" failed \"$vartest\" test'
|
|
|
|
|
fi"
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-13 10:00:04 -05:00
|
|
|
|
ca_override_conf() {
|
|
|
|
|
local varname
|
|
|
|
|
|
|
|
|
|
varname="${1#USER_}"
|
|
|
|
|
eval "$varname=\"\$USER_$varname\""
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-12 17:46:50 -04:00
|
|
|
|
ca_set_default() {
|
|
|
|
|
local varname vardef
|
2010-03-13 13:04:59 -05:00
|
|
|
|
|
2009-10-12 17:46:50 -04:00
|
|
|
|
varname="$1"
|
|
|
|
|
vardef="$2"
|
|
|
|
|
eval "if [ -z \"\$$varname\" ]; then
|
2010-03-13 10:00:04 -05:00
|
|
|
|
$varname=\"$vardef\"
|
2009-10-12 17:46:50 -04:00
|
|
|
|
fi"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ca_load_conf() {
|
|
|
|
|
local varname vartest varerr vardef error ca_name
|
2015-02-18 14:04:40 -05:00
|
|
|
|
if [ -r "$HOME/.ca-scripts.conf" ]; then
|
|
|
|
|
# Does a system-wide config exist? If so load it first.
|
|
|
|
|
if [ -r "$CONFFILE" ]; then
|
|
|
|
|
source "$CONFFILE"
|
|
|
|
|
fi
|
|
|
|
|
# Override system configuration with local values.
|
2015-02-18 14:11:16 -05:00
|
|
|
|
# If manually set on command-line, don't load.
|
|
|
|
|
if [ -n "$CONFFILECLI" ]; then
|
|
|
|
|
source "$HOME/.ca-scripts.conf"
|
|
|
|
|
fi
|
2015-02-18 14:04:40 -05:00
|
|
|
|
elif [ -r "$CONFFILE" ]; then
|
|
|
|
|
source "$CONFFILE"
|
|
|
|
|
else
|
2015-02-18 14:11:16 -05:00
|
|
|
|
if [ -n "$CONFFILECLI" ]; then
|
|
|
|
|
error "Unable to load $HOME/.ca-scripts.conf or $CONFFILE"
|
|
|
|
|
else
|
|
|
|
|
error "Unable to load $CONFFILE"
|
|
|
|
|
fi
|
2009-10-12 17:46:50 -04:00
|
|
|
|
fi
|
2015-02-18 14:04:40 -05:00
|
|
|
|
|
|
|
|
|
# TODO: Refactored config loader with error fallback, allowing local overrides.
|
|
|
|
|
#if [ ! -r "$CONFFILE" ]; then
|
|
|
|
|
# error "Unable to find $CONFFILE."
|
|
|
|
|
#fi
|
|
|
|
|
## XXX: seems like . <file> doesn't work if it's not relative to a directory
|
|
|
|
|
## look this up on the internet sometime to work out why...
|
|
|
|
|
#if [ "$CONFFILE" = "$( basename $CONFFILE )" ]; then
|
|
|
|
|
# CONFFILE="./$CONFFILE"
|
|
|
|
|
#fi
|
|
|
|
|
#. "$CONFFILE"
|
2010-03-13 13:04:59 -05:00
|
|
|
|
|
2009-10-12 17:46:50 -04:00
|
|
|
|
error=""
|
|
|
|
|
while read vartest varname; do
|
|
|
|
|
varerr=$( ca_check_var "$varname" "$vartest" )
|
|
|
|
|
if [ -n "$varerr" ]; then
|
|
|
|
|
error="$error\n $varerr"
|
|
|
|
|
fi
|
|
|
|
|
done <<__TESTS__
|
|
|
|
|
-d CA_HOME
|
|
|
|
|
-n CA_DOMAIN
|
|
|
|
|
-n CA_DN_C
|
|
|
|
|
-n CA_DN_ST
|
|
|
|
|
-n CA_DN_L
|
|
|
|
|
-n CA_DN_O
|
|
|
|
|
-n CA_DN_OU
|
|
|
|
|
-n CA_DN_CN
|
|
|
|
|
__TESTS__
|
|
|
|
|
if [ -n "$error" ]; then
|
|
|
|
|
error "Parsing config file $CONFFILE failed:\n$error"
|
|
|
|
|
fi
|
2010-03-13 13:04:59 -05:00
|
|
|
|
|
2010-03-13 10:00:04 -05:00
|
|
|
|
# check user-provided variables and copy them to CA_ namespace to override
|
|
|
|
|
# any defaults that have potentially been set in configuration file
|
|
|
|
|
# XXX: this is getting really dirty now, perhaps find an alternative?
|
|
|
|
|
set | awk -F\= '/^USER_CA_[A-Z_]*=/{print $1}' | while read user_var; do
|
|
|
|
|
ca_override_conf "$user_var"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# XXX: and this alternative should probably have better validation ;-)
|
2009-10-12 17:46:50 -04:00
|
|
|
|
case "$CA_CRT_TYPE" in
|
2010-03-13 09:47:57 -05:00
|
|
|
|
server|client|user) :;;
|
|
|
|
|
*) error "Unrecognised certificate type '$CA_CRT_TYPE'!";;
|
2009-10-12 17:46:50 -04:00
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
# we need to do these first to use them in other default defs
|
|
|
|
|
# NOTE: bash's here-string syntax appends \n which tr turns to _ :(
|
2010-03-13 10:13:52 -05:00
|
|
|
|
# CA_NAME is NOT configurable, due to the breakage this could cause.
|
|
|
|
|
CA_NAME="$( echo -n "$CA_DOMAIN" | tr -c '[:alnum:]@-' _ )"
|
2009-10-12 17:46:50 -04:00
|
|
|
|
ca_set_default CA_EMAIL "ca@$CA_DOMAIN"
|
|
|
|
|
|
|
|
|
|
while read varname vardef; do
|
|
|
|
|
ca_set_default "$varname" "$vardef"
|
|
|
|
|
done <<__DEFAULTS__
|
|
|
|
|
CA_DESC $CA_DN_CN
|
2010-03-13 10:11:51 -05:00
|
|
|
|
CA_DAYS 3652
|
|
|
|
|
CA_PATHLEN 0
|
2009-10-12 17:46:50 -04:00
|
|
|
|
CA_CRT_URI http://$CA_DOMAIN/ca/$CA_NAME.ca.crt
|
|
|
|
|
CA_CRL_URI http://$CA_DOMAIN/ca/$CA_NAME.ca.crl
|
2010-02-14 04:14:35 -05:00
|
|
|
|
CA_CRT_DAYS 365
|
2010-03-13 10:11:51 -05:00
|
|
|
|
CA_CRL_DAYS 365
|
2010-02-14 01:58:41 -05:00
|
|
|
|
CA_CRT_BITS 2048
|
2010-03-13 09:47:57 -05:00
|
|
|
|
CA_CRT_TYPE server
|
2009-10-12 17:46:50 -04:00
|
|
|
|
CA_CRT_C $CA_DN_C
|
|
|
|
|
CA_CRT_ST $CA_DN_ST
|
|
|
|
|
CA_CRT_L $CA_DN_L
|
|
|
|
|
CA_CRT_O $CA_DN_O
|
|
|
|
|
CA_CRT_OU $CA_DN_OU
|
|
|
|
|
CA_CRT_E $CA_EMAIL
|
|
|
|
|
__DEFAULTS__
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-19 13:40:37 -05:00
|
|
|
|
# TODO: Remove this, as it's no longer used.
|
2009-10-12 17:46:50 -04:00
|
|
|
|
ca_sed_cmd() {
|
|
|
|
|
# MD5 in CA_CR[TL]_MD5_FP has a non alphabetic character :(
|
|
|
|
|
# XXX: pretty sure this is a dirty and wrong way of templating vars
|
2010-03-13 10:13:52 -05:00
|
|
|
|
# but we can hope that there's no ascii 001 in the values...
|
2009-10-12 17:46:50 -04:00
|
|
|
|
set | awk -F\= '/^CA_[A-Z5_]*=/{print $1}' | while read ca_var; do
|
2010-03-13 10:13:52 -05:00
|
|
|
|
echo -e "s\001%$ca_var%\001${!ca_var}\001g;"
|
2009-10-12 17:46:50 -04:00
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ca_template() {
|
2015-02-19 13:40:37 -05:00
|
|
|
|
local template dest sedstr
|
2009-10-12 17:46:50 -04:00
|
|
|
|
|
2010-03-13 13:04:59 -05:00
|
|
|
|
if [ -r "$1" ]; then
|
2009-10-12 17:46:50 -04:00
|
|
|
|
template="$1"
|
|
|
|
|
elif [ -r "$SHAREDIR/$1.tpl" ]; then
|
|
|
|
|
template="$SHAREDIR/$1.tpl"
|
|
|
|
|
else
|
|
|
|
|
error "Could not read from template $1"
|
|
|
|
|
fi
|
|
|
|
|
dest="$2"
|
|
|
|
|
|
2015-02-19 13:40:37 -05:00
|
|
|
|
while read ca_var; do
|
|
|
|
|
sedstr="${sedstr}s"$'\1'"%$ca_var%"$'\1'"${!ca_var}"$'\1'"g; "
|
|
|
|
|
#sedstr="${sedstr}s%$ca_var%${!ca_var}g; "
|
|
|
|
|
done < <(set | awk -F\= '/^CA_[A-Z5_]*=/{print $1}')
|
|
|
|
|
|
|
|
|
|
#echo "DEBUG: setstr=$sedstr"
|
|
|
|
|
sed -e "$sedstr" <"$template" >"$dest"
|
2009-10-12 17:46:50 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ca_gen_crl() {
|
2010-03-13 10:11:51 -05:00
|
|
|
|
openssl ca -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" -gencrl -md sha1 \
|
|
|
|
|
-crldays "$CA_CRL_DAYS" -out "$CA_HOME/crl/$CA_NAME.ca.crl"
|
2009-10-12 17:46:50 -04:00
|
|
|
|
openssl crl -in "$CA_HOME/crl/$CA_NAME.ca.crl" \
|
|
|
|
|
-out "$CA_HOME/crl/$CA_NAME.ca.crl.der" -outform DER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ca_gen_p12() {
|
|
|
|
|
local cnf_name
|
|
|
|
|
cnf_name="$1"
|
|
|
|
|
openssl pkcs12 -export -descert -out $CA_HOME/p12/$cnf_name.p12 \
|
|
|
|
|
-in $CA_HOME/crt/$cnf_name.crt \
|
|
|
|
|
-inkey $CA_HOME/key/$cnf_name.key \
|
|
|
|
|
-certfile $CA_HOME/crt/$CA_NAME.ca.crt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ca_checksum() {
|
|
|
|
|
CA_CRT_MD5_FP="$( openssl x509 -in $CA_HOME/crt/$CA_NAME.ca.crt \
|
|
|
|
|
-noout -md5 -fingerprint | cut -d= -f2 )"
|
|
|
|
|
CA_CRT_SHA_FP="$( openssl x509 -in $CA_HOME/crt/$CA_NAME.ca.crt \
|
|
|
|
|
-noout -sha1 -fingerprint | cut -d= -f2 )"
|
|
|
|
|
CA_CRL_MD5_FP="$( openssl crl -in $CA_HOME/crl/$CA_NAME.ca.crl \
|
|
|
|
|
-noout -md5 -fingerprint | cut -d= -f2 )"
|
|
|
|
|
CA_CRL_SHA_FP="$( openssl crl -in $CA_HOME/crl/$CA_NAME.ca.crl \
|
|
|
|
|
-noout -sha1 -fingerprint | cut -d= -f2 )"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ca_cnf_name() {
|
|
|
|
|
local crt
|
|
|
|
|
crt="$1"
|
|
|
|
|
# work out what configuration files we should be using from the cert's CN
|
|
|
|
|
echo $( openssl x509 -in "$crt" -noout -nameopt sep_multiline,use_quote \
|
2010-03-13 13:04:59 -05:00
|
|
|
|
-subject | awk -F= '/CN=/{ gsub("[^A-Za-z0-9@-]", "_", $2); print $2}' )
|
2009-10-16 13:37:11 -04:00
|
|
|
|
# grep "CN=" | cut -d= -f2 | tr -c '[:alnum:]@-' _ )
|
|
|
|
|
# originally did this like the above but tried awk instead. awk(1) seems
|
|
|
|
|
# to lie about it's egrep(1) support though as no matter what I tried the
|
|
|
|
|
# tr(1) regex didn't work in the gsub() call above.
|
2009-10-12 17:46:50 -04:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-13 09:47:57 -05:00
|
|
|
|
ca_cnf_type() {
|
|
|
|
|
local crt
|
|
|
|
|
# XXX: dirty hack -- derive type from filename being *.TYPE.crt
|
|
|
|
|
crt="${1%.crt}"
|
|
|
|
|
crt="${crt##*.}"
|
|
|
|
|
case "$crt" in
|
|
|
|
|
server|client|user) echo "$crt";;
|
|
|
|
|
*) echo $CA_CRT_TYPE;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-12 17:46:50 -04:00
|
|
|
|
ca_find_cnf() {
|
2010-03-13 09:47:57 -05:00
|
|
|
|
local name _name _type
|
2009-10-12 17:46:50 -04:00
|
|
|
|
name="$1"
|
|
|
|
|
|
|
|
|
|
if [ -f "$name" ]; then
|
2010-03-13 09:47:57 -05:00
|
|
|
|
_name="$(ca_cnf_name $name)"
|
|
|
|
|
_type="$(ca_cnf_type $name)"
|
|
|
|
|
if [ $(basename "$name" .crt) = "${_name}.${_type}" ]; then
|
|
|
|
|
echo "${_name}.${_type}"
|
|
|
|
|
return
|
2009-10-12 17:46:50 -04:00
|
|
|
|
else
|
2010-03-13 09:47:57 -05:00
|
|
|
|
error "Unable to derive config details from certificate '$name'."
|
2009-10-12 17:46:50 -04:00
|
|
|
|
fi
|
|
|
|
|
fi
|
2010-03-13 13:04:59 -05:00
|
|
|
|
|
2010-03-13 09:47:57 -05:00
|
|
|
|
# XXX: this stil doesn't handle default types. FIXME when it's not 1am.
|
2009-10-12 17:46:50 -04:00
|
|
|
|
_name=$( echo -n "$name" | tr -c '[:alnum:]@-' _ )
|
2010-03-13 13:04:59 -05:00
|
|
|
|
if [ "$CA_CRT_TYPE" = "user" ]; then
|
2009-10-12 17:46:50 -04:00
|
|
|
|
# user names may have dots etc. in, so use munged version in match
|
|
|
|
|
# check if name is "user@domain", append $CA_DOMAIN if not
|
|
|
|
|
if [ "${_name%%@*}" = "$_name" \
|
|
|
|
|
-a -f "$CA_HOME/crt/${_name}@$CA_NAME.$CA_CRT_TYPE.crt" ];
|
|
|
|
|
then
|
|
|
|
|
# name is not fully-qualified, but a cert exists for it
|
|
|
|
|
echo "${_name}@$CA_NAME.$CA_CRT_TYPE"
|
|
|
|
|
elif [ -f "$CA_HOME/crt/$_name.$CA_CRT_TYPE.crt" ]; then
|
|
|
|
|
# name was fully-qualified and a cert exists
|
|
|
|
|
echo "$_name.$CA_CRT_TYPE"
|
|
|
|
|
else
|
|
|
|
|
error "Could not find $CA_CRT_TYPE certificate configuration matching '$name'"
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
# check if name is fully-qualified -- contains more than 1 dot
|
|
|
|
|
# NOTE: we have to do this test with the unmunged name ...
|
|
|
|
|
if [ "${name%%.*}" = "$name" \
|
|
|
|
|
-a -f "$CA_HOME/crt/${_name}_$CA_NAME.$CA_CRT_TYPE.crt" ];
|
|
|
|
|
then
|
|
|
|
|
# name is not fully-qualified, but a cert exists for it
|
|
|
|
|
echo "${_name}_$CA_NAME.$CA_CRT_TYPE"
|
|
|
|
|
elif [ -f "$CA_HOME/crt/$_name.$CA_CRT_TYPE.crt" ]; then
|
|
|
|
|
# name was fully-qualified and a cert exists
|
|
|
|
|
echo "$_name.$CA_CRT_TYPE"
|
|
|
|
|
else
|
|
|
|
|
error "Could not find $CA_CRT_TYPE certificate configuration matching '$name'"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
}
|
2015-02-19 14:43:20 -05:00
|
|
|
|
|
|
|
|
|
cert_info() {
|
|
|
|
|
local certFile="$1"
|
|
|
|
|
local certCN certIssuer certValid certExpire certCA certFilename
|
|
|
|
|
|
|
|
|
|
if [ -r "$certFile" ]; then
|
|
|
|
|
certFilename=$(basename "$certFile")
|
|
|
|
|
certCN="$(openssl x509 -in "$certFile" -noout -subject | sed -r 's|.*CN=(.*)|\1|; s|/[^/]*=.*$||')"
|
|
|
|
|
certIssuer="$(openssl x509 -in "$certFile" -noout -issuer | sed -r 's|.*CN=(.*)|\1|; s|/[^/]*=.*$||')"
|
|
|
|
|
certValid="$(openssl x509 -in "$certFile" -noout -startdate | sed -r 's|.*notBefore=(.*)|\1|;')"
|
|
|
|
|
certExpire="$(openssl x509 -in "$certFile" -noout -enddate | sed -r 's|.*notAfter=(.*)$|\1|;')"
|
|
|
|
|
if [ "$certCN" = "$certIssuer" ]; then
|
|
|
|
|
echo "$certFilename: $certCN expires on $certExpire"
|
|
|
|
|
else
|
|
|
|
|
echo "$certFilename: $certCN issued by $certIssuer expires on $certExpire"
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
echo "ERROR: $certFile does not exist or cannot be read"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|