Jérémy Lecour
706608ca4a
It doesn't seem more or less secure to embed the password as an argument than an environment variable written at the begining of the line.
793 lines
21 KiB
Bash
Executable file
793 lines
21 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# shellpki is a wrapper around OpenSSL to manage a small PKI
|
|
#
|
|
|
|
set -e
|
|
|
|
VERSION="1.0.0"
|
|
|
|
show_version() {
|
|
cat <<END
|
|
shellpki version ${VERSION}
|
|
|
|
Copyright 2010-2019 Evolix <info@evolix.fr>,
|
|
Thomas Martin <tmartin@evolix.fr>,
|
|
Gregory Colpart <reg@evolix.fr>,
|
|
Romain Dessort <rdessort@evolix.fr>,
|
|
Benoit Série <bserie@evolix.fr>,
|
|
Victor Laborie <vlaborie@evolix.fr>,
|
|
Daniel Jakots <djakots@evolix.fr>,
|
|
Patrick Marchand <pmarchand@evolix.fr>,
|
|
Jérémy Lecour <jlecour@evolix.fr>
|
|
and others.
|
|
|
|
shellpki comes with ABSOLUTELY NO WARRANTY. This is free software,
|
|
and you are welcome to redistribute it under certain conditions.
|
|
See the MIT Licence for details.
|
|
END
|
|
}
|
|
|
|
init() {
|
|
umask 0177
|
|
|
|
[ -d "${CA_DIR}" ] || mkdir -m 0750 "${CA_DIR}"
|
|
[ -d "${CRT_DIR}" ] || mkdir -m 0750 "${CRT_DIR}"
|
|
[ -f "${INDEX_FILE}" ] || touch "${INDEX_FILE}"
|
|
[ -f "${CRL}" ] || touch "${CRL}"
|
|
[ -f "${SERIAL}" ] || echo "01" > "${SERIAL}"
|
|
|
|
cn="${1:-}"
|
|
if [ -z "${cn}" ]; then
|
|
show_usage >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -f "${CA_KEY}" ]; then
|
|
printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_KEY}"
|
|
read -r REPLY
|
|
resp=$(echo "${REPLY}" | tr 'Y' 'y')
|
|
if [ "${resp}" = "y" ]; then
|
|
rm -f "${CA_KEY}" "${CA_CERT}"
|
|
fi
|
|
fi
|
|
|
|
if [ ! -f "${CA_KEY}" ]; then
|
|
"${OPENSSL_BIN}" genrsa \
|
|
-out "${CA_KEY}" \
|
|
-aes256 ${CA_KEY_LENGTH} \
|
|
>/dev/null 2>&1
|
|
fi
|
|
|
|
if [ -f "${CA_CERT}" ]; then
|
|
printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_CERT}"
|
|
read -r REPLY
|
|
resp=$(echo "${REPLY}" | tr 'Y' 'y')
|
|
if [ "${resp}" = "y" ]; then
|
|
rm "${CA_CERT}"
|
|
fi
|
|
fi
|
|
|
|
if [ ! -f "${CA_CERT}" ]; then
|
|
ask_ca_password 0
|
|
fi
|
|
|
|
if [ ! -f "${CA_CERT}" ]; then
|
|
"${OPENSSL_BIN}" req \
|
|
-new \
|
|
-batch \
|
|
-sha512 \
|
|
-x509 \
|
|
-days 3650 \
|
|
-extensions v3_ca \
|
|
-passin pass:${CA_PASSWORD} \
|
|
-key "${CA_KEY}" \
|
|
-out "${CA_CERT}" \
|
|
-config /dev/stdin <<EOF
|
|
$(cat "${CONF_FILE}")
|
|
commonName_default = ${cn}
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
ocsp() {
|
|
umask 0177
|
|
|
|
ocsp_uri="${1:-}"
|
|
if [ -z "${ocsp_uri}" ]; then
|
|
show_usage >&2
|
|
exit 1
|
|
fi
|
|
ocsp_csr_file="${CSR_DIR}/ocsp.csr"
|
|
|
|
url=$(echo "${ocsp_uri}" | cut -d':' -f1)
|
|
port=$(echo "${ocsp_uri}" | cut -d':' -f2)
|
|
|
|
if [ ! -f "${OCSP_KEY}" ]; then
|
|
"${OPENSSL_BIN}" genrsa \
|
|
-out "${OCSP_KEY}" \
|
|
${KEY_LENGTH} \
|
|
>/dev/null 2>&1
|
|
fi
|
|
|
|
"${OPENSSL_BIN}" req \
|
|
-batch \
|
|
-new \
|
|
-key "${OCSP_KEY}" \
|
|
-out "${ocsp_csr_file}" \
|
|
-config /dev/stdin <<EOF
|
|
$(cat "${CONF_FILE}")
|
|
commonName_default = ${url}
|
|
[ usr_cert ]
|
|
authorityInfoAccess = OCSP;URI:http://${ocsp_uri}
|
|
EOF
|
|
|
|
if [ ! -f "${OCSP_CERT}" ]; then
|
|
ask_ca_password 0
|
|
fi
|
|
|
|
if [ ! -f "${OCSP_CERT}" ]; then
|
|
"${OPENSSL_BIN}" ca \
|
|
-extensions v3_ocsp \
|
|
-in "${ocsp_csr_file}" \
|
|
-out "${OCSP_CERT}" \
|
|
-passin pass:${CA_PASSWORD} \
|
|
-config "${CONF_FILE}"
|
|
fi
|
|
|
|
exec "${OPENSSL_BIN}" ocsp \
|
|
-ignore_err \
|
|
-index "${INDEX_FILE}" \
|
|
-port "${port}" \
|
|
-rsigner "${OCSP_CERT}" \
|
|
-rkey "${OCSP_KEY}" \
|
|
-CA "${CA_CERT}" \
|
|
-text
|
|
}
|
|
|
|
show_usage() {
|
|
cat <<EOF
|
|
Usage: ${0} <subcommand> [options] [CommonName]
|
|
|
|
Initialize PKI (create CA key and self-signed cert) :
|
|
|
|
${0} init <commonName_for_CA>
|
|
|
|
Run OCSP_D server :
|
|
|
|
${0} ocsp <ocsp_uri:ocsp_port>
|
|
|
|
Create a client cert with key and CSR directly generated on server
|
|
(use -p or --password-file to set a password on the client key) :
|
|
|
|
${0} create [-p|--password-file=<FILE>] <commonName>
|
|
|
|
Create a client cert from a CSR (doesn't need key) :
|
|
|
|
${0} create -f <path>
|
|
|
|
Revoke a client cert with is commonName (CN) :
|
|
|
|
${0} revoke <commonName>
|
|
|
|
List all actually valid commonName (CN) :
|
|
|
|
${0} list [-a|v|r]
|
|
|
|
Check expiration date of valid certificates :
|
|
|
|
${0} check
|
|
|
|
EOF
|
|
}
|
|
|
|
error() {
|
|
echo "${1}" >&2
|
|
exit 1
|
|
}
|
|
|
|
warning() {
|
|
echo "${1}" >&2
|
|
}
|
|
|
|
verify_ca_password() {
|
|
"${OPENSSL_BIN}" rsa \
|
|
-in "${CA_KEY}" \
|
|
-passin pass:${CA_PASSWORD} \
|
|
>/dev/null 2>&1
|
|
}
|
|
|
|
ask_ca_password() {
|
|
attempt=${1:-0}
|
|
max_attempt=3
|
|
|
|
trap 'unset CA_PASSWORD' 0
|
|
|
|
if [ ! -f "${CA_KEY}" ]; then
|
|
error "You must initialize your PKI with \`shellpki init' !"
|
|
fi
|
|
if [ "${attempt}" -gt 0 ]; then
|
|
warning "Invalid password, retry."
|
|
fi
|
|
if [ "${attempt}" -ge "${max_attempt}" ]; then
|
|
error "Maximum number of attempts reached (${max_attempt})."
|
|
fi
|
|
if [ -z "${CA_PASSWORD}" ]; then
|
|
stty -echo
|
|
printf "Password for CA key : "
|
|
read -r CA_PASSWORD
|
|
stty echo
|
|
printf "\n"
|
|
fi
|
|
if [ -z "${CA_PASSWORD}" ] || ! verify_ca_password; then
|
|
unset CA_PASSWORD
|
|
attempt=$(( attempt + 1 ))
|
|
ask_ca_password "${attempt}"
|
|
fi
|
|
}
|
|
|
|
create() {
|
|
from_csr=0
|
|
ask_pass=0
|
|
|
|
# Parse options
|
|
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
while :; do
|
|
case $1 in
|
|
-f|--file|--csr-file)
|
|
# csr-file option, with value separated by space
|
|
if [ -n "$2" ]; then
|
|
from_csr=1
|
|
csr_file=$(readlink -f -- "${2}")
|
|
shift
|
|
else
|
|
printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
--file=?*|--csr-file=?*)
|
|
from_csr=1
|
|
# csr-file option, with value separated by =
|
|
csr_file=$(readlink -f -- "${1#*=}")
|
|
;;
|
|
--file=|--csr-file=)
|
|
# csr-file options, without value
|
|
printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2
|
|
exit 1
|
|
;;
|
|
-p|--password)
|
|
ask_pass=1
|
|
;;
|
|
--password-file)
|
|
# password-file option, with value separated by space
|
|
if [ -n "$2" ]; then
|
|
password_file=$(readlink -f -- "${2}")
|
|
shift
|
|
else
|
|
printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
--password-file=?*)
|
|
# password-file option, with value separated by =
|
|
password_file=$(readlink -f -- "${1#*=}")
|
|
;;
|
|
--password-file=)
|
|
# password-file options, without value
|
|
printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2
|
|
exit 1
|
|
;;
|
|
--days)
|
|
# days option, with value separated by space
|
|
if [ -n "$2" ]; then
|
|
days=${2}
|
|
shift
|
|
else
|
|
printf 'ERROR: "--days" requires a non-empty option argument.\n' >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
--days=?*)
|
|
# days option, with value separated by =
|
|
days=${1#*=}
|
|
;;
|
|
--days=)
|
|
# days options, without value
|
|
printf 'ERROR: "--days" requires a non-empty option argument.\n' >&2
|
|
exit 1
|
|
;;
|
|
--end-date)
|
|
# end-date option, with value separated by space
|
|
if [ -n "$2" ]; then
|
|
end_date=${2}
|
|
shift
|
|
else
|
|
printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
--end-date=?*)
|
|
# end-date option, with value separated by =
|
|
end_date=${1#*=}
|
|
;;
|
|
--end-date=)
|
|
# end-date options, without value
|
|
printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2
|
|
exit 1
|
|
;;
|
|
--)
|
|
# End of all options.
|
|
shift
|
|
break
|
|
;;
|
|
-?*)
|
|
# ignore unknown options
|
|
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
|
|
;;
|
|
*)
|
|
# Default case: If no more options then break out of the loop.
|
|
break
|
|
;;
|
|
esac
|
|
|
|
shift
|
|
done
|
|
|
|
# The name of the certificate
|
|
cn="${1:-}"
|
|
|
|
# Set expiration argument
|
|
crt_expiration_arg=""
|
|
if [ -n "${days}" ] && [ "${days}" -gt 0 ]; then
|
|
crt_expiration_arg="-days ${days}"
|
|
fi
|
|
if [ -n "${end_date}" ]; then
|
|
cert_end_date=$(TZ=:Zulu date --date "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null)
|
|
if [ "$?" -ne 0 ]; then
|
|
error "Invalid end date format : \`${end_date}' can't be parsed by date(1)"
|
|
else
|
|
crt_expiration_arg="-enddate ${cert_end_date}"
|
|
fi
|
|
fi
|
|
|
|
if [ "${from_csr}" -eq 1 ]; then
|
|
if [ "${ask_pass}" -eq 1 ]; then
|
|
warning "Warning: -p|--password is ignored with -f|--file|--crt-file"
|
|
fi
|
|
if [ -n "${password_file}" ]; then
|
|
warning "Warning: --password-file is ignored with -f|--file|--crt-file"
|
|
fi
|
|
|
|
crt_file="${CRT_DIR}/${cn}.crt"
|
|
|
|
# ask for CA passphrase
|
|
ask_ca_password 0
|
|
|
|
# check if csr_file is a CSR
|
|
"${OPENSSL_BIN}" req \
|
|
-noout \
|
|
-subject \
|
|
-in "${csr_file}" \
|
|
>/dev/null 2>&1
|
|
if [ "$?" -ne 0 ]; then
|
|
error "${csr_file} is not a valid CSR !"
|
|
fi
|
|
|
|
# check if csr_file contain a CN
|
|
"${OPENSSL_BIN}" req \
|
|
-noout \
|
|
-subject \
|
|
-in "${csr_file}" \
|
|
| grep -Eo "CN\s*=[^,/]*" \
|
|
>/dev/null 2>&1
|
|
if [ "$?" -ne 0 ]; then
|
|
error "${csr_file} doesn't contain a CommonName !"
|
|
fi
|
|
|
|
# get CN from CSR
|
|
cn=$("${OPENSSL_BIN}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs)
|
|
|
|
# check if CN already exist
|
|
if [ -f "${CRT_DIR}/${cn}.crt" ]; then
|
|
printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}"
|
|
read -r REPLY
|
|
resp=$(echo "${REPLY}" | tr 'Y' 'y')
|
|
if [ "${resp}" = "y" ]; then
|
|
revoke "${cn}"
|
|
else
|
|
error "Abort"
|
|
fi
|
|
fi
|
|
|
|
# ca sign and generate cert
|
|
"${OPENSSL_BIN}" ca \
|
|
-config "${CONF_FILE}" \
|
|
-in "${csr_file}" \
|
|
-passin pass:${CA_PASSWORD} \
|
|
-out "${crt_file}" \
|
|
${crt_expiration_arg}
|
|
|
|
echo "The CRT file is available in ${crt_file}"
|
|
else
|
|
if [ -z "${cn}" ]; then
|
|
show_usage >&2
|
|
exit 1
|
|
fi
|
|
csr_file="${CSR_DIR}/${cn}-${SUFFIX}.csr"
|
|
crt_file="${CRT_DIR}/${cn}.crt"
|
|
key_file="${KEY_DIR}/${cn}-${SUFFIX}.key"
|
|
ovpn_file="${OVPN_DIR}/${cn}-${SUFFIX}.ovpn"
|
|
pkcs12_file="${PKCS12_DIR}/${cn}-${SUFFIX}.p12"
|
|
|
|
# check if CN already exist
|
|
if [ -f "${crt_file}" ]; then
|
|
printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}"
|
|
read -r REPLY
|
|
resp=$(echo "${REPLY}" | tr 'Y' 'y')
|
|
if [ "${resp}" = "y" ]; then
|
|
revoke "${cn}"
|
|
else
|
|
error "Abort"
|
|
fi
|
|
fi
|
|
|
|
# ask for CA passphrase
|
|
ask_ca_password 0
|
|
|
|
if [ "${ask_pass}" -eq 1 ]; then
|
|
trap 'unset PASSWORD' 0
|
|
stty -echo
|
|
printf "Password for user key : "
|
|
read -r PASSWORD
|
|
stty echo
|
|
printf "\n"
|
|
|
|
if [ -z "${PASSWORD}" ]; then
|
|
warning "Warning: empty password from input"
|
|
fi
|
|
fi
|
|
|
|
# generate private key
|
|
PASS_ARGS=""
|
|
if [ -n "${password_file}" ]; then
|
|
PASS_ARGS="-aes256 -passout file:${password_file}"
|
|
elif [ -n "${PASSWORD}" ]; then
|
|
PASS_ARGS="-aes256 -passout pass:${PASSWORD}"
|
|
fi
|
|
"${OPENSSL_BIN}" genrsa \
|
|
-out "${key_file}" \
|
|
${PASS_ARGS} \
|
|
${KEY_LENGTH}
|
|
|
|
# generate csr req
|
|
PASS_ARGS=""
|
|
if [ -n "${password_file}" ]; then
|
|
PASS_ARGS="-passin file:${password_file}"
|
|
elif [ -n "${PASSWORD}" ]; then
|
|
PASS_ARGS="-passin pass:${PASSWORD}"
|
|
fi
|
|
"${OPENSSL_BIN}" req \
|
|
-batch \
|
|
-new \
|
|
-key "${key_file}" \
|
|
-out "${csr_file}" \
|
|
${PASS_ARGS} \
|
|
-config /dev/stdin <<EOF
|
|
$(cat "${CONF_FILE}")
|
|
commonName_default = ${cn}
|
|
EOF
|
|
|
|
# ca sign and generate cert
|
|
"${OPENSSL_BIN}" ca \
|
|
-config "${CONF_FILE}" \
|
|
-passin pass:${CA_PASSWORD} \
|
|
-in "${csr_file}" \
|
|
-out "${crt_file}" \
|
|
${crt_expiration_arg}
|
|
|
|
# check if CRT is a valid
|
|
"${OPENSSL_BIN}" x509 \
|
|
-noout \
|
|
-subject \
|
|
-in "${crt_file}" \
|
|
>/dev/null 2>&1
|
|
if [ "$?" -ne 0 ]; then
|
|
rm -f "${crt_file}"
|
|
fi
|
|
if [ ! -f "${crt_file}" ]; then
|
|
error "Error in CSR creation"
|
|
fi
|
|
|
|
chmod 640 "${crt_file}"
|
|
|
|
echo "The CRT file is available in ${crt_file}"
|
|
|
|
# generate pkcs12 format
|
|
PASS_ARGS=""
|
|
if [ -n "${password_file}" ]; then
|
|
# Hack for pkcs12 :
|
|
# If passin and passout files are the same path, it expects 2 lines
|
|
# so we make a temporary copy of the password file
|
|
password_file_out=$(mktemp)
|
|
cp "${password_file}" "${password_file_out}"
|
|
PASS_ARGS="-passin file:${password_file} -passout file:${password_file_out}"
|
|
elif [ -n "${PASSWORD}" ]; then
|
|
PASS_ARGS="-passin pass:${PASSWORD} -passout pass:${PASSWORD}"
|
|
else
|
|
PASS_ARGS="-passout pass:"
|
|
fi
|
|
"${OPENSSL_BIN}" pkcs12 \
|
|
-export \
|
|
-nodes \
|
|
-inkey "${key_file}" \
|
|
-in "${crt_file}" \
|
|
-out "${pkcs12_file}" \
|
|
${PASS_ARGS}
|
|
|
|
if [ -n "${password_file_out}" ]; then
|
|
# Hack for pkcs12 :
|
|
# Destroy the temporary file
|
|
rm -f "${password_file_out}"
|
|
fi
|
|
|
|
chmod 640 "${pkcs12_file}"
|
|
echo "The PKCS12 config file is available in ${pkcs12_file}"
|
|
|
|
# generate openvpn format
|
|
if [ -e "${CA_DIR}/ovpn.conf" ]; then
|
|
cat "${CA_DIR}/ovpn.conf" - > "${ovpn_file}" <<EOF
|
|
<ca>
|
|
$(cat "${CA_CERT}")
|
|
</ca>
|
|
|
|
<cert>
|
|
$(cat "${crt_file}")
|
|
</cert>
|
|
|
|
<key>
|
|
$(cat "${key_file}")
|
|
</key>
|
|
EOF
|
|
chmod 640 "${ovpn_file}"
|
|
echo "The OpenVPN config file is available in ${ovpn_file}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
revoke() {
|
|
if [ "${1}" = "" ]; then
|
|
show_usage >&2
|
|
exit 1
|
|
fi
|
|
crt_file="${CRT_DIR}/${cn}.crt"
|
|
|
|
# get CN from param
|
|
cn="${1}"
|
|
|
|
# check if CRT exists
|
|
if [ ! -f "${crt_file}" ]; then
|
|
error "Unknow CN : ${cn} (\`${crt_file}' not found)"
|
|
fi
|
|
|
|
# check if CRT is a valid
|
|
"${OPENSSL_BIN}" x509 \
|
|
-noout \
|
|
-subject \
|
|
-in "${crt_file}" \
|
|
>/dev/null 2>&1
|
|
if [ "$?" -ne 0 ]; then
|
|
error "${crt_file} is not a valid CRT, you must delete it !"
|
|
fi
|
|
|
|
# ask for CA passphrase
|
|
ask_ca_password 0
|
|
|
|
echo "Revoke certificate ${crt_file} :"
|
|
"${OPENSSL_BIN}" ca \
|
|
-config "${CONF_FILE}" \
|
|
-passin pass:${CA_PASSWORD} \
|
|
-revoke "${crt_file}"
|
|
if [ "$?" -eq 0 ]; then
|
|
rm "${crt_file}"
|
|
fi
|
|
|
|
"${OPENSSL_BIN}" ca \
|
|
-config "${CONF_FILE}" \
|
|
-passin pass:${CA_PASSWORD} \
|
|
-gencrl \
|
|
-out "${CRL}"
|
|
}
|
|
|
|
list() {
|
|
if [ ! -f "${INDEX_FILE}" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
list_valid=0
|
|
list_revoked=1
|
|
|
|
while :; do
|
|
case "${1}" in
|
|
-a|--all)
|
|
list_valid=0
|
|
list_revoked=0
|
|
shift;;
|
|
-v|--valid)
|
|
list_valid=0
|
|
list_revoked=1
|
|
shift;;
|
|
-r|--revoked)
|
|
list_valid=1
|
|
list_revoked=0
|
|
shift;;
|
|
--)
|
|
shift
|
|
break;;
|
|
-?*)
|
|
warning "unknow option ${1} (ignored)"
|
|
shift;;
|
|
*)
|
|
break;;
|
|
esac
|
|
done
|
|
|
|
if [ "${list_valid}" -eq 0 ]; then
|
|
certs=$(grep "^V" "${INDEX_FILE}")
|
|
fi
|
|
|
|
if [ "${list_revoked}" -eq 0 ]; then
|
|
certs=$(grep "^R" "${INDEX_FILE}")
|
|
fi
|
|
|
|
if [ "${list_valid}" -eq 0 ] && [ "${list_revoked}" -eq 0 ]; then
|
|
certs=$(cat "${INDEX_FILE}")
|
|
fi
|
|
|
|
echo "${certs}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs -n1
|
|
}
|
|
|
|
cert_end_date() {
|
|
"${OPENSSL_BIN}" x509 -noout -enddate -in "${1}" | cut -d'=' -f2
|
|
}
|
|
|
|
check() {
|
|
# default expiration alert
|
|
# TODO : permit override with parameters
|
|
min_day=90
|
|
cur_epoch=$(date -u +'%s')
|
|
|
|
for cert in ${CRT_DIR}/*; do
|
|
end_date=$(cert_end_date "${cert}")
|
|
end_epoch=$(date -ud "${end_date}" +'%s')
|
|
diff_epoch=$(( end_epoch - cur_epoch ))
|
|
diff_day=$(( diff_epoch / 60 / 60 / 24 ))
|
|
if [ "${diff_day}" -lt "${min_day}" ]; then
|
|
if [ "${diff_day}" -le 0 ]; then
|
|
echo "${cert} has expired"
|
|
else
|
|
echo "${cert} expire in ${diff_day} days"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
is_user() {
|
|
getent passwd "${1}" >/dev/null
|
|
}
|
|
is_group() {
|
|
getent group "${1}" >/dev/null
|
|
}
|
|
|
|
main() {
|
|
# default config
|
|
# TODO : override with /etc/default/shellpki
|
|
CONF_FILE="/etc/shellpki/openssl.cnf"
|
|
|
|
if [ "$(uname)" = "OpenBSD" ]; then
|
|
PKI_USER="_shellpki"
|
|
else
|
|
PKI_USER="shellpki"
|
|
fi
|
|
|
|
if [ "${USER}" != "root" ] && [ "${USER}" != "${PKI_USER}" ]; then
|
|
error "Please become root before running ${0} !"
|
|
fi
|
|
|
|
# retrieve CA path from config file
|
|
CA_DIR=$(grep -E "^dir" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1)
|
|
CA_KEY=$(grep -E "^private_key" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
|
CA_CERT=$(grep -E "^certificate" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
|
OCSP_KEY="${CA_DIR}/ocsp.key"
|
|
OCSP_CERT="${CA_DIR}/ocsp.pem"
|
|
CRT_DIR=$(grep -E "^certs" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
|
TMP_DIR=$(grep -E "^new_certs_dir" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
|
INDEX_FILE=$(grep -E "^database" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
|
SERIAL=$(grep -E "^serial" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
|
CRL=$(grep -E "^crl" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
|
|
|
|
# directories for clients key, csr, crt
|
|
KEY_DIR="${CA_DIR}/private"
|
|
CSR_DIR="${CA_DIR}/requests"
|
|
PKCS12_DIR="${CA_DIR}/pkcs12"
|
|
OVPN_DIR="${CA_DIR}/openvpn"
|
|
|
|
CA_KEY_LENGTH=4096
|
|
if [ "${CA_KEY_LENGTH}" -lt 4096 ]; then
|
|
error "CA key must be at least 4096 bits long."
|
|
fi
|
|
KEY_LENGTH=2048
|
|
if [ "${KEY_LENGTH}" -lt 2048 ]; then
|
|
error "User key must be at least 2048 bits long."
|
|
fi
|
|
|
|
OPENSSL_BIN=$(command -v openssl)
|
|
SUFFIX=$(/bin/date +"%s")
|
|
|
|
if ! is_user "${PKI_USER}" || ! is_group "${PKI_USER}"; then
|
|
error "You must create ${PKI_USER} user and group !"
|
|
fi
|
|
|
|
if [ ! -e "${CONF_FILE}" ]; then
|
|
error "${CONF_FILE} is missing"
|
|
fi
|
|
|
|
mkdir -p "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}"
|
|
|
|
command=${1:-help}
|
|
|
|
case "${command}" in
|
|
init)
|
|
shift
|
|
init "$@"
|
|
;;
|
|
|
|
ocsp)
|
|
shift
|
|
ocsp "$@"
|
|
;;
|
|
|
|
create)
|
|
shift
|
|
create "$@"
|
|
;;
|
|
|
|
revoke)
|
|
shift
|
|
revoke "$@"
|
|
;;
|
|
|
|
list)
|
|
shift
|
|
list "$@"
|
|
;;
|
|
|
|
check)
|
|
shift
|
|
check "$@"
|
|
;;
|
|
|
|
version)
|
|
show_version
|
|
exit 0
|
|
;;
|
|
|
|
help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
|
|
*)
|
|
show_usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# fix right
|
|
chown -R "${PKI_USER}":"${PKI_USER}" "${CA_DIR}"
|
|
chmod 750 "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}"
|
|
chmod 600 "${INDEX_FILE}"* "${SERIAL}"* "${CA_KEY}" "${CRL}"
|
|
chmod 640 "${CA_CERT}"
|
|
}
|
|
|
|
main "$@"
|