A test server (acacia root server) is already included in the inventory, with an ecg admin account.
1699 lines
60 KiB
Bash
Executable File
1699 lines
60 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# vim: foldmarker={{{,}}}:foldmethod=marker
|
|
|
|
# pki-authority: CA-side PKI management
|
|
# Copyright (C) 2016 Maciej Delmanowski <drybjed@gmail.com>
|
|
# Copyright (C) 2016-2017 Robin Schneider <ypid@riseup.net>
|
|
# Copyright (C) 2016-2020 DebOps <https://debops.org/>
|
|
# SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
set -o nounset -o pipefail -o errexit
|
|
|
|
umask 022
|
|
|
|
script="$(basename "${0}")"
|
|
|
|
_openssl () {
|
|
# OpenSSL writes to stderr/stdout even when there are no errors. So just
|
|
# display the output if the exit code was != 0 to simplify debugging.
|
|
set +e
|
|
_openssl_out="$(openssl "${@}" 2>&1)"
|
|
local rc=$?
|
|
set -e
|
|
if [ ${rc} -ne 0 ]; then
|
|
echo "${script}: Error: failed to run $* (Exitcode: ${rc})" >&2
|
|
echo >&2
|
|
echo "Details:" >&2
|
|
echo "${_openssl_out}" >&2
|
|
exit ${rc}
|
|
fi
|
|
unset _openssl_out
|
|
return ${rc}
|
|
}
|
|
|
|
array_exists () {
|
|
# Check if an element is in an array
|
|
local array="$1[@]"
|
|
local seeking=${2}
|
|
local in=1
|
|
for element in "${!array}"; do
|
|
if [[ $element == "$seeking" ]]; then
|
|
in=0
|
|
break
|
|
fi
|
|
done
|
|
return $in
|
|
}
|
|
|
|
# Inlined in the following scripts: pki-authority, pki-realm {{{
|
|
version () {
|
|
# Normalize version numbers for comparison.
|
|
echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'
|
|
}
|
|
|
|
key_exists () {
|
|
# Check if an associative array has a specified key present
|
|
# shellcheck disable=SC2086
|
|
eval '[ ${'$1'[$2]+test_of_existence} ]'
|
|
}
|
|
|
|
get_absolute_path () {
|
|
# Get an absolute path to a file
|
|
if type python3 > /dev/null ; then
|
|
python3 -c 'import sys, os.path; print(os.path.abspath(sys.argv[1]))' "${1}"
|
|
else
|
|
python -c 'import sys, os.path; print(os.path.abspath(sys.argv[1]))' "${1}"
|
|
fi
|
|
}
|
|
|
|
get_relative_path () {
|
|
# Get a relative path to a file
|
|
if type python3 > /dev/null ; then
|
|
python3 -c 'import sys, os.path; print(os.path.relpath(sys.argv[1], sys.argv[2]))' "${1}" "${2:-$PWD}"
|
|
else
|
|
python -c 'import sys, os.path; print(os.path.relpath(sys.argv[1], sys.argv[2]))' "${1}" "${2:-$PWD}"
|
|
fi
|
|
}
|
|
|
|
get_dnsdomainname () {
|
|
# Wrap dnsdomainname if it's not present (on MacOS X)
|
|
|
|
if type dnsdomainname > /dev/null 2>&1 ; then
|
|
dnsdomainname
|
|
else
|
|
local fqdn
|
|
fqdn="$(get_fqdn)"
|
|
if [[ ${fqdn} == *.* ]] ; then
|
|
echo "${fqdn#*.}"
|
|
else
|
|
echo ""
|
|
fi
|
|
fi
|
|
}
|
|
|
|
get_fqdn () {
|
|
shopt -s nocasematch
|
|
case $OSTYPE in
|
|
*bsd* | dragonfly* ) hostname;;
|
|
* ) hostname -f;;
|
|
esac
|
|
}
|
|
|
|
chmod_idempotent () {
|
|
# chmod does a `fchmodat` (after a `stat`) even if it does not need to
|
|
# change anything as of 8.23-4.
|
|
# This behavior is not ideal for this script as it would update the ctime.
|
|
|
|
local new_mode_in_octal="${1##0}"
|
|
local file_path="${2}"
|
|
|
|
if [ "$(stat -c %a "${file_path}")" != "${new_mode_in_octal}" ] ; then
|
|
chmod "${new_mode_in_octal}" "${file_path}"
|
|
fi
|
|
}
|
|
|
|
chgrp_idempotent () {
|
|
# chgrp does a `fchownat` (after a `newfstatat`) even if it does not need
|
|
# to change anything as of 8.23-4.
|
|
# This behavior is not ideal for this script as it would update the ctime.
|
|
|
|
local new_group_name="${1}"
|
|
local file_path="${2}"
|
|
|
|
if [ "$(stat -c %G "${file_path}")" != "${new_group_name}" ] ; then
|
|
chgrp "${new_group_name}" "${file_path}"
|
|
fi
|
|
}
|
|
# }}}
|
|
|
|
get_openssl_ocsp_uri_directive () {
|
|
local ocsp_uri
|
|
case "${config['ocsp']}" in
|
|
true|True)
|
|
ocsp_uri="OCSP;URI.0 = \$ocsp_url"
|
|
;;
|
|
false|False)
|
|
ocsp_uri=""
|
|
;;
|
|
*)
|
|
ocsp_uri="OCSP;URI.0 = ${config['ocsp']}"
|
|
;;
|
|
esac
|
|
echo "$ocsp_uri"
|
|
}
|
|
|
|
get_openssl_name_constraints_directive () {
|
|
local config_domain="${1}"
|
|
local name_constraints
|
|
case "${config['name_constraints']}" in
|
|
true|True)
|
|
case "${config['name_constraints_critical']}" in
|
|
false|False)
|
|
name_constraints="nameConstraints = permitted;DNS:${config_domain},permitted;DNS:.${config_domain}"
|
|
;;
|
|
*)
|
|
name_constraints="nameConstraints = critical, permitted;DNS:${config_domain},permitted;DNS:.${config_domain}"
|
|
;;
|
|
esac
|
|
;;
|
|
false|False)
|
|
name_constraints=""
|
|
;;
|
|
*)
|
|
name_constraints="nameConstraints = ${config['name_constraints']}"
|
|
;;
|
|
esac
|
|
|
|
echo "$name_constraints"
|
|
}
|
|
|
|
get_openssl_crl_distribution_points_directive () {
|
|
local crl_distribution_points=''
|
|
case "${config['crl']}" in
|
|
true|True)
|
|
crl_distribution_points="crlDistributionPoints = @crl_info"
|
|
;;
|
|
false|False)
|
|
crl_distribution_points=""
|
|
;;
|
|
*)
|
|
crl_distribution_points="crlDistributionPoints = ${config['crl']}"
|
|
;;
|
|
esac
|
|
|
|
echo "$crl_distribution_points"
|
|
}
|
|
|
|
initialize_environment () {
|
|
|
|
declare -gA config
|
|
|
|
config["pki_root"]="${PKI_ROOT:-$(pwd)}"
|
|
config["pki_library"]="${PKI_LIBRARY:-openssl}"
|
|
config["pki_ca_certificates"]="${PKI_CA_CERTIFICATES:-${config['pki_root']}/ca-certificates/by-group/all}"
|
|
config["system_ca"]="true"
|
|
|
|
config["pki_authorities"]="${config['pki_root']}/authorities"
|
|
config["pki_requests"]="${config['pki_root']}/requests"
|
|
|
|
config["pki_default_fqdn"]="$(get_fqdn)"
|
|
config["pki_default_domain"]="$(get_dnsdomainname)"
|
|
config["pki_default_domain_dn"]=$(echo "${config['pki_default_domain']}" | cut -d. -f1 | sed 's/.*/\u&/')
|
|
|
|
config["domain"]=""
|
|
config["ca_type"]=""
|
|
config["issuer_name"]=""
|
|
config["alt_authority"]=""
|
|
config["name_constraints"]=""
|
|
|
|
config["pki_default_sign_base"]="365"
|
|
config["pki_default_root_sign_multiplier"]="12"
|
|
config["pki_default_ca_sign_multiplier"]="10"
|
|
config["pki_default_cert_sign_multiplier"]="3"
|
|
|
|
config["root_sign_days"]=""
|
|
config["ca_sign_days"]=""
|
|
config["cert_sign_days"]=""
|
|
config["key_size"]="4096"
|
|
config["crl"]="true"
|
|
config["ocsp"]="true"
|
|
|
|
config["public_dir_group"]="$(id -g)"
|
|
config["public_file_group"]="$(id -g)"
|
|
|
|
config["private_dir_group"]="$(id -g)"
|
|
config["private_file_group"]="$(id -g)"
|
|
|
|
config["public_dir_mode"]="755"
|
|
config["private_dir_mode"]="700"
|
|
|
|
config["public_file_mode"]="644"
|
|
config["private_file_mode"]="600"
|
|
|
|
# shellcheck disable=SC2174
|
|
test -d ${config['pki_root']} || mkdir -p -m ${config['public_dir_mode']} ${config['pki_root']}
|
|
}
|
|
|
|
enter_authority () {
|
|
|
|
local authority="${1}"
|
|
local config_file="${2:-config/authority.conf}"
|
|
|
|
mkdir -p "${config['pki_authorities']}/${authority}"
|
|
chmod_idempotent ${config['public_dir_mode']} "${config['pki_authorities']}/${authority}"
|
|
|
|
cd "${config['pki_authorities']}/${authority}"
|
|
local rc=$?
|
|
|
|
if [ -r "${config_file}" ] ; then
|
|
|
|
# shellcheck source=/dev/null
|
|
. "${config_file}"
|
|
fi
|
|
|
|
return ${rc}
|
|
}
|
|
|
|
update_symlink () {
|
|
|
|
local symlink_target="${1}"
|
|
|
|
if [ -n "${symlink_target}" ] ; then
|
|
|
|
shift 1
|
|
|
|
for target in "${@}" ; do
|
|
if [ -r "${target}" ] ; then
|
|
if [ -L "${symlink_target}" ] && [ "$(readlink "${symlink_target}")" == "${target}" ] ; then
|
|
break
|
|
else
|
|
ln -sf "${target}" "${symlink_target}"
|
|
fi
|
|
break
|
|
fi
|
|
done
|
|
|
|
fi
|
|
}
|
|
|
|
create_openssl_request_config () {
|
|
|
|
local config_file="${1:-config/openssl-request.conf}"
|
|
local config_dn="${2}"
|
|
|
|
if [ -n "${config_file}" ] && [ -n "${config_dn}" ] && [ ! -r "${config_file}" ] ; then
|
|
|
|
cat << EOF > "${config_file}"
|
|
# Configuration file generated by pki-authority
|
|
|
|
[ req ]
|
|
default_md = sha256
|
|
default_bits = ${config['key_size']}
|
|
default_keyfile = private/key.pem
|
|
prompt = no
|
|
encrypt_key = no
|
|
distinguished_name = ca_dn
|
|
utf8 = yes
|
|
string_mask = utf8only
|
|
|
|
[ ca_dn ]
|
|
EOF
|
|
echo "${config_dn}" | tr "/" "\\n" | sed \
|
|
-e 's/^[Cc]=/countryName=/' \
|
|
-e 's/^[Ss][Tt]=/stateOrProvinceName=/' \
|
|
-e 's/^[Ll]=/localityName=/' \
|
|
-e 's/^[Oo]=/organizationName=/' \
|
|
-e 's/^[Oo][Uu]=/organizationalUnitName=/' \
|
|
-e 's/^[Cc][Nn]=/commonName=/' >> "${config_file}"
|
|
|
|
fi
|
|
}
|
|
|
|
create_gnutls_request_config () {
|
|
|
|
local config_file="${1:-config/gnutls-request.conf}"
|
|
local config_dn="${2}"
|
|
|
|
if [ -n "${config_file}" ] && [ -n "${config_dn}" ] && [ ! -r "${config_file}" ] ; then
|
|
|
|
cat << EOF > "${config_file}"
|
|
# Configuration file generated by pki-authority
|
|
|
|
EOF
|
|
echo "${config_dn}" | tr "/" "\\n" | sed \
|
|
-e 's/^[Cc]=/country = "/' \
|
|
-e 's/^[Ss][Tt]=/state = "/' \
|
|
-e 's/^[Ll]=/locality = "/' \
|
|
-e 's/^[Oo]=/organization = "/' \
|
|
-e 's/^[Oo][Uu]=/unit = "/' \
|
|
-e 's/^[Cc][Nn]=/cn = "/' \
|
|
-e 's/$/"/' >> "${config_file}"
|
|
|
|
fi
|
|
}
|
|
|
|
create_openssl_selfsign_config () {
|
|
|
|
local config_file="${1:-config/openssl-selfsign.conf}"
|
|
local config_name="${2}"
|
|
local config_domain="${3}"
|
|
local config_ca_type="${4}"
|
|
|
|
if [ -n "${config_file}" ] && [ ! -r "${config_file}" ] ; then
|
|
local ocsp_uri
|
|
ocsp_uri="$(get_openssl_ocsp_uri_directive)"
|
|
|
|
cat << EOF > "${config_file}"
|
|
# Configuration file generated by pki-authority
|
|
|
|
[ default ]
|
|
name = ${config_name}
|
|
domain_suffix = ${config_domain}
|
|
aia_url = http://\$name.\$domain_suffix/crt/
|
|
crl_url = http://\$name.\$domain_suffix/crl/
|
|
ocsp_url = http://\$name.\$domain_suffix/ocsp/
|
|
default_ca = ca_default
|
|
name_opt = utf8,esc_ctrl,multiline,lname,align
|
|
|
|
[ ca_default ]
|
|
home = .
|
|
database = \$home/database/index
|
|
serial = \$home/database/serial
|
|
crlnumber = \$home/database/crlnumber
|
|
certificate = \$home/subject/cert.pem
|
|
private_key = \$home/private/key.pem
|
|
RANDFILE = \$home/private/random
|
|
new_certs_dir = \$home/certs
|
|
unique_subject = no
|
|
copy_extensions = none
|
|
default_days = ${config['root_sign_days']:-$(( config['pki_default_sign_base'] * config['pki_default_root_sign_multiplier'] ))}
|
|
default_crl_days = 365
|
|
default_md = sha256
|
|
policy = policy_default
|
|
x509_extensions = extension_default
|
|
|
|
[ crl_info ]
|
|
URI.0 = \$crl_url
|
|
|
|
[ issuer_info ]
|
|
caIssuers;URI.0 = \$aia_url
|
|
${ocsp_uri}
|
|
|
|
[ extension_ocsp ]
|
|
authorityKeyIdentifier = keyid:always
|
|
basicConstraints = critical, CA:false
|
|
extendedKeyUsage = OCSPSigning
|
|
keyUsage = critical, digitalSignature
|
|
subjectKeyIdentifier = hash
|
|
|
|
[ policy_default ]
|
|
countryName = optional
|
|
stateOrProvinceName = optional
|
|
organizationName = optional
|
|
organizationalUnitName = optional
|
|
commonName = optional
|
|
emailAddress = optional
|
|
|
|
[ extension_default ]
|
|
EOF
|
|
|
|
local name_constraints=''
|
|
name_constraints="$(get_openssl_name_constraints_directive "$config_domain")"
|
|
local crl_distribution_points=''
|
|
crl_distribution_points="$(get_openssl_crl_distribution_points_directive)"
|
|
if [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "root" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
basicConstraints = critical, CA:TRUE
|
|
keyUsage = critical, keyCertSign, cRLSign
|
|
subjectKeyIdentifier = hash
|
|
${name_constraints}
|
|
|
|
EOF
|
|
elif [ "${config_ca_type}" = "service" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
authorityInfoAccess = @issuer_info
|
|
basicConstraints = critical, CA:TRUE, pathlen:0
|
|
${crl_distribution_points}
|
|
keyUsage = critical, keyCertSign, cRLSign
|
|
subjectKeyIdentifier = hash
|
|
${name_constraints}
|
|
|
|
EOF
|
|
fi
|
|
|
|
fi
|
|
}
|
|
|
|
create_gnutls_selfsign_config () {
|
|
|
|
local config_file="${1:-config/gnutls-selfsign.conf}"
|
|
local config_name="${2}"
|
|
local config_domain="${3}"
|
|
local config_ca_type="${4}"
|
|
|
|
if [ -n "${config_file}" ] && [ ! -r "${config_file}" ] ; then
|
|
|
|
cat << EOF > "${config_file}"
|
|
# Configuration file generated by pki-authority
|
|
|
|
expiration_days = ${config['root_sign_days']:-$(( config['pki_default_sign_base'] * config['pki_default_root_sign_multiplier'] ))}
|
|
|
|
EOF
|
|
|
|
if [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "root" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
ca
|
|
cert_signing_key
|
|
crl_signing_key
|
|
|
|
EOF
|
|
elif [ "${config_ca_type}" = "service" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
ca
|
|
cert_signing_key
|
|
crl_signing_key
|
|
path_len = 0
|
|
|
|
#ca_issuers_uri = "http://${config_name}.${config_domain}/crt/"
|
|
crl_dist_points = "http://${config_name}.${config_domain}/crl/"
|
|
#ocsp_uri = "http://${config_name}.${config_domain}/ocsp/"
|
|
|
|
EOF
|
|
fi
|
|
|
|
fi
|
|
}
|
|
|
|
create_openssl_sign_config () {
|
|
|
|
local config_file="${1:-config/openssl-sign.conf}"
|
|
local config_name="${2}"
|
|
local config_domain="${3}"
|
|
local config_ca_type="${4}"
|
|
local config_issuer="${5}"
|
|
|
|
if [ -n "${config_file}" ] && [ ! -r "${config_file}" ] ; then
|
|
local ocsp_uri
|
|
ocsp_uri="$(get_openssl_ocsp_uri_directive)"
|
|
|
|
cat << EOF > "${config_file}"
|
|
# Configuration file generated by pki-authority
|
|
|
|
[ default ]
|
|
name = ${config_name}
|
|
domain_suffix = ${config_domain}
|
|
aia_url = http://\$name.\$domain_suffix/crt/
|
|
crl_url = http://\$name.\$domain_suffix/crl/
|
|
ocsp_url = http://\$name.\$domain_suffix/ocsp/
|
|
default_ca = ca_default
|
|
name_opt = utf8,esc_ctrl,multiline,lname,align
|
|
|
|
[ ca_default ]
|
|
home = .
|
|
database = \$home/database/index
|
|
serial = \$home/database/serial
|
|
crlnumber = \$home/database/crlnumber
|
|
certificate = \$home/subject/cert.pem
|
|
private_key = \$home/private/key.pem
|
|
RANDFILE = \$home/private/random
|
|
new_certs_dir = \$home/certs
|
|
unique_subject = no
|
|
policy = policy_default
|
|
x509_extensions = extension_default
|
|
EOF
|
|
|
|
if [ -z "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "root" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
copy_extensions = none
|
|
default_days = ${config['ca_sign_days']:-$(( config['pki_default_sign_base'] * config['pki_default_ca_sign_multiplier'] ))}
|
|
default_crl_days = 365
|
|
default_md = sha256
|
|
|
|
EOF
|
|
elif [ -z "${config_issuer}" ] && [ "${config_ca_type}" = "service" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
copy_extensions = copy
|
|
default_days = ${config['cert_sign_days']:-$(( config['pki_default_sign_base'] * config['pki_default_cert_sign_multiplier'] ))}
|
|
default_crl_days = 365
|
|
default_md = sha256
|
|
|
|
EOF
|
|
elif [ -n "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "server" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
copy_extensions = copy
|
|
default_days = ${config['cert_sign_days']:-$(( config['pki_default_sign_base'] * config['pki_default_cert_sign_multiplier'] ))}
|
|
default_crl_days = 30
|
|
default_md = sha256
|
|
|
|
EOF
|
|
fi
|
|
|
|
cat << EOF >> "${config_file}"
|
|
[ crl_info ]
|
|
URI.0 = \$crl_url
|
|
|
|
[ issuer_info ]
|
|
caIssuers;URI.0 = \$aia_url
|
|
${ocsp_uri}
|
|
|
|
[ extension_ocsp ]
|
|
authorityKeyIdentifier = keyid:always
|
|
basicConstraints = critical, CA:false
|
|
extendedKeyUsage = OCSPSigning
|
|
keyUsage = critical, digitalSignature
|
|
subjectKeyIdentifier = hash
|
|
|
|
EOF
|
|
|
|
if [ -z "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "root" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
[ policy_default ]
|
|
countryName = optional
|
|
stateOrProvinceName = optional
|
|
organizationName = optional
|
|
organizationalUnitName = optional
|
|
commonName = optional
|
|
emailAddress = optional
|
|
|
|
EOF
|
|
elif [ -z "${config_issuer}" ] && [ "${config_ca_type}" = "service" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
[ policy_default ]
|
|
countryName = optional
|
|
stateOrProvinceName = optional
|
|
organizationName = optional
|
|
organizationalUnitName = optional
|
|
commonName = optional
|
|
emailAddress = optional
|
|
|
|
EOF
|
|
elif [ -n "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "server" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
[ policy_default ]
|
|
countryName = optional
|
|
stateOrProvinceName = optional
|
|
organizationName = optional
|
|
organizationalUnitName = optional
|
|
commonName = optional
|
|
emailAddress = optional
|
|
|
|
EOF
|
|
fi
|
|
|
|
local name_constraints=''
|
|
name_constraints="$(get_openssl_name_constraints_directive "$config_domain")"
|
|
local crl_distribution_points=''
|
|
crl_distribution_points="$(get_openssl_crl_distribution_points_directive)"
|
|
if [ -z "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "root" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
[ extension_default ]
|
|
authorityInfoAccess = @issuer_info
|
|
authorityKeyIdentifier = keyid:always
|
|
basicConstraints = critical, CA:TRUE, pathlen:0
|
|
${crl_distribution_points}
|
|
keyUsage = critical, keyCertSign, cRLSign
|
|
subjectKeyIdentifier = hash
|
|
${name_constraints}
|
|
|
|
EOF
|
|
elif [ -z "${config_issuer}" ] && [ "${config_ca_type}" = "service" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
[ extension_default ]
|
|
authorityInfoAccess = @issuer_info
|
|
authorityKeyIdentifier = keyid:always, issuer:always
|
|
basicConstraints = critical, CA:FALSE
|
|
${crl_distribution_points}
|
|
extendedKeyUsage = clientAuth, serverAuth
|
|
keyUsage = critical, digitalSignature, keyEncipherment
|
|
subjectKeyIdentifier = hash
|
|
|
|
EOF
|
|
elif [ -n "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "server" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
[ extension_default ]
|
|
authorityInfoAccess = @issuer_info
|
|
authorityKeyIdentifier = keyid:always, issuer:always
|
|
basicConstraints = critical, CA:FALSE
|
|
${crl_distribution_points}
|
|
extendedKeyUsage = clientAuth, serverAuth
|
|
keyUsage = critical, digitalSignature, keyEncipherment
|
|
subjectKeyIdentifier = hash
|
|
|
|
EOF
|
|
fi
|
|
|
|
fi
|
|
}
|
|
|
|
create_gnutls_sign_config () {
|
|
|
|
local config_file="${1:-config/gnutls-sign.conf}"
|
|
local config_name="${2}"
|
|
local config_domain="${3}"
|
|
local config_ca_type="${4}"
|
|
local config_issuer="${5}"
|
|
|
|
if [ -n "${config_file}" ] && [ ! -r "${config_file}" ] ; then
|
|
|
|
cat << EOF > "${config_file}"
|
|
# Configuration file generated by pki-authority
|
|
|
|
EOF
|
|
|
|
if [ -z "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "root" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
expiration_days = ${config['ca_sign_days']:-$(( config['pki_default_sign_base'] * config['pki_default_ca_sign_multiplier'] ))}
|
|
|
|
ca
|
|
cert_signing_key
|
|
crl_signing_key
|
|
path_len = 0
|
|
|
|
#ca_issuers_uri = "http://${config_name}.${config_domain}/crt/"
|
|
crl_dist_points = "http://${config_name}.${config_domain}/crl/"
|
|
#ocsp_uri = "http://${config_name}.${config_domain}/ocsp/"
|
|
|
|
EOF
|
|
elif [ -z "${config_issuer}" ] && [ "${config_ca_type}" = "service" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
expiration_days = ${config['cert_sign_days']:-$(( config['pki_default_sign_base'] * config['pki_default_cert_sign_multiplier'] ))}
|
|
#copy_extensions = copy
|
|
|
|
signing_key
|
|
encryption_key
|
|
tls_www_client
|
|
tls_www_server
|
|
|
|
#ca_issuers_uri = "http://${config_name}.${config_domain}/crt/"
|
|
crl_dist_points = "http://${config_name}.${config_domain}/crl/"
|
|
#ocsp_uri = "http://${config_name}.${config_domain}/ocsp/"
|
|
|
|
EOF
|
|
elif [ -n "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "server" ] ; then
|
|
cat << EOF >> "${config_file}"
|
|
expiration_days = ${config['cert_sign_days']:-$(( config['pki_default_sign_base'] * config['pki_default_cert_sign_multiplier'] ))}
|
|
#copy_extensions = copy
|
|
|
|
signing_key
|
|
encryption_key
|
|
tls_www_client
|
|
tls_www_server
|
|
|
|
#ca_issuers_uri = "http://${config_name}.${config_domain}/crt/"
|
|
crl_dist_points = "http://${config_name}.${config_domain}/crl/"
|
|
#ocsp_uri = "http://${config_name}.${config_domain}/ocsp/"
|
|
|
|
EOF
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
save_authority_config () {
|
|
|
|
local config_file="${1:-config/authority.conf}"
|
|
|
|
# shellcheck disable=SC2034
|
|
local ignored_config_vars=( pki_root pki_requests pki_authorities pki_ca_certificates public_file_group public_dir_group private_file_group private_dir_group )
|
|
|
|
if [ -r "${config_file}" ] ; then
|
|
if grep -q -E "^#\\s+Configuration\\s+file\\s+generated\\s+by\\s+pki-authority$" "${config_file}" ; then
|
|
rm -f "${config_file}"
|
|
fi
|
|
fi
|
|
|
|
if [ ! -r "${config_file}" ] ; then
|
|
cat << EOF > "${config_file}"
|
|
# Configuration file generated by pki-authority
|
|
|
|
EOF
|
|
for key in "${!config[@]}" ; do
|
|
if ! array_exists ignored_config_vars "${key}" ; then
|
|
echo "config['${key}']='${config[${key}]}'" >> "${config_file}"
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
check_openssl_req_session_match () {
|
|
local check_req="${1}"
|
|
local check_session="${PKI_SESSION_TOKEN:-}"
|
|
|
|
if [ -n "${check_req}" ] && [ -n "${check_session}" ] ; then
|
|
if [ -r "${check_req}" ] ; then
|
|
openssl asn1parse -in "${check_req}" | grep -q "${check_session}"
|
|
rc=$?
|
|
return ${rc}
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
check_gnutls_req_session_match () {
|
|
return "$(check_openssl_req_session_match "${@}")"
|
|
}
|
|
|
|
check_openssl_crt_ca_match () {
|
|
local check_crt="${1}"
|
|
local check_ca="${2}"
|
|
local check_ca_issuer="${3:-}"
|
|
|
|
if [ -n "${check_crt}" ] && [ -n "${check_ca}" ] ; then
|
|
if [ -r "${check_crt}" ] && [ -r "${check_ca}" ] ; then
|
|
if [ -n "${check_ca_issuer}" ] && [ -r "${check_ca_issuer}" ] ; then
|
|
_openssl verify -CAfile "${check_ca_issuer}" -untrusted "${check_ca}" "${check_crt}"
|
|
local rc=$?
|
|
else
|
|
_openssl verify -CAfile "${check_ca}" "${check_crt}"
|
|
local rc=$?
|
|
fi
|
|
return ${rc}
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
check_gnutls_crt_ca_match () {
|
|
return "$(check_openssl_crt_ca_match "${@}")"
|
|
}
|
|
|
|
check_openssl_req_crt_match () {
|
|
local check_req="${1}"
|
|
local check_crt="${2}"
|
|
|
|
local req_modulus
|
|
local crt_modulus
|
|
|
|
req_modulus="$(openssl req -noout -modulus -in "${check_req}" | base64)"
|
|
crt_modulus="$(openssl x509 -noout -modulus -in "${check_crt}" | base64)"
|
|
|
|
if [ "${req_modulus}" = "${crt_modulus}" ] ; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_gnutls_req_crt_match () {
|
|
return "$(check_openssl_req_crt_match "${@}")"
|
|
}
|
|
|
|
sign_openssl_certificate () {
|
|
local sign_config="${1}"
|
|
local sign_request="${2}"
|
|
local sign_out="${3}"
|
|
local sign_sect="${4}"
|
|
local sign_ext="${5}"
|
|
|
|
if [ -n "${sign_config}" ] && [ -n "${sign_request}" ] && [ -n "${sign_out}" ] ; then
|
|
|
|
if [ -r "${sign_request}" ] && [ ! -r "${sign_out}" ] ; then
|
|
|
|
_openssl ca -batch -notext \
|
|
-in "${sign_request}" -out "${sign_out}.tmp" \
|
|
-name "${sign_sect}" -extensions "${sign_ext}" \
|
|
-config "${sign_config}"
|
|
|
|
if [ -r "${sign_out}.tmp" ] && [ -s "${sign_out}.tmp" ] ; then
|
|
mv "${sign_out}.tmp" "${sign_out}"
|
|
fi
|
|
|
|
if [ -d issuer ] ; then
|
|
if [ -r "$(dirname "${sign_out}")/intermediate.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem"
|
|
fi
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
sign_openssl_host_certificate () {
|
|
local sign_config="${1}"
|
|
local sign_request="${2}"
|
|
local sign_out="${3}"
|
|
local sign_alt="${4:-}"
|
|
|
|
if [ -n "${sign_config}" ] && [ -n "${sign_request}" ] && [ -n "${sign_out}" ] ; then
|
|
|
|
if [ -r "${sign_request}" ] && [ ! -r "${sign_out}" ] ; then
|
|
|
|
_openssl ca -batch -notext \
|
|
-in "${sign_request}" -out "${sign_out}.tmp" \
|
|
-config "${sign_config}"
|
|
|
|
if [ -r "${sign_out}.tmp" ] && [ -s "${sign_out}.tmp" ] ; then
|
|
mv "${sign_out}.tmp" "${sign_out}"
|
|
fi
|
|
|
|
if [ -d issuer ] ; then
|
|
if [ -r "$(dirname "${sign_out}")/intermediate.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem"
|
|
fi
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "${sign_alt}" ] ; then
|
|
|
|
if [ -d "${sign_alt}/issuer" ] ; then
|
|
if [ -r "$(dirname "${sign_out}")/alt_intermediate.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_intermediate.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_intermediate.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_intermediate.pem"
|
|
fi
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "${sign_alt}/issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "${sign_alt}/issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "${sign_alt}/issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem"
|
|
fi
|
|
else
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem"
|
|
fi
|
|
fi
|
|
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
sign_gnutls_host_certificate () {
|
|
local sign_config="${1}"
|
|
local sign_request="${2}"
|
|
local sign_out="${3}"
|
|
local sign_alt="${4:-}"
|
|
|
|
if [ -n "${sign_config}" ] && [ -n "${sign_request}" ] && [ -n "${sign_out}" ] ; then
|
|
|
|
if [ -r "${sign_request}" ] && [ ! -r "${sign_out}" ] ; then
|
|
|
|
certtool --generate-certificate --template "${sign_config}" \
|
|
--load-request "${sign_request}" --load-ca-privkey private/key.pem \
|
|
--load-ca-certificate subject/cert.pem --outfile "${sign_out}.tmp"
|
|
|
|
if [ -r "${sign_out}.tmp" ] && [ -s "${sign_out}.tmp" ] ; then
|
|
mv "${sign_out}.tmp" "${sign_out}"
|
|
fi
|
|
|
|
if [ -d issuer ] ; then
|
|
if [ -r "$(dirname "${sign_out}")/intermediate.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/intermediate.pem"
|
|
fi
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/root.pem"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "${sign_alt}" ] ; then
|
|
|
|
if [ -d "${sign_alt}/issuer" ] ; then
|
|
if [ -r "$(dirname "${sign_out}")/alt_intermediate.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_intermediate.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_intermediate.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_intermediate.pem"
|
|
fi
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "${sign_alt}/issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "${sign_alt}/issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "${sign_alt}/issuer/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem"
|
|
fi
|
|
else
|
|
if [ -r "$(dirname "${sign_out}")/root.pem" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "${sign_alt}/subject/cert.pem" "$(dirname "${sign_out}")")" "$(dirname "${sign_out}")/alt_root.pem"
|
|
fi
|
|
fi
|
|
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
sign_openssl_ca_certificate () {
|
|
local sign_config="${1}"
|
|
local sign_request="${2}"
|
|
local sign_out="${3}"
|
|
|
|
if [ -n "${sign_config}" ] && [ -n "${sign_request}" ] && [ -n "${sign_out}" ] ; then
|
|
if [ -r "${sign_request}" ] && [ ! -r "${sign_out}" ] ; then
|
|
|
|
_openssl ca -batch -notext \
|
|
-in "${sign_request}" -out "${sign_out}.tmp" \
|
|
-config "${sign_config}"
|
|
|
|
if [ -r "${sign_out}.tmp" ] && [ -s "${sign_out}.tmp" ] ; then
|
|
mv "${sign_out}.tmp" "${sign_out}"
|
|
fi
|
|
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
sign_gnutls_ca_certificate () {
|
|
local sign_config="${1}"
|
|
local sign_request="${2}"
|
|
local sign_out="${3}"
|
|
|
|
if [ -n "${sign_config}" ] && [ -n "${sign_request}" ] && [ -n "${sign_out}" ] ; then
|
|
if [ -r "${sign_request}" ] && [ ! -r "${sign_out}" ] ; then
|
|
|
|
certtool --generate-certificate --template "${sign_config}" \
|
|
--load-request "${sign_request}" --load-ca-privkey private/key.pem \
|
|
--load-ca-certificate subject/cert.pem --outfile "${sign_out}.tmp"
|
|
|
|
if [ -r "${sign_out}.tmp" ] && [ -s "${sign_out}.tmp" ] ; then
|
|
mv "${sign_out}.tmp" "${sign_out}"
|
|
fi
|
|
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
sign_openssl_root_certificate () {
|
|
local sign_config="${1}"
|
|
local sign_request="${2}"
|
|
local sign_out="${3}"
|
|
|
|
if [ -n "${sign_config}" ] && [ -n "${sign_request}" ] && [ -n "${sign_out}" ] ; then
|
|
if [ -r "${sign_request}" ] && [ ! -r "${sign_out}" ] ; then
|
|
|
|
_openssl ca -selfsign -batch -notext \
|
|
-in "${sign_request}" -out "${sign_out}.tmp" \
|
|
-config "${sign_config}"
|
|
|
|
if [ -r "${sign_out}.tmp" ] && [ -s "${sign_out}.tmp" ] ; then
|
|
mv "${sign_out}.tmp" "${sign_out}"
|
|
fi
|
|
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
sign_gnutls_root_certificate () {
|
|
local sign_config="${1}"
|
|
local sign_request="${2}"
|
|
local sign_out="${3}"
|
|
|
|
if [ -n "${sign_config}" ] && [ -n "${sign_request}" ] && [ -n "${sign_out}" ] ; then
|
|
if [ -r "${sign_request}" ] && [ ! -r "${sign_out}" ] ; then
|
|
|
|
certtool --generate-self-signed --template "${sign_config}" \
|
|
--load-request "${sign_request}" --load-privkey private/key.pem \
|
|
--outfile "${sign_out}.tmp"
|
|
|
|
if [ -r "${sign_out}.tmp" ] && [ -s "${sign_out}.tmp" ] ; then
|
|
mv "${sign_out}.tmp" "${sign_out}"
|
|
fi
|
|
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
generate_openssl_request () {
|
|
|
|
local req_config="${1}"
|
|
local req_out="${2}"
|
|
|
|
if [ -n "${req_config}" ] && [ -n "${req_out}" ] ; then
|
|
if [ -r "${req_config}" ] && [ ! -r "${req_out}" ] ; then
|
|
|
|
local req_keyfile
|
|
req_keyfile=$(grep -E '^default_keyfile\s+=\s+' "${req_config}" | awk '{print $3}')
|
|
if [ -n "${req_keyfile}" ] ; then
|
|
openssl req -new -key "${req_keyfile}" -config "${req_config}" -out "${req_out}.tmp"
|
|
else
|
|
openssl req -new -config "${req_config}" -out "${req_out}.tmp"
|
|
fi
|
|
test -r "${req_out}.tmp" && mv "${req_out}.tmp" "${req_out}"
|
|
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
generate_gnutls_request () {
|
|
|
|
local req_config="${1}"
|
|
local req_out="${2}"
|
|
|
|
if [ -n "${req_config}" ] && [ -n "${req_out}" ] ; then
|
|
if [ -r "${req_config}" ] && [ ! -r "${req_out}" ] ; then
|
|
|
|
certtool --generate-request --template "${req_config}" \
|
|
--load-privkey private/key.pem --outfile "${req_out}.tmp"
|
|
# Remove text output from the request
|
|
test -r "${req_out}.tmp" && sed -n -i '/-----BEGIN NEW CERTIFICATE REQUEST-----/,$ p' "${req_out}.tmp"
|
|
test -r "${req_out}.tmp" && mv "${req_out}.tmp" "${req_out}"
|
|
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
generate_openssl_serial () {
|
|
local serial_file="${1}"
|
|
local serial_length="${2:-16}"
|
|
|
|
if [ -n "${serial_file}" ] && [ ! -r "${serial_file}" ] ; then
|
|
openssl rand -hex "${serial_length}" > "${serial_file}"
|
|
fi
|
|
}
|
|
|
|
generate_gnutls_serial () {
|
|
# GnuTLS doesn't use this, so do nothing
|
|
return 0
|
|
}
|
|
|
|
generate_openssl_rsa_private_key () {
|
|
|
|
local key_file="${1:-private/key.pem}"
|
|
local key_size="${2:-${config['key_size']}}"
|
|
local key_group="${3:-${config['private_file_group']}}"
|
|
|
|
test -r "${key_file}" || openssl genrsa -out "${key_file}.tmp" "${key_size}"
|
|
|
|
if [ -r "${key_file}.tmp" ] ; then
|
|
chmod "${config['private_file_mode']}" "${key_file}.tmp"
|
|
chgrp "${key_group}" "${key_file}.tmp"
|
|
mv "${key_file}.tmp" "${key_file}"
|
|
fi
|
|
|
|
chmod_idempotent ${config['private_file_mode']} "${key_file}"
|
|
chgrp_idempotent "${key_group}" "${key_file}"
|
|
|
|
}
|
|
|
|
generate_gnutls_rsa_private_key () {
|
|
|
|
local key_file="${1:-private/key.pem}"
|
|
local key_size="${2:-${config['key_size']}}"
|
|
local key_group="${3:-${config['private_file_group']}}"
|
|
|
|
if ! [ -r "${key_file}" ] ; then
|
|
if [ "$(version "$(certtool --version | head -n 1 | awk '{print $NF}')")" -lt "$(version 3.3.0)" ] ; then
|
|
certtool --generate-privkey --outfile "${key_file}.tmp" --bits "${key_size}"
|
|
else
|
|
certtool --generate-privkey --rsa --outfile "${key_file}.tmp" --bits "${key_size}"
|
|
fi
|
|
fi
|
|
|
|
if [ -r "${key_file}.tmp" ] ; then
|
|
sed -n -i '/-----BEGIN RSA PRIVATE KEY-----/,$ p' "${key_file}.tmp"
|
|
chmod "${config['private_file_mode']}" "${key_file}.tmp"
|
|
chgrp "${key_group}" "${key_file}.tmp"
|
|
mv "${key_file}.tmp" "${key_file}"
|
|
fi
|
|
|
|
chmod_idempotent "${config['private_file_mode']}" "${key_file}"
|
|
chgrp_idempotent "${key_group}" "${key_file}"
|
|
|
|
}
|
|
|
|
create_public_directories () {
|
|
|
|
local dir_group="${1}"
|
|
if [ -n "${dir_group}" ] ; then
|
|
shift
|
|
else
|
|
return 1
|
|
fi
|
|
|
|
local directories
|
|
read -r -a directories <<< "${@}"
|
|
|
|
for directory in "${directories[@]}" ; do
|
|
mkdir -p "${directory}"
|
|
chmod_idempotent "${config['public_dir_mode']}" "${directory}"
|
|
chgrp_idempotent "${dir_group}" "${directory}"
|
|
done
|
|
|
|
}
|
|
|
|
create_private_directories () {
|
|
|
|
local dir_group="${1}"
|
|
if [ -n "${dir_group}" ] ; then
|
|
shift
|
|
else
|
|
return 1
|
|
fi
|
|
|
|
local directories
|
|
read -r -a directories <<< "${@}"
|
|
|
|
for directory in "${directories[@]}" ; do
|
|
mkdir -p "${directory}"
|
|
chmod_idempotent "${config['private_dir_mode']}" "${directory}"
|
|
chgrp_idempotent "${dir_group}" "${directory}"
|
|
done
|
|
|
|
}
|
|
|
|
sub_sign () {
|
|
|
|
local -A args
|
|
|
|
local optspec=":hn-:"
|
|
while getopts "${optspec}" optchar; do
|
|
case "${optchar}" in
|
|
-)
|
|
case "${OPTARG}" in
|
|
name)
|
|
args["name"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
name=*)
|
|
args["name"]=${OPTARG#*=}
|
|
;;
|
|
req)
|
|
args["req"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
req=*)
|
|
args["req"]=${OPTARG#*=}
|
|
;;
|
|
out)
|
|
args["out"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
out=*)
|
|
args["out"]=${OPTARG#*=}
|
|
;;
|
|
*)
|
|
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
|
|
echo "Unknown option --${OPTARG}" >&2
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
h)
|
|
echo "usage: ${script} init <-n|--name[=]authority>" >&2
|
|
exit 2
|
|
;;
|
|
n)
|
|
args["name"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
*)
|
|
if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
|
|
echo "Non-option argument: '-${OPTARG}'" >&2
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
local name=""
|
|
local req=""
|
|
local out=""
|
|
|
|
if key_exists args "name" ; then
|
|
|
|
enter_authority "${args['name']}"
|
|
|
|
local library="${config['pki_library']}"
|
|
local input
|
|
local output
|
|
input="$(get_absolute_path ${args['req']})"
|
|
output="$(get_absolute_path ${args['out']})"
|
|
|
|
sign_${library}_certificate "config/${library}-sign.conf" "${input}" "${output}"
|
|
|
|
fi
|
|
}
|
|
|
|
sub_sign-by-host () {
|
|
|
|
if [ $# -gt 0 ] ; then
|
|
if [ -d requests ] && [ -d realms ] ; then
|
|
for authority in requests/* ; do
|
|
if [ -r "authorities/$(basename "${authority}")/subject/cert.pem" ] ; then
|
|
for host in "${authority}"/* ; do
|
|
for play_host in "${@}" ; do
|
|
if [ "$(basename "${host}")" = "${play_host}" ] ; then
|
|
for realm in "${host}"/* ; do
|
|
if [ -r "${realm}/request.pem" ] ; then
|
|
|
|
local input
|
|
local output
|
|
input="$(get_absolute_path "${realm}"/request.pem)"
|
|
output="$(get_absolute_path "realms/by-host/$(basename "${play_host}")/$(basename "${realm}")/internal/cert.pem")"
|
|
|
|
enter_authority "$(basename "${authority}")"
|
|
|
|
local alt_authority=""
|
|
|
|
if [ -n "${config['alt_authority']}" ] && [ -d ../${config['alt_authority']} ] ; then
|
|
alt_authority="$(get_absolute_path ../${config['alt_authority']})"
|
|
fi
|
|
|
|
if [ -r "${output}" ] && check_${config['pki_library']}_req_session_match "${input}" ; then
|
|
rm -f "${output}"
|
|
fi
|
|
|
|
if [ -r "${output}" ] && ! check_${config['pki_library']}_req_crt_match "${input}" "${output}" ; then
|
|
rm -f "${output}"
|
|
if ! check_${config['pki_library']}_req_session_match "${input}" ; then
|
|
rm -f "${input}"
|
|
fi
|
|
fi
|
|
|
|
if [ -r "${output}" ] && ! check_${config['pki_library']}_crt_ca_match "${output}" "subject/cert.pem" "issuer/subject/cert.pem" ; then
|
|
rm -f "${output}"
|
|
if ! check_${config['pki_library']}_req_session_match "${input}" ; then
|
|
rm -f "${input}"
|
|
fi
|
|
fi
|
|
|
|
if [ ! -r "${output}" ] && ! check_${config['pki_library']}_req_session_match "${input}" ; then
|
|
rm -f "${input}"
|
|
fi
|
|
|
|
if [ -r "${input}" ] ; then
|
|
sign_${config['pki_library']}_host_certificate "config/${config['pki_library']}-sign.conf" "${input}" "${output}" "${alt_authority}"
|
|
fi
|
|
|
|
cd - > /dev/null
|
|
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
done
|
|
fi
|
|
done
|
|
fi
|
|
else
|
|
cat << EOF >&2
|
|
Usage: ${script} sign-by-host [host1 host2 ...]
|
|
|
|
signs certificates for specified hosts and writes them to the secret/pki subdirectory
|
|
EOF
|
|
exit 1
|
|
fi
|
|
|
|
}
|
|
|
|
sub_new-ca () {
|
|
|
|
local -A args
|
|
|
|
local optspec=":hn-:"
|
|
while getopts "${optspec}" optchar; do
|
|
case "${optchar}" in
|
|
-)
|
|
case "${OPTARG}" in
|
|
name)
|
|
args["name"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
name=*)
|
|
args["name"]=${OPTARG#*=}
|
|
;;
|
|
system-ca)
|
|
# shellcheck disable=SC2154
|
|
args["system_ca"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
system-ca=*)
|
|
args["system_ca"]=${OPTARG#*=}
|
|
;;
|
|
alt-authority)
|
|
args["alt_authority"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
alt-authority=*)
|
|
args["alt_authority"]=${OPTARG#*=}
|
|
;;
|
|
library)
|
|
args["pki_library"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
library=*)
|
|
args["pki_library"]=${OPTARG#*=}
|
|
;;
|
|
subdomain)
|
|
# shellcheck disable=SC2154
|
|
args["subdomain"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
subdomain=*)
|
|
args["subdomain"]=${OPTARG#*=}
|
|
;;
|
|
subject)
|
|
# shellcheck disable=SC2154
|
|
args["subject"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
subject=*)
|
|
args["subject"]=${OPTARG#*=}
|
|
;;
|
|
type)
|
|
# shellcheck disable=SC2154
|
|
args["ca_type"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
type=*)
|
|
args["ca_type"]=${OPTARG#*=}
|
|
;;
|
|
issuer-name)
|
|
# shellcheck disable=SC2154
|
|
args["issuer_name"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
issuer-name=*)
|
|
args["issuer_name"]=${OPTARG#*=}
|
|
;;
|
|
domain)
|
|
# shellcheck disable=SC2154
|
|
args["domain"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
domain=*)
|
|
args["domain"]=${OPTARG#*=}
|
|
;;
|
|
root-sign-days)
|
|
args["root_sign_days"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
root-sign-days=*)
|
|
args["root_sign_days"]=${OPTARG#*=}
|
|
;;
|
|
ca-sign-days)
|
|
args["ca_sign_days"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
ca-sign-days=*)
|
|
args["ca_sign_days"]=${OPTARG#*=}
|
|
;;
|
|
cert-sign-days)
|
|
args["cert_sign_days"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
cert-sign-days=*)
|
|
args["cert_sign_days"]=${OPTARG#*=}
|
|
;;
|
|
key-size)
|
|
args["key_size"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
key-size=*)
|
|
args["key_size"]=${OPTARG#*=}
|
|
;;
|
|
crl)
|
|
args["crl"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
crl=*)
|
|
args["crl"]=${OPTARG#*=}
|
|
;;
|
|
ocsp)
|
|
args["ocsp"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
ocsp=*)
|
|
args["ocsp"]=${OPTARG#*=}
|
|
;;
|
|
name-constraints)
|
|
args["name_constraints"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
name-constraints=*)
|
|
args["name_constraints"]=${OPTARG#*=}
|
|
;;
|
|
name-constraints-critical)
|
|
args["name_constraints_critical"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
name-constraints-critical=*)
|
|
args["name_constraints_critical"]=${OPTARG#*=}
|
|
;;
|
|
*)
|
|
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
|
|
echo "Unknown option --${OPTARG}" >&2
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
h)
|
|
echo "usage: ${script} new-ca <-n|--name[=]authority>" >&2
|
|
exit 2
|
|
;;
|
|
n)
|
|
args["name"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
*)
|
|
if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
|
|
echo "Non-option argument: '-${OPTARG}'" >&2
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if key_exists args "name" ; then
|
|
|
|
enter_authority "${args['name']}"
|
|
|
|
local config_changed="false"
|
|
|
|
for key in "${!args[@]}" ; do
|
|
if [ -n "${args[${key}]+x}" ] ; then
|
|
if key_exists config "${key}" ; then
|
|
if [ "${config[${key}]}" != "${args[${key}]}" ] ; then
|
|
local config_changed="true"
|
|
config["${key}"]="${args[${key}]}"
|
|
fi
|
|
else
|
|
local config_changed="true"
|
|
config["${key}"]="${args[${key}]}"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
create_public_directories ${config['public_dir_group']} config database subject certs requests signed
|
|
create_private_directories ${config['private_dir_group']} private
|
|
|
|
if [ "${config_changed}" = "true" ] ; then
|
|
save_authority_config config/authority.conf
|
|
fi
|
|
|
|
local name="${config['name']}"
|
|
local library="${config['pki_library']}"
|
|
|
|
generate_${library}_rsa_private_key private/key.pem
|
|
|
|
create_${library}_request_config config/${library}-request.conf "${config['subject']}"
|
|
if [ -z "${config['issuer_name']}" ] ; then
|
|
create_${library}_selfsign_config config/${library}-selfsign.conf "${config['subdomain']}" "${config['domain']}" "${config['ca_type']}"
|
|
fi
|
|
create_${library}_sign_config config/${library}-sign.conf "${config['subdomain']}" "${config['domain']}" "${config['ca_type']}" "${config['issuer_name']}"
|
|
if [ -n "${config['issuer_name']}" ] ; then
|
|
update_symlink issuer ../${config['issuer_name']}
|
|
fi
|
|
|
|
if [ "${library}" = "openssl" ] ; then
|
|
generate_${library}_serial database/serial
|
|
test -r database/index || touch database/index
|
|
fi
|
|
|
|
generate_${library}_request config/${library}-request.conf subject/request.pem
|
|
|
|
if [ -z "${config['issuer_name']}" ] && [ ! -L issuer ] ; then
|
|
|
|
sign_${library}_root_certificate config/${library}-selfsign.conf \
|
|
subject/request.pem subject/cert.pem
|
|
|
|
elif [ -n "${config['issuer_name']}" ] && [ -L issuer ] ; then
|
|
local subject_dir
|
|
subject_dir="$(pwd)"
|
|
cd issuer || exit 1
|
|
sign_${library}_ca_certificate config/${library}-sign.conf \
|
|
"${subject_dir}/subject/request.pem" "${subject_dir}/subject/cert.pem"
|
|
cd - > /dev/null
|
|
fi
|
|
|
|
if [ "${config['system_ca']}" = "true" ] && [ -n "${config['pki_ca_certificates']}" ] && [ -n "${config['subdomain']}" ] && [ -z "${config['issuer_name']}" ] && [ ! -L issuer ] ; then
|
|
if [ -r "$(dirname "${config['pki_ca_certificates']}")/${config['subdomain']}.${config['domain']}.crt" ] ; then
|
|
if ! diff -q -N "$(get_relative_path "subject/cert.pem" "${config['pki_ca_certificates']}")" "${config['pki_ca_certificates']}/${config['subdomain']}.${config['domain']}.crt" > /dev/null ; then
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "${config['pki_ca_certificates']}")" "${config['pki_ca_certificates']}/${config['subdomain']}.${config['domain']}.crt"
|
|
fi
|
|
else
|
|
ln -sf "$(get_relative_path "subject/cert.pem" "${config['pki_ca_certificates']}")" "${config['pki_ca_certificates']}/${config['subdomain']}.${config['domain']}.crt"
|
|
fi
|
|
fi
|
|
|
|
fi
|
|
}
|
|
|
|
sub_init () {
|
|
|
|
local -A args
|
|
|
|
local optspec=":hn-:"
|
|
while getopts "${optspec}" optchar; do
|
|
case "${optchar}" in
|
|
-)
|
|
case "${OPTARG}" in
|
|
name)
|
|
args["name"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
name=*)
|
|
args["name"]=${OPTARG#*=}
|
|
;;
|
|
library)
|
|
args["pki_library"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
library=*)
|
|
args["pki_library"]=${OPTARG#*=}
|
|
;;
|
|
default-sign-base)
|
|
args["pki_default_sign_base"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
default-sign-base=*)
|
|
args["pki_default_sign_base"]=${OPTARG#*=}
|
|
;;
|
|
root-sign-multiplier)
|
|
args["pki_default_root_sign_multiplier"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
root-sign-multiplier=*)
|
|
args["pki_default_root_sign_multiplier"]=${OPTARG#*=}
|
|
;;
|
|
ca-sign-multiplier)
|
|
args["pki_default_ca_sign_multiplier"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
ca-sign-multiplier=*)
|
|
args["pki_default_ca_sign_multiplier"]=${OPTARG#*=}
|
|
;;
|
|
cert-sign-multiplier)
|
|
args["pki_default_cert_sign_multiplier"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
cert-sign-multiplier=*)
|
|
args["pki_default_cert_sign_multiplier"]=${OPTARG#*=}
|
|
;;
|
|
*)
|
|
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
|
|
echo "Unknown option --${OPTARG}" >&2
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
h)
|
|
echo "usage: ${script} init <-n|--name[=]authority>" >&2
|
|
exit 2
|
|
;;
|
|
n)
|
|
args["name"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
|
|
;;
|
|
*)
|
|
if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
|
|
echo "Non-option argument: '-${OPTARG}'" >&2
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if key_exists args "name" ; then
|
|
|
|
enter_authority ${args['name']}
|
|
|
|
for key in "${!args[@]}" ; do
|
|
if [ -n "${args[${key}]+x}" ] ; then
|
|
if key_exists config "${key}" ; then
|
|
if [ "${config[${key}]}" != "${args[${key}]}" ] ; then
|
|
local config_changed="true"
|
|
config["${key}"]="${args[${key}]}"
|
|
fi
|
|
else
|
|
local config_changed="true"
|
|
config["${key}"]="${args[${key}]}"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
create_public_directories ${config['public_dir_group']} config database subject certs requests signed
|
|
create_private_directories ${config['private_dir_group']} private
|
|
|
|
if [ "${config_changed}" = "true" ] ; then
|
|
save_authority_config config/authority.conf
|
|
fi
|
|
|
|
fi
|
|
}
|
|
|
|
print_usage () {
|
|
cat << EOF >&2
|
|
Usage: ${script} <command> [parameters]
|
|
|
|
Available commands:
|
|
|
|
init
|
|
usage: pki-authority init <-n|--name[=]authority>
|
|
|
|
new-ca
|
|
usage: pki-authority new-ca <-n|--name[=]authority>
|
|
|
|
sign
|
|
usage: pki-authority init <-n|--name[=]authority>
|
|
|
|
sign-by-host
|
|
usage: ${script} sign-by-host [host1 host2 ...]
|
|
signs certificates for specified hosts and writes them to the secret/pki subdirectory
|
|
EOF
|
|
|
|
}
|
|
|
|
initialize_environment
|
|
|
|
if [ $# -gt 0 ] ; then
|
|
|
|
subcommand="${1}"
|
|
|
|
if [ -n "${subcommand}" ] ; then
|
|
case "${subcommand}" in
|
|
init|new-ca|sign|sign-by-host)
|
|
shift
|
|
"sub_${subcommand}" "${@}"
|
|
;;
|
|
|
|
*)
|
|
echo "${script}: Error: unknown subcommand '${subcommand}'" >&2
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
else
|
|
print_usage
|
|
exit 1
|
|
fi
|