#!/usr/bin/env bash # vim: foldmarker={{{,}}}:foldmethod=marker # pki-authority: CA-side PKI management # Copyright (C) 2016 Maciej Delmanowski # Copyright (C) 2016-2017 Robin Schneider # Copyright (C) 2016-2020 DebOps # 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} [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