ca-scripts/lib/ca-functions

292 lines
9.3 KiB
Text
Raw Permalink Normal View History

2015-01-24 07:35:42 -05:00
#!/bin/bash
# Common functions for ca-scripts.
PROGNAME=$( basename $0 )
CONFFILE="/etc/ca-scripts.conf"
SHAREDIR=$(dirname $(readlink -f $0))/../tpl
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
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"
}
ca_override_conf() {
local varname
varname="${1#USER_}"
eval "$varname=\"\$USER_$varname\""
}
ca_set_default() {
local varname vardef
varname="$1"
vardef="$2"
eval "if [ -z \"\$$varname\" ]; then
$varname=\"$vardef\"
fi"
}
ca_load_conf() {
local varname vartest varerr vardef error ca_name
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.
# If manually set on command-line, don't load.
2015-02-19 14:49:51 -05:00
if [ -z "$CONFFILECLI" ]; then
source "$HOME/.ca-scripts.conf"
fi
elif [ -r "$CONFFILE" ]; then
source "$CONFFILE"
else
if [ -n "$CONFFILECLI" ]; then
error "Unable to load $HOME/.ca-scripts.conf or $CONFFILE"
else
error "Unable to load $CONFFILE"
fi
fi
# 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"
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
# 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 ;-)
case "$CA_CRT_TYPE" in
server|client|user) :;;
*) error "Unrecognised certificate type '$CA_CRT_TYPE'!";;
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 _ :(
# CA_NAME is NOT configurable, due to the breakage this could cause.
CA_NAME="$( echo -n "$CA_DOMAIN" | tr -c '[:alnum:]@-' _ )"
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
CA_DAYS 3652
CA_PATHLEN 0
CA_CRT_URI http://$CA_DOMAIN/ca/$CA_NAME.ca.crt
CA_CRL_URI http://$CA_DOMAIN/ca/$CA_NAME.ca.crl
CA_CRT_DAYS 365
CA_CRL_DAYS 365
2010-02-14 01:58:41 -05:00
CA_CRT_BITS 2048
CA_CRT_TYPE server
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__
}
# TODO: Remove this, as it's no longer used.
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
# but we can hope that there's no ascii 001 in the values...
set | awk -F\= '/^CA_[A-Z5_]*=/{print $1}' | while read ca_var; do
echo -e "s\001%$ca_var%\001${!ca_var}\001g;"
done
}
ca_template() {
local template dest sedstr
if [ -r "$1" ]; then
template="$1"
elif [ -r "$SHAREDIR/$1.tpl" ]; then
template="$SHAREDIR/$1.tpl"
else
error "Could not read from template $1"
fi
dest="$2"
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"
}
ca_gen_crl() {
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"
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 \
-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.
}
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
}
ca_find_cnf() {
local name _name _type
name="$1"
if [ -f "$name" ]; then
_name="$(ca_cnf_name $name)"
_type="$(ca_cnf_type $name)"
if [ $(basename "$name" .crt) = "${_name}.${_type}" ]; then
echo "${_name}.${_type}"
return
else
error "Unable to derive config details from certificate '$name'."
fi
fi
# XXX: this stil doesn't handle default types. FIXME when it's not 1am.
_name=$( echo -n "$name" | tr -c '[:alnum:]@-' _ )
if [ "$CA_CRT_TYPE" = "user" ]; then
# 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
}
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
}