ca-scripts/lib/ca-functions

243 lines
7.4 KiB
Bash

#! /bin/bash
# common functions for ca-scripts
PROGNAME=$( basename $0 )
CONFFILE="/etc/ca-scripts.conf"
SHAREDIR="/home/alex/code/ca-scripts/tpl"
CRYPTKEY="-nodes"
INDEXTPL="index-html"
INDEXOUT=""
# 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 "$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
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__
}
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
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"
sed -e "$(ca_sed_cmd)" <"$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}' )
# 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
}