force_set_var() is intended to deliberately over-write all prior values. This also drops an unnecessary 'eval'. Signed-off-by: Richard T Bonhomme <tincantech@protonmail.com>
5699 lines
146 KiB
Bash
Executable File
5699 lines
146 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
# Easy-RSA 3 -- A Shell-based CA Utility
|
|
#
|
|
# Copyright (C) 2018 by the Open-Source OpenVPN development community.
|
|
# A full list of contributors can be found on Github at:
|
|
# https://github.com/OpenVPN/easy-rsa/graphs/contributors
|
|
#
|
|
# This code released under version 2 of the GNU GPL; see COPYING and the
|
|
# Licensing/ directory of this project for full licensing details.
|
|
|
|
# Help/usage output to stdout
|
|
usage() {
|
|
# command help:
|
|
print "
|
|
Easy-RSA 3 usage and overview
|
|
|
|
USAGE: easyrsa [options] COMMAND [command-options]
|
|
|
|
A list of commands is shown below. To get detailed usage and help for a
|
|
command, run:
|
|
./easyrsa help COMMAND
|
|
|
|
For a listing of options that can be supplied before the command, use:
|
|
./easyrsa help options
|
|
|
|
Here is the list of commands available with a short syntax reminder. Use the
|
|
'help' command above to get full usage details.
|
|
|
|
init-pki [ cmd-opts ]
|
|
build-ca [ cmd-opts ]
|
|
gen-dh
|
|
gen-req <file_name_base> [ cmd-opts ]
|
|
sign-req <type> <file_name_base>
|
|
build-client-full <file_name_base> [ cmd-opts ]
|
|
build-server-full <file_name_base> [ cmd-opts ]
|
|
build-serverClient-full <file_name_base> [ cmd-opts ]
|
|
revoke <file_name_base> [ cmd-opts ]
|
|
renew <file_name_base>
|
|
revoke-renewed <file_name_base> [ cmd-opts ]
|
|
rewind-renew <certificate_serial_number>
|
|
rebuild <file_name_base> [ cmd-opts ]
|
|
gen-crl
|
|
update-db
|
|
make-safe-ssl
|
|
show-req <file_name_base> [ cmd-opts ]
|
|
show-cert <file_name_base> [ cmd-opts ]
|
|
show-ca [ cmd-opts ]
|
|
show-crl
|
|
show-expire <file_name_base> (Optional)
|
|
show-revoke <file_name_base> (Optional)
|
|
show-renew <file_name_base> (Optional)
|
|
verify-cert <file_name_base>
|
|
import-req <request_file_path> <short_name_base>
|
|
export-p1 <file_name_base> [ cmd-opts ]
|
|
export-p7 <file_name_base> [ cmd-opts ]
|
|
export-p8 <file_name_base> [ cmd-opts ]
|
|
export-p12 <file_name_base> [ cmd-opts ]
|
|
set-pass <file_name_base> [ cmd-opts ]
|
|
upgrade <type>
|
|
"
|
|
|
|
# collect/show dir status:
|
|
text_only=1
|
|
err_source="Not defined: vars autodetect failed and no value provided"
|
|
work_dir="${EASYRSA:-$err_source}"
|
|
pki_dir="${EASYRSA_PKI:-$err_source}"
|
|
print "\
|
|
DIRECTORY STATUS (commands would take effect on these locations)
|
|
EASYRSA: $work_dir
|
|
PKI: $pki_dir
|
|
x509-types: ${EASYRSA_EXT_DIR:-Missing or undefined}"
|
|
|
|
} # => usage()
|
|
|
|
# Detailed command help
|
|
# When called with no args, calls usage(), otherwise shows help for a command
|
|
# Please maintain strict indentation rules.
|
|
# Commands are TAB indented, while text is SPACE indented.
|
|
# 'case' indentation is minimalistic.
|
|
cmd_help() {
|
|
unset -v text err_text opts text_only
|
|
case "$1" in
|
|
init-pki|clean-all)
|
|
text="
|
|
* init-pki [ cmd-opts ]
|
|
|
|
Removes & re-initializes the PKI directory for a new PKI"
|
|
|
|
opts="
|
|
* hard - Recursively delete the PKI directory (default).
|
|
* soft - Keep the named PKI directory and PKI 'vars' file intact."
|
|
;;
|
|
build-ca)
|
|
text="
|
|
* build-ca [ cmd-opts ]
|
|
|
|
Creates a new CA"
|
|
|
|
opts="
|
|
* nopass - Do not encrypt the private key (default is encrypted)
|
|
(Equivalent to global option '--nopass|--no-pass')
|
|
* subca - Create an intermediate CA keypair and request
|
|
(default is a root CA)
|
|
* intca - Alias to the above"
|
|
;;
|
|
gen-dh)
|
|
text="
|
|
* gen-dh
|
|
|
|
Generates DH (Diffie-Hellman) parameters"
|
|
;;
|
|
gen-req)
|
|
text="
|
|
* gen-req <file_name_base> [ cmd-opts ]
|
|
|
|
Generate a standalone private key and certificate signing request [CSR]
|
|
|
|
This request is suitable for sending to a remote CA for signing."
|
|
|
|
opts="
|
|
* nopass - Do not encrypt the private key (default is encrypted)
|
|
(Equivalent to global option '--nopass|--no-pass')
|
|
* text - Include certificate text in request"
|
|
;;
|
|
sign|sign-req)
|
|
text="
|
|
* sign-req <type> <file_name_base>
|
|
|
|
Sign a certificate request of the defined type. <type> must be a known type,
|
|
such as: 'client', 'server', 'serverClient', or 'ca' (or a user-added type).
|
|
All supported types are listed in the x509-types directory.
|
|
|
|
This request file must exist in the reqs/ dir and have a .req file
|
|
extension. See import-req below for importing reqs from other sources."
|
|
;;
|
|
build|build-client-full|build-server-full|build-serverClient-full)
|
|
text="
|
|
* build-client-full <file_name_base> [ cmd-opts ]
|
|
* build-server-full <file_name_base> [ cmd-opts ]
|
|
* build-serverClient-full <file_name_base> [ cmd-opts ]
|
|
|
|
Generate a keypair and sign locally for a client and/or server
|
|
|
|
This mode uses the <file_name_base> as the X509 CN."
|
|
|
|
opts="
|
|
* nopass - Do not encrypt the private key (default is encrypted)
|
|
(Equivalent to global option '--nopass|--no-pass')
|
|
* inline - Create an inline credentials file for this node"
|
|
;;
|
|
revoke)
|
|
text="
|
|
* revoke <file_name_base> [reason]
|
|
|
|
Revoke a certificate specified by the <file_name_base>,
|
|
with an optional revocation reason which can be one of:
|
|
unspecified
|
|
keyCompromise
|
|
CACompromise
|
|
affiliationChanged
|
|
superseded
|
|
cessationOfOperation
|
|
certificateHold"
|
|
;;
|
|
revoke-renewed)
|
|
text="
|
|
* revoke-renewed <file_name_base> [reason]
|
|
|
|
Revoke a *renewed* certificate specified by the <file_name_base>,
|
|
with an optional revocation reason which can be one of:
|
|
unspecified
|
|
keyCompromise
|
|
CACompromise
|
|
affiliationChanged
|
|
superseded
|
|
cessationOfOperation
|
|
certificateHold"
|
|
;;
|
|
rebuild)
|
|
text="
|
|
* rebuild <file_name_base> [ cmd-opts ]
|
|
|
|
Rebuild a certificate and key specified by <file_name_base>"
|
|
|
|
opts="
|
|
* nopass - Do not encrypt the private key (default is encrypted)
|
|
(Equivalent to global option '--nopass|--no-pass')"
|
|
;;
|
|
renew)
|
|
text="
|
|
* renew <file_name_base>
|
|
|
|
Renew a certificate specified by <file_name_base>"
|
|
;;
|
|
rewind|rewind-renew)
|
|
text="
|
|
* rewind-renew <certificate_serial_number>
|
|
|
|
Rewind an EasyRSA version 3.0 'style' renewed certificate.
|
|
Once 'rewind' has completed the certificate can be revoked
|
|
by using: 'revoke-renewed <file_name_base> [reason]'
|
|
|
|
* NOTE: This does NOT 'unrenew' or 'unrevoke' a certificate.
|
|
Ref : https://github.com/OpenVPN/easy-rsa/issues/578"
|
|
;;
|
|
gen-crl)
|
|
text="
|
|
* gen-crl
|
|
|
|
Generate a certificate revocation list [CRL]"
|
|
;;
|
|
update-db)
|
|
text="
|
|
* update-db
|
|
|
|
Update the index.txt database
|
|
|
|
This command will use the system time to update the status of
|
|
issued certificates."
|
|
;;
|
|
make-safe-ssl)
|
|
text="
|
|
* make-safe-ssl
|
|
|
|
Generate a safe SSL config file"
|
|
;;
|
|
show-req|show-cert)
|
|
text="
|
|
* show-req <file_name_base> [ cmd-opts ]
|
|
* show-cert <file_name_base> [ cmd-opts ]
|
|
|
|
Shows details of the req or cert referenced by <file_name_base>
|
|
|
|
Human-readable output is shown, including any requested cert
|
|
options when showing a request."
|
|
|
|
opts="
|
|
* full - show full req/cert info, including pubkey/sig data"
|
|
;;
|
|
show-ca)
|
|
text="
|
|
* show-ca [ cmd-opts ]
|
|
|
|
Shows details of the Certificate Authority [CA] certificate
|
|
|
|
Human-readable output is shown."
|
|
|
|
opts="
|
|
* full - show full cert info, including pubkey/sig data"
|
|
;;
|
|
show-crl)
|
|
text="
|
|
* show-crl
|
|
|
|
Shows details of the current certificate revocation list (CRL)
|
|
|
|
Human-readable output is shown."
|
|
;;
|
|
show-expire)
|
|
text="
|
|
* show-expire [ <file_name_base> ]
|
|
|
|
Shows details of *all* expiring certificates
|
|
Use --renew-days=NN to extend the grace period (Default 90 days)
|
|
Optionally, check *only* <file_name_base> certificate"
|
|
;;
|
|
show-revoke)
|
|
text="
|
|
* show-revoke [ <file_name_base> ]
|
|
|
|
Shows details of *all* revoked certificates.
|
|
Optionally, check *only* <file_name_base> certificate"
|
|
;;
|
|
show-renew)
|
|
text="
|
|
* show-renew [ <file_name_base> ]
|
|
|
|
Shows details of renewed certificates, which have not been revoked
|
|
Optionally, check *only* <file_name_base> certificate"
|
|
;;
|
|
verify|verify-cert)
|
|
text="
|
|
* verify-cert <file_name_base> [ cmd-opts ]
|
|
|
|
Verify certificate against CA
|
|
|
|
Returns the current validity of the certificate."
|
|
|
|
opts="
|
|
* batch - On failure to verify, return error (1) to caller"
|
|
;;
|
|
import-req)
|
|
text="
|
|
* import-req <request_file_path> <short_name_base>
|
|
|
|
Import a certificate request from a file
|
|
|
|
This will copy the specified file into the reqs/ dir in
|
|
preparation for signing.
|
|
|
|
The <short_name_base> is the <file_name_base> to create.
|
|
|
|
Example usage:
|
|
import-req /some/where/bob_request.req bob"
|
|
;;
|
|
export-p12)
|
|
text="
|
|
* export-p12 <file_name_base> [ cmd-opts ]
|
|
|
|
Export a PKCS#12 file with the keypair,
|
|
specified by <file_name_base>"
|
|
|
|
opts="
|
|
* nopass - Do not encrypt the private key (default is encrypted)
|
|
(Equivalent to global option '--nopass|--no-pass')
|
|
* noca - Do not include the ca.crt file in the PKCS12 output
|
|
* nokey - Do not include the private key in the PKCS12 output
|
|
* usefn - Use <file_name_base> as friendly name"
|
|
;;
|
|
export-p7)
|
|
text="
|
|
* export-p7 <file_name_base> [ cmd-opts ]
|
|
|
|
Export a PKCS#7 file with the pubkey,
|
|
specified by <file_name_base>"
|
|
|
|
opts="
|
|
* noca - Do not include the ca.crt file in the PKCS7 output"
|
|
;;
|
|
export-p8)
|
|
text="
|
|
* export-p8 <file_name_base> [ cmd-opts ]
|
|
|
|
Export a PKCS#8 file with the private key,
|
|
specified by <file_name_base>"
|
|
|
|
opts="
|
|
* nopass - Do not encrypt the private key (default is encrypted)
|
|
(Equivalent to global option '--nopass|--no-pass')"
|
|
;;
|
|
export-p1)
|
|
text="
|
|
* export-p1 <file_name_base> [ cmd-opts ]
|
|
|
|
Export a PKCS#1 (RSA format) file with the pubkey,
|
|
specified by <file_name_base>"
|
|
|
|
opts="
|
|
* nopass - Do not encrypt the private key (default is encrypted)
|
|
(Equivalent to global option '--nopass|--no-pass')"
|
|
;;
|
|
set-pass|set-ed-pass|set-rsa-pass|set-ec-pass)
|
|
text="
|
|
* set-pass <file_name_base> [ cmd-opts ]
|
|
|
|
Set a new passphrase for the private key specified by <file_name_base>
|
|
|
|
DEPRECATED: 'set-rsa-pass' and 'set-ec-pass'"
|
|
|
|
opts="
|
|
* nopass - Do not encrypt the private key (default is encrypted)
|
|
(Equivalent to global option '--nopass|--no-pass')
|
|
* file - (Advanced) Treat the file as a raw path, not a short-name"
|
|
;;
|
|
upgrade)
|
|
text="
|
|
* upgrade <type>
|
|
|
|
Upgrade EasyRSA PKI and/or CA.
|
|
|
|
Upgrade <type> must be one of:
|
|
|
|
* pki - Upgrade EasyRSA v2.x PKI to EasyRSA v3.x PKI (includes CA below)
|
|
* ca - Upgrade EasyRSA v3.0.5 CA or older to EasyRSA v3.0.6 CA or later."
|
|
;;
|
|
altname|subjectaltname|san)
|
|
text_only=1
|
|
text="
|
|
* Option: --subject-alt-name=SAN_FORMAT_STRING
|
|
|
|
This global option adds a subjectAltName to the request or issued
|
|
certificate. It MUST be in a valid format accepted by openssl or
|
|
req/cert generation will fail. Note that including multiple such names
|
|
requires them to be comma-separated; further invocations of this
|
|
option will REPLACE the value.
|
|
|
|
Examples of the SAN_FORMAT_STRING shown below:
|
|
|
|
* DNS:alternate.example.net
|
|
* DNS:primary.example.net,DNS:alternate.example.net
|
|
* IP:203.0.113.29
|
|
* email:alternate@example.net"
|
|
;;
|
|
--days|days)
|
|
text_only=1
|
|
text="
|
|
* Option: --days=DAYS
|
|
|
|
This global option is an alias for one of the following:
|
|
* Expiry days for a new CA.
|
|
eg: '--days=3650 build-ca'
|
|
* Expiry days for new/renewed certificate.
|
|
eg: '--days=1095 renew server'
|
|
* Expiry days for certificate revokation list.
|
|
eg: '--days=180 gen-crl'
|
|
* Cutoff days for command: show-expire.
|
|
eg: '--days=90 show-expire'"
|
|
;;
|
|
--req-cn|req-cn)
|
|
text_only=1
|
|
text="
|
|
* Option: --req-cn=NAME
|
|
|
|
This specific option can set the CSR commonName.
|
|
|
|
Can only be used in BATCH mode for the following commands:
|
|
* To build a new CA [or Sub-CA]:
|
|
eg: '--batch --req-cn=NAME build-ca [subca]'
|
|
* To generate a certificate signing request:
|
|
eg: '--batch --req-cn=NAME gen-req <file_name_base>'"
|
|
;;
|
|
opts|options)
|
|
opt_usage
|
|
;;
|
|
"")
|
|
usage ;;
|
|
*)
|
|
err_text="
|
|
Unknown command: '$1' (try without commands for a list of commands)"
|
|
easyrsa_error_exit=1
|
|
esac
|
|
|
|
if [ "$err_text" ]; then
|
|
print "${err_text}${NL}"
|
|
else
|
|
# display the help text
|
|
[ "$text" ] && print "${text}"
|
|
|
|
if [ "$text_only" ]; then
|
|
: # ok - No opts message required
|
|
else
|
|
print "
|
|
Available command options [ cmd-opts ]:
|
|
${opts:-
|
|
* No supported command options}"
|
|
fi
|
|
fi
|
|
} # => cmd_help()
|
|
|
|
# Options usage
|
|
opt_usage() {
|
|
text_only=1
|
|
print "
|
|
Easy-RSA Global Option Flags
|
|
|
|
The following options may be provided before the command. Options specified
|
|
at runtime override env-vars and any 'vars' file in use. Unless noted,
|
|
non-empty values to options are mandatory.
|
|
|
|
General options:
|
|
|
|
--version : Prints EasyRSA version and build information
|
|
--batch : Set automatic (no-prompts when possible) mode
|
|
--silent|-s : Disable all warnings, notices and information
|
|
--sbatch : Combined --silent and --batch operating mode
|
|
|
|
--no-pass : Do not use passwords
|
|
Can not be used with --passin or --passout
|
|
--passin=ARG : Set -passin ARG for openssl (eg: pass:xEasyRSAy)
|
|
--passout=ARG : Set -passout ARG for openssl (eg: pass:xEasyRSAy)
|
|
|
|
--vars=FILE : Define a specific 'vars' file to use for Easy-RSA config
|
|
(Default vars file is in the EasyRSA PKI directory)
|
|
--pki-dir=DIR : Declare the PKI directory
|
|
(Default PKI directory is sub-directory 'pki')
|
|
--ssl-conf=FILE : Define a specific OpenSSL config file for Easy-RSA to use
|
|
(Default config file is in the EasyRSA PKI directory)
|
|
--no-safe-ssl : OpenSSL Only: Do not use a safe SSL config file
|
|
|
|
--tmp-dir=DIR : Declare the temporary directory
|
|
(Default temporary directory is the EasyRSA PKI directory)
|
|
--keep-tmp=NAME : Keep the original temporary session by name: NAME
|
|
NAME is a sub-directory of the dir declared by --tmp-dir
|
|
This option ALWAYS over-writes a sub-dir of the same name.
|
|
|
|
Certificate & Request options: (these impact cert/req field values)
|
|
|
|
--no-text : Create certificates without human readable text
|
|
--days=# : Sets the signing validity to the specified number of days
|
|
Also applies to renewal period. For details, see: 'help days'
|
|
--fix-offset=# : Generate certificate with fixed start and end dates
|
|
Start-date is 01 January 00:00:01 of the current year
|
|
plus the --fix-offset=# number of days (Range 1 to 365)
|
|
end-date is configured via --days=# (Default: 825 days)
|
|
|
|
--digest=ALG : Digest to use in the requests & certificates
|
|
--keysize=# : Size in bits of keypair to generate (RSA Only)
|
|
--use-algo=ALG : Crypto alg to use: choose rsa (default), ec or ed
|
|
--curve=NAME : For elliptic curve, sets the named curve (Default: secp384r1)
|
|
|
|
--subca-len=# : Path length of signed intermediate CA certificates
|
|
--copy-ext : Copy included request X509 extensions (namely subjAltName)
|
|
--san|--subject-alt-name
|
|
: Add a subjectAltName.
|
|
For more info and syntax, see: 'easyrsa help altname'
|
|
|
|
Distinguished Name mode:
|
|
|
|
--dn-mode=MODE : Distinguished Name mode to use 'cn_only' (Default) or 'org'
|
|
|
|
--req-cn=NAME : Set CSR commonName to NAME. For details, see: 'help req-cn'
|
|
|
|
Distinguished Name Organizational options: (only used with '--dn-mode=org')
|
|
--req-c=CC : Country code (2-letters)
|
|
--req-st=NAME : State/Province
|
|
--req-city=NAME : City/Locality
|
|
--req-org=NAME : Organization
|
|
--req-email=NAME : Email addresses
|
|
--req-ou=NAME : Organizational Unit
|
|
--req-serial=VALUE : Entity serial number (Only used when declared)
|
|
|
|
Deprecated features:
|
|
|
|
--ns-cert : Include deprecated Netscape extensions
|
|
--ns-comment=COMMENT : Include deprecated Netscape comment (may be blank)"
|
|
} # => opt_usage()
|
|
|
|
# Wrapper around printf - clobber print since it's not POSIX anyway
|
|
# print() is used internally, so MUST NOT be silenced.
|
|
# shellcheck disable=SC1117
|
|
print() { printf "%s\n" "$*" || exit 1; }
|
|
|
|
# Exit fatally with a message to stderr
|
|
# present even with EASYRSA_BATCH as these are fatal problems
|
|
die() {
|
|
print "
|
|
Easy-RSA error:
|
|
|
|
$1
|
|
" 1>&2
|
|
exit "${2:-1}"
|
|
} # => die()
|
|
|
|
# non-fatal warning output
|
|
warn() {
|
|
[ "$EASYRSA_SILENT" ] && return
|
|
print "
|
|
WARNING
|
|
=======
|
|
$1
|
|
" 1>&2
|
|
} # => warn()
|
|
|
|
# informational notices to stdout
|
|
notice() {
|
|
[ "$EASYRSA_SILENT" ] && return
|
|
print "
|
|
Notice
|
|
------
|
|
$1"
|
|
} # => notice()
|
|
|
|
# Helpful information
|
|
information() {
|
|
[ "$EASYRSA_SILENT" ] && return
|
|
print "
|
|
* $1"
|
|
} # => information()
|
|
|
|
# intent confirmation helper func
|
|
# returns without prompting in EASYRSA_BATCH
|
|
confirm() {
|
|
[ "$EASYRSA_BATCH" ] && return
|
|
prompt="$1"
|
|
value="$2"
|
|
msg="$3"
|
|
input=""
|
|
print "\
|
|
$msg
|
|
|
|
Type the word '$value' to continue, or any other input to abort."
|
|
printf %s " $prompt"
|
|
# shellcheck disable=SC2162 # read without -r will mangle backslashes
|
|
read input
|
|
printf '\n'
|
|
[ "$input" = "$value" ] && return
|
|
easyrsa_error_exit=1
|
|
notice "Aborting without confirmation."
|
|
cleanup 9
|
|
} # => confirm()
|
|
|
|
# Generate random hex
|
|
# Cannot use easyrsa-openssl() due to chicken vs egg,
|
|
# easyrsa_openssl() creates temp-files, which needs `openssl rand`.
|
|
# Redirect error-out, because LibreSSL complains of missing conf.
|
|
easyrsa_random() {
|
|
case "$1" in
|
|
(*[!1234567890]*|0*|"") : ;; # invalid input
|
|
(*)
|
|
# Only return on success
|
|
"$EASYRSA_OPENSSL" rand -hex "$1" 2>/dev/null && return
|
|
esac
|
|
die "easyrsa_random failed"
|
|
} # => easyrsa_random()
|
|
|
|
# Create session directory atomically or fail
|
|
secure_session() {
|
|
# Session is already defined
|
|
[ "$EASYRSA_TEMP_DIR_session" ] && die "session overload"
|
|
|
|
# temporary directory must exist
|
|
if [ "$EASYRSA_TEMP_DIR" ] && [ -d "$EASYRSA_TEMP_DIR" ]
|
|
then
|
|
: # ok
|
|
else
|
|
die "Non-existant temporary directory: $EASYRSA_TEMP_DIR"
|
|
fi
|
|
|
|
for i in 1 2 3; do
|
|
random_session="$(
|
|
easyrsa_random 4
|
|
)" || die "secure_session - random_session '$random_session'"
|
|
|
|
EASYRSA_TEMP_DIR_session="${EASYRSA_TEMP_DIR}/${random_session}"
|
|
# atomic:
|
|
mkdir "$EASYRSA_TEMP_DIR_session" && return
|
|
done
|
|
die "secure_session failed"
|
|
} # => secure_session()
|
|
|
|
# Create tempfile atomically or fail
|
|
easyrsa_mktemp() {
|
|
# session directory must exist
|
|
if [ "$EASYRSA_TEMP_DIR_session" ] && [ -d "$EASYRSA_TEMP_DIR_session" ]
|
|
then
|
|
: # ok
|
|
else
|
|
die "\
|
|
easyrsa_mktemp - Non-existant temporary session:
|
|
* $EASYRSA_TEMP_DIR_session"
|
|
fi
|
|
|
|
for i in 1 2 3; do
|
|
# Always use openssl directly for rand
|
|
random_file="$(
|
|
easyrsa_random 4
|
|
)" || die "easyrsa_mktemp - random_file '$random_file'"
|
|
|
|
shotfile="${EASYRSA_TEMP_DIR_session}/shot.$random_file"
|
|
if [ -e "$shotfile" ]; then
|
|
continue
|
|
else
|
|
printf "" > "$shotfile" || continue
|
|
fi
|
|
|
|
tempfile="${EASYRSA_TEMP_DIR_session}/temp.$random_file"
|
|
# atomic:
|
|
if mv "$shotfile" "$tempfile"; then
|
|
# Print the new temporary file-name for the caller
|
|
printf '%s\n' "$tempfile" && return
|
|
fi
|
|
done
|
|
die "easyrsa_mktemp failed"
|
|
} # => easyrsa_mktemp()
|
|
|
|
# remove temp files and do terminal cleanups
|
|
cleanup() {
|
|
if [ "${EASYRSA_TEMP_DIR_session%/*}" ] && \
|
|
[ -d "$EASYRSA_TEMP_DIR_session" ]
|
|
then
|
|
# Remove temp-session or create temp-snapshot
|
|
if [ "$EASYRSA_KEEP_TEMP" ]
|
|
then
|
|
# skip on black-listed directory names, with a warning
|
|
if [ -e "$EASYRSA_TEMP_DIR/$EASYRSA_KEEP_TEMP" ]
|
|
then
|
|
warn "\
|
|
Prohibited value for --keep-tmp: '$EASYRSA_KEEP_TEMP'
|
|
Temporary session not preserved."
|
|
else
|
|
# create temp-snapshot
|
|
keep_tmp="$EASYRSA_TEMP_DIR/tmp/$EASYRSA_KEEP_TEMP"
|
|
mkdir -p "$keep_tmp"
|
|
rm -rf "$keep_tmp"
|
|
mv -f "$EASYRSA_TEMP_DIR_session" "$keep_tmp"
|
|
print "Temp session preserved: $keep_tmp"
|
|
fi
|
|
fi
|
|
|
|
# Always remove temp-session
|
|
rm -rf "$EASYRSA_TEMP_DIR_session"
|
|
fi
|
|
|
|
if [ "${EASYRSA_EC_DIR%/*}" ] && [ -d "$EASYRSA_EC_DIR" ]
|
|
then
|
|
rm -rf "$EASYRSA_EC_DIR"
|
|
fi
|
|
|
|
# Remove files when build_full()->sign_req() is interrupted
|
|
[ "$error_build_full_cleanup" ] && \
|
|
rm -f "$crt_out" "$req_out" "$key_out"
|
|
|
|
# Restore files when renew is interrupted
|
|
[ "$error_undo_renew_move" ] && renew_restore_move
|
|
# Restore files when rebuild is interrupted
|
|
[ "$error_undo_rebuild_move" ] && rebuild_restore_move
|
|
|
|
# shellcheck disable=SC3040
|
|
# In POSIX sh, set option [name] is undefined
|
|
case "$easyrsa_host_os" in
|
|
nix) [ -t 1 ] && stty echo ;;
|
|
win)
|
|
if [ "$easyrsa_win_git_bash" ]; then
|
|
[ -t 1 ] && stty echo
|
|
else
|
|
set -o echo
|
|
fi
|
|
;;
|
|
*) warn "Host OS undefined."
|
|
esac
|
|
|
|
[ "$EASYRSA_SILENT" ] || print # Get a clean line
|
|
|
|
# Clear traps
|
|
trap - 0 1 2 3 6 15
|
|
|
|
# Exit: Known errors:
|
|
# -> confirm(): aborted
|
|
# -> verify_cert(): verify failed --batch mode
|
|
if [ "$easyrsa_error_exit" ]; then
|
|
exit 1
|
|
fi
|
|
|
|
# Exit: SIGINT
|
|
if [ "$1" = 2 ]; then
|
|
kill -2 "$$"
|
|
fi
|
|
|
|
# Exit: Final Success
|
|
if [ "$1" = ok ]; then
|
|
# if there is no error
|
|
# then 'cleanup ok' is called
|
|
exit 0
|
|
fi
|
|
|
|
# Exit: Final Fail
|
|
# if 'cleanup' is called without 'ok'
|
|
# then an error occurred
|
|
show_host
|
|
exit 1
|
|
} # => cleanup()
|
|
|
|
# Make a copy safe SSL config file
|
|
make_safe_ssl() {
|
|
verify_pki_init
|
|
easyrsa_openssl makesafeconf
|
|
notice "\
|
|
Generated safe SSL config file:
|
|
* $EASYRSA_SAFE_CONF"
|
|
} # => make_safe_ssl_copy()
|
|
|
|
# Escape hazardous characters
|
|
escape_hazard() {
|
|
# Assign temp file
|
|
easyrsa_vars_org="$(easyrsa_mktemp)" || \
|
|
die "escape_hazard - easyrsa_mktemp failed"
|
|
|
|
# write org fields to org temp-file and escape '&' and '$'
|
|
print "\
|
|
export EASYRSA_REQ_COUNTRY=\"$EASYRSA_REQ_COUNTRY\"
|
|
export EASYRSA_REQ_PROVINCE=\"$EASYRSA_REQ_PROVINCE\"
|
|
export EASYRSA_REQ_CITY=\"$EASYRSA_REQ_CITY\"
|
|
export EASYRSA_REQ_ORG=\"$EASYRSA_REQ_ORG\"
|
|
export EASYRSA_REQ_OU=\"$EASYRSA_REQ_OU\"
|
|
export EASYRSA_REQ_EMAIL=\"$EASYRSA_REQ_EMAIL\"
|
|
" | sed -e s\`'\&'\`'\\\&'\`g \
|
|
-e s\`'\$'\`'\\\$'\`g \
|
|
> "$easyrsa_vars_org" || die "\
|
|
escape_hazard - Failed to write temp-file"
|
|
|
|
# Reload fields from fully escaped temp-file
|
|
# shellcheck disable=SC1090 # can't follow non-constant source.
|
|
. "$easyrsa_vars_org" || die "\
|
|
escape_hazard - Failed to source temp-file"
|
|
} # => escape_hazard()
|
|
|
|
# Easy-RSA meta-wrapper for SSL
|
|
easyrsa_openssl() {
|
|
openssl_command="$1"; shift
|
|
|
|
# Do not allow 'rand' here, see easyrsa_random()
|
|
case "$openssl_command" in
|
|
rand) die "easyrsa_openssl: Illegal SSL command: rand" ;;
|
|
makesafeconf) require_safe_ssl_conf=1 ;;
|
|
ca|req|srp|ts) has_config=1 ;;
|
|
*) unset -v has_config
|
|
esac
|
|
|
|
# OpenSSL 1x genpkey does not support -config - Not as documented:
|
|
# https://www.openssl.org/docs/manmaster/man1/openssl-genpkey.html
|
|
# This could be anyones bug ..
|
|
if [ "$openssl_command" = genpkey ] && \
|
|
[ "$ssl_lib" = openssl ] && [ "$osslv_major" = 3 ]
|
|
then
|
|
has_config=1
|
|
fi
|
|
|
|
# Assign safe temp file to create, may not be used
|
|
easyrsa_safe_ssl_conf="$(easyrsa_mktemp)" || \
|
|
die "easyrsa_openssl - easyrsa_mktemp failed"
|
|
|
|
# Auto-escape hazardous characters:
|
|
# '&' - Workaround 'sed' behavior
|
|
# '$' - Workaround 'easyrsa' based limitation
|
|
# This is required for all SSL libs, otherwise,
|
|
# there are unacceptable differences in behavior
|
|
escape_hazard
|
|
|
|
# Make LibreSSL safe config file from OpenSSL config file
|
|
# $require_safe_ssl_conf is ALWAYS set by verify_ssl_lib()
|
|
# Can be over-ruled for OpenSSL by option --no-safe-ssl
|
|
if [ "$require_safe_ssl_conf" ]; then
|
|
|
|
# Break indentation for long lines
|
|
# Make a safe SSL config file
|
|
# shellcheck disable=SC2016 # No expansion inside ' single quote
|
|
sed \
|
|
-e s\`'$dir'\`\""$EASYRSA_PKI"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_PKI'\`\""$EASYRSA_PKI"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_CERT_EXPIRE'\`\""$EASYRSA_CERT_EXPIRE"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_CRL_DAYS'\`\""$EASYRSA_CRL_DAYS"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_DIGEST'\`\""$EASYRSA_DIGEST"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_KEY_SIZE'\`\""$EASYRSA_KEY_SIZE"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_DN'\`\""$EASYRSA_DN"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_REQ_CN'\`\""$EASYRSA_REQ_CN"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_REQ_COUNTRY'\`\""$EASYRSA_REQ_COUNTRY"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_REQ_PROVINCE'\`\""$EASYRSA_REQ_PROVINCE"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_REQ_CITY'\`\""$EASYRSA_REQ_CITY"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_REQ_ORG'\`\""$EASYRSA_REQ_ORG"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_REQ_OU'\`\""$EASYRSA_REQ_OU"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_REQ_EMAIL'\`\""$EASYRSA_REQ_EMAIL"\"\`g \
|
|
-e s\`'$ENV::EASYRSA_REQ_SERIAL'\`\""$EASYRSA_REQ_SERIAL"\"\`g \
|
|
"$EASYRSA_SSL_CONF" > "$easyrsa_safe_ssl_conf" || \
|
|
die "easyrsa_openssl - sed failed"
|
|
|
|
# Restore standard indentation
|
|
else
|
|
# Assign safe temp file as: Use original EasyRSA-OpenSSL conf
|
|
easyrsa_safe_ssl_conf="$EASYRSA_SSL_CONF"
|
|
fi
|
|
|
|
# set $OPENSSL_CONF - Use which-ever file is assigned above
|
|
export OPENSSL_CONF="$easyrsa_safe_ssl_conf"
|
|
|
|
# Execute command
|
|
if [ "$openssl_command" = "makesafeconf" ]; then
|
|
# move temp file to safessl-easyrsa.cnf
|
|
mv -f "$easyrsa_safe_ssl_conf" "$EASYRSA_SAFE_CONF" || \
|
|
die "easyrsa_openssl - makesafeconf failed"
|
|
|
|
elif [ "$has_config" ]; then
|
|
# debug log on
|
|
if [ "$EASYRSA_DEBUG" ]; then print "<< DEBUG-ON >>"; set -x; fi
|
|
|
|
# Exec SSL with -config temp-file
|
|
"$EASYRSA_OPENSSL" "$openssl_command" \
|
|
-config "$easyrsa_safe_ssl_conf" "$@" || return
|
|
|
|
# debug log off
|
|
if [ "$EASYRSA_DEBUG" ]; then set +x; print ">> DEBUG-OFF <<"; fi
|
|
|
|
else
|
|
# debug log on
|
|
if [ "$EASYRSA_DEBUG" ]; then print "<< DEBUG-ON >>"; set -x; fi
|
|
|
|
# Exec SSL without -config temp-file
|
|
"$EASYRSA_OPENSSL" "$openssl_command" "$@" || return
|
|
|
|
# debug log off
|
|
if [ "$EASYRSA_DEBUG" ]; then set +x; print ">> DEBUG-OFF <<"; fi
|
|
fi
|
|
} # => easyrsa_openssl()
|
|
|
|
# Verify the SSL library is functional and establish version dependencies
|
|
verify_ssl_lib() {
|
|
# Run once only
|
|
[ "$EASYRSA_SSL_OK" ] && die "verify_ssl_lib - Overloaded"
|
|
EASYRSA_SSL_OK=1
|
|
|
|
# redirect std-err to ignore missing etc/ssl/openssl.cnf file
|
|
val="$("$EASYRSA_OPENSSL" version 2>/dev/null)"
|
|
|
|
# SSL lib name
|
|
case "${val%% *}" in
|
|
# OpenSSL does require a safe config-file for ampersand
|
|
OpenSSL)
|
|
ssl_lib=openssl
|
|
[ "$EASYRSA_NO_SAFE_SSL" ] || require_safe_ssl_conf=1
|
|
;;
|
|
LibreSSL)
|
|
ssl_lib=libressl
|
|
require_safe_ssl_conf=1
|
|
;;
|
|
*)
|
|
error_msg="$("$EASYRSA_OPENSSL" version 2>&1)"
|
|
die "\
|
|
Invalid SSL output for 'version':
|
|
|
|
$error_msg"
|
|
esac
|
|
|
|
# Set SSL version dependent $no_password option
|
|
osslv_major="${val#* }"
|
|
osslv_major="${osslv_major%%.*}"
|
|
case "$osslv_major" in
|
|
1) no_password='-nodes' ;;
|
|
2) no_password='-nodes' ;;
|
|
3)
|
|
case "$ssl_lib" in
|
|
openssl) no_password='-noenc' ;;
|
|
libressl) no_password='-nodes' ;;
|
|
*) die "Unsupported SSL library: $ssl_lib"
|
|
esac
|
|
;;
|
|
*) die "Unsupported SSL library: $osslv_major"
|
|
esac
|
|
ssl_version="$val"
|
|
} # => verify_ssl_lib()
|
|
|
|
# Basic sanity-check of PKI init and complain if missing
|
|
verify_pki_init() {
|
|
help_note="Run easyrsa without commands for usage and command help."
|
|
|
|
# Check for defined EASYRSA_PKI
|
|
[ "$EASYRSA_PKI" ] || die "\
|
|
EASYRSA_PKI env-var undefined"
|
|
|
|
# check that the pki dir exists
|
|
[ -d "$EASYRSA_PKI" ] || die "\
|
|
EASYRSA_PKI does not exist (perhaps you need to run init-pki)?
|
|
Expected to find the EASYRSA_PKI at: $EASYRSA_PKI
|
|
$help_note"
|
|
|
|
# verify expected dirs present:
|
|
for i in private reqs; do
|
|
[ -d "$EASYRSA_PKI/$i" ] || die "\
|
|
Missing expected directory: $i (perhaps you need to run init-pki?)
|
|
$help_note"
|
|
done
|
|
unset -v help_note
|
|
} # => verify_pki_init()
|
|
|
|
# Verify core CA files present
|
|
verify_ca_init() {
|
|
# First check the PKI has been initialized
|
|
verify_pki_init
|
|
|
|
help_note="Run without commands for usage and command help."
|
|
|
|
# Verify expected files are present. Allow files to be regular files
|
|
# (or symlinks), but also pipes, for flexibility with ca.key
|
|
for i in ca.crt private/ca.key index.txt index.txt.attr serial; do
|
|
if [ ! -f "$EASYRSA_PKI/$i" ] && [ ! -p "$EASYRSA_PKI/$i" ]; then
|
|
[ "$1" = "test" ] && return 1
|
|
die "\
|
|
Missing expected CA file: $i (perhaps you need to run build-ca?)
|
|
$help_note"
|
|
fi
|
|
done
|
|
|
|
# When operating in 'test' mode, return success.
|
|
# test callers don't care about CA-specific dir structure
|
|
[ "$1" = "test" ] && return 0
|
|
|
|
# verify expected CA-specific dirs:
|
|
for i in issued certs_by_serial
|
|
do
|
|
[ -d "$EASYRSA_PKI/$i" ] || die "\
|
|
Missing expected CA dir: $i (perhaps you need to run build-ca?)
|
|
$help_note"
|
|
done
|
|
|
|
# explicitly return success for callers
|
|
unset -v help_note
|
|
return 0
|
|
} # => verify_ca_init()
|
|
|
|
# init-pki backend:
|
|
init_pki() {
|
|
# Process command options
|
|
reset="hard"
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
hard-reset|hard) reset="hard" ;;
|
|
soft-reset|soft) reset="soft"; old_vars_true=1 ;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# If EASYRSA_PKI exists, confirm before deletion
|
|
if [ -e "$EASYRSA_PKI" ]; then
|
|
confirm "Confirm removal: " "yes" "\
|
|
WARNING!!!
|
|
|
|
You are about to remove the EASYRSA_PKI at:
|
|
* $EASYRSA_PKI
|
|
|
|
and initialize a fresh PKI here."
|
|
|
|
# now remove it:
|
|
case "$reset" in
|
|
hard)
|
|
|
|
# Promote use of soft init
|
|
confirm "Remove current 'vars' file? " yes "\
|
|
* SECOND WARNING!!!
|
|
|
|
* This will remove everything in your current PKI directory.
|
|
To keep your current settings use 'init-pki soft' instead.
|
|
Using 'init-pki soft' is recommended."
|
|
|
|
# # # shellcheck disable=SC2115 # Use "${var:?}"
|
|
rm -rf "$EASYRSA_PKI" || \
|
|
die "init-pki hard reset failed."
|
|
|
|
# If vars was in the old pki, it has been removed
|
|
# If vars was somewhere else, it is user defined
|
|
# Clear found_vars, we MUST not find pki/vars
|
|
[ "$vars_in_pki" ] && unset -v found_vars
|
|
;;
|
|
soft)
|
|
# There is no unit test for a soft reset
|
|
for i in ca.crt \
|
|
issued private reqs inline revoked renewed \
|
|
serial serial.old index.txt index.txt.old \
|
|
index.txt.attr index.txt.attr.old \
|
|
ecparams certs_by_serial
|
|
do
|
|
# # # shellcheck disable=SC2115 # Use "${var:?}"
|
|
rm -rf "$EASYRSA_PKI/$i" || \
|
|
die "init-pki soft reset failed."
|
|
done
|
|
;;
|
|
*)
|
|
die "Unknown reset type: $reset"
|
|
esac
|
|
fi
|
|
|
|
# new dirs:
|
|
for i in private reqs; do
|
|
mkdir -p "$EASYRSA_PKI/$i" || \
|
|
die "\
|
|
Failed to create PKI file structure (permissions?)"
|
|
done
|
|
|
|
# for 'init-pki' create a secure_session
|
|
secure_session || die "\
|
|
init_pki - secure_session failed."
|
|
|
|
# Install data-files into ALL new PKIs
|
|
install_data_to_pki init-pki || \
|
|
warn "\
|
|
Failed to install required data-files to PKI. (init)"
|
|
|
|
notice "\
|
|
'init-pki' complete; you may now create a CA or requests.
|
|
|
|
Your newly created PKI dir is:
|
|
* $EASYRSA_PKI"
|
|
|
|
# Installation information
|
|
# if $no_new_vars then there are one or more known vars
|
|
# which are not in the PKI. All further commands will fail
|
|
# until vars is manually corrected
|
|
[ "$no_new_vars" ] || information "\
|
|
Using Easy-RSA configuration: $vars"
|
|
|
|
# For new PKIs , pki/vars was auto-created, show message
|
|
if [ "$new_vars_true" ]; then
|
|
information "\
|
|
IMPORTANT: \
|
|
Easy-RSA 'vars' template file has been created in your new PKI.
|
|
\
|
|
Edit this 'vars' file to customise the settings for your PKI.
|
|
\
|
|
To use a global vars file, use global option --vars=<YOUR_VARS>"
|
|
|
|
elif [ "$user_vars_true" ] || [ "$old_vars_true" ] || \
|
|
[ "$no_new_vars" ]
|
|
then
|
|
: # ok - User defined, old or no vars file exist
|
|
else
|
|
# Not in PKI and not user defined
|
|
prefer_vars_in_pki_msg
|
|
fi
|
|
information "Using x509-types directory: $EASYRSA_EXT_DIR"
|
|
} # => init_pki()
|
|
|
|
# Must be used in two places, so made it a function
|
|
prefer_vars_in_pki_msg() {
|
|
information "\
|
|
The preferred location for 'vars' is within the PKI folder.
|
|
To silence this message move your 'vars' file to your PKI
|
|
or declare your 'vars' file with option: --vars=<FILE>"
|
|
} # => prefer_vars_in_pki_msg()
|
|
|
|
# Copy data-files from various sources
|
|
install_data_to_pki() {
|
|
#
|
|
# Explicitly find and optionally copy data-files to the PKI.
|
|
# During 'init-pki' this is the new default.
|
|
# During all other functions these requirements are tested for
|
|
# and files will be copied to the PKI, if they do not already
|
|
# exist there.
|
|
#
|
|
# One reason for this is to make packaging work.
|
|
|
|
context="$1"
|
|
shift
|
|
|
|
# Set required sources
|
|
vars_file='vars'
|
|
vars_file_example='vars.example'
|
|
ssl_cnf_file='openssl-easyrsa.cnf'
|
|
x509_types_dir='x509-types'
|
|
|
|
# "$EASYRSA_PKI" - Preferred
|
|
# "$EASYRSA" - Old default and Windows
|
|
# "$PWD" - Usually the same as above, avoid
|
|
# "${0%/*}" - Usually the same as above, avoid
|
|
# '/usr/local/share/easy-rsa' - Default user installed
|
|
# '/usr/share/easy-rsa' - Default system installed
|
|
# Room for more..
|
|
# '/etc/easy-rsa' - Last resort
|
|
|
|
# Find and optionally copy data-files, in specific order
|
|
for area in \
|
|
"$EASYRSA_PKI" \
|
|
"$EASYRSA" \
|
|
"$PWD" \
|
|
"${0%/*}" \
|
|
'/usr/local/share/easy-rsa' \
|
|
'/usr/share/easy-rsa' \
|
|
'/etc/easy-rsa' \
|
|
# EOL
|
|
do
|
|
if [ "$context" = x509-types-only ]; then
|
|
# Find x509-types ONLY
|
|
# Declare in preferred order, first wins
|
|
# beaten by command line.
|
|
[ -e "${area}/${x509_types_dir}" ] && set_var \
|
|
EASYRSA_EXT_DIR "${area}/${x509_types_dir}"
|
|
else
|
|
# Find x509-types ALSO
|
|
# Declare in preferred order, first wins
|
|
# beaten by command line.
|
|
[ -e "${area}/${x509_types_dir}" ] && set_var \
|
|
EASYRSA_EXT_DIR "${area}/${x509_types_dir}"
|
|
|
|
# Find other files - Omitting "$vars_file"
|
|
for source in \
|
|
"$vars_file_example" \
|
|
"$ssl_cnf_file" \
|
|
# EOL
|
|
do
|
|
# Find each item
|
|
[ -e "${area}/${source}" ] || continue
|
|
|
|
# If source does not exist in PKI then copy it
|
|
if [ -e "${EASYRSA_PKI}/${source}" ]; then
|
|
continue
|
|
else
|
|
cp "${area}/${source}" "$EASYRSA_PKI" || die \
|
|
"Failed to copy to PKI: ${area}/${source}"
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
|
|
# Short circuit for x509-types-only
|
|
[ "$context" = x509-types-only ] && return
|
|
|
|
# Create PKI/vars from PKI/example
|
|
unset -v new_vars_true
|
|
if [ "$found_vars" ] || [ "$user_vars_true" ] || \
|
|
[ "$no_new_vars" ]
|
|
then
|
|
: # ok - Do not make a PKI/vars if another vars exists
|
|
else
|
|
case "$context" in
|
|
init-pki)
|
|
# Only create for 'init-pki', if one does not exist
|
|
# 'init-pki soft' should have it's own 'vars' file
|
|
if [ -e "${EASYRSA_PKI}/${vars_file_example}" ] && \
|
|
[ ! -e "${EASYRSA_PKI}/${vars_file}" ]
|
|
then
|
|
# Failure means that no vars will exist and
|
|
# 'cp' will generate an error message
|
|
# This is not a fatal error
|
|
cp "${EASYRSA_PKI}/${vars_file_example}" \
|
|
"${EASYRSA_PKI}/${vars_file}" && \
|
|
new_vars_true=1
|
|
fi
|
|
|
|
# Use set_var to set vars, do not clobber $vars
|
|
set_var vars "${EASYRSA_PKI}/${vars_file}"
|
|
;;
|
|
vars-setup)
|
|
: ;; # No change to current 'vars' required
|
|
x509-types-only)
|
|
die "install_data_to_pki - unexpected context" ;;
|
|
'')
|
|
die "install_data_to_pki - unspecified context" ;;
|
|
*)
|
|
die "install_data_to_pki - unknown context: $context"
|
|
esac
|
|
fi
|
|
|
|
# Check PKI is updated - Omit unnecessary checks
|
|
[ -e "${EASYRSA_PKI}/${ssl_cnf_file}" ] || \
|
|
die "install_data_to_pki - Missing: '$ssl_cnf_file'"
|
|
[ -d "$EASYRSA_EXT_DIR" ] || \
|
|
die "install_data_to_pki - Missing: '$x509_types_dir'"
|
|
|
|
} # => install_data_to_pki ()
|
|
|
|
# Disable terminal echo, if possible, otherwise warn
|
|
hide_read_pass()
|
|
{
|
|
# 3040 - In POSIX sh, set option [name] is undefined
|
|
# 3045 - In POSIX sh, some-command-with-flag is undefined
|
|
# shellcheck disable=SC3040,SC3045
|
|
if stty -echo 2>/dev/null; then
|
|
read -r "$@"
|
|
stty echo
|
|
elif (set +o echo 2>/dev/null); then
|
|
set +o echo
|
|
read -r "$@"
|
|
set -o echo
|
|
elif (echo | read -r -s 2>/dev/null) ; then
|
|
read -r -s "$@"
|
|
else
|
|
warn "\
|
|
Could not disable echo. Password will be shown on screen!"
|
|
read -r "$@"
|
|
fi
|
|
|
|
return 0
|
|
} # => hide_read_pass()
|
|
|
|
# Get passphrase
|
|
get_passphrase() {
|
|
while :; do
|
|
r=""
|
|
t="$1"; shift || die "password malfunction"
|
|
printf '\n%s' "$*"
|
|
hide_read_pass r
|
|
|
|
if [ "${#r}" -lt 4 ]; then
|
|
printf '\n%s\n' \
|
|
"Passphrase must be at least 4 characters!"
|
|
else
|
|
force_set_var "$t" "$r" || die "Passphrase error!"
|
|
unset -v r t
|
|
print
|
|
return 0
|
|
fi
|
|
done
|
|
} # => get_passphrase()
|
|
|
|
# build-ca backend:
|
|
build_ca() {
|
|
cipher="-aes256"
|
|
unset -v sub_ca ssl_batch date_stamp x509
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
intca|subca) sub_ca=1 ;;
|
|
nopass)
|
|
[ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1
|
|
;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# Verify PKI has been initialised
|
|
verify_pki_init
|
|
|
|
out_key="$EASYRSA_PKI/private/ca.key"
|
|
# setup for an intermediate CA
|
|
if [ "$sub_ca" ]; then
|
|
# Gerate a CSR
|
|
out_file="$EASYRSA_PKI/reqs/ca.req"
|
|
else
|
|
# Gerate a certificate
|
|
out_file="$EASYRSA_PKI/ca.crt"
|
|
date_stamp=1
|
|
x509=1
|
|
fi
|
|
|
|
# If encrypted then create the CA key with AES256 cipher
|
|
if [ "$EASYRSA_NO_PASS" ]; then
|
|
unset -v cipher
|
|
else
|
|
unset -v no_password
|
|
fi
|
|
|
|
# Test for existing CA, and complain if already present
|
|
if verify_ca_init test; then
|
|
die "\
|
|
Unable to create a CA as you already seem to have one set up.
|
|
If you intended to start a new CA, run init-pki first."
|
|
fi
|
|
|
|
# If a private key exists, an intermediate ca was created
|
|
# but not signed.
|
|
# Notify user and require a signed ca.crt or a init-pki:
|
|
if [ -f "$out_key" ]; then
|
|
die "\
|
|
A CA private key exists but no ca.crt is found in your PKI:
|
|
$EASYRSA_PKI
|
|
Refusing to create a new CA as this would overwrite your
|
|
current CA. To start a new CA, run init-pki first."
|
|
fi
|
|
|
|
# Cert type must exist under the EASYRSA_EXT_DIR
|
|
[ -e "$EASYRSA_EXT_DIR/ca" ] || die "\
|
|
Missing X509-type 'ca'"
|
|
[ -e "$EASYRSA_EXT_DIR/COMMON" ] || die "\
|
|
Missing X509-type 'COMMON'"
|
|
|
|
# Check for insert-marker in ssl config file
|
|
if grep -q '^#%CA_X509_TYPES_EXTRA_EXTS%' \
|
|
"$EASYRSA_SSL_CONF"
|
|
then
|
|
: # [ "$EASYRSA_BATCH" ] || print
|
|
else
|
|
warn "\
|
|
This openssl config file does not support X509-type 'ca'.
|
|
* $EASYRSA_SSL_CONF
|
|
Please update openssl-easyrsa.cnf to the latest release."
|
|
fi
|
|
|
|
# create necessary files and dirs:
|
|
err_msg="\
|
|
Unable to create necessary PKI files (permissions?)"
|
|
for i in issued inline certs_by_serial \
|
|
revoked/certs_by_serial revoked/private_by_serial \
|
|
revoked/reqs_by_serial
|
|
do
|
|
mkdir -p "$EASYRSA_PKI/$i" || die "$err_msg"
|
|
done
|
|
printf "" > "$EASYRSA_PKI/index.txt" || \
|
|
die "$err_msg"
|
|
attrib='unique_subject = no'
|
|
printf '%s\n' "$attrib" > "$EASYRSA_PKI/index.txt.attr" || \
|
|
die "$err_msg"
|
|
printf '%s\n' "01" > "$EASYRSA_PKI/serial" || \
|
|
die "$err_msg"
|
|
unset -v err_msg
|
|
|
|
# Set ssl batch mode, as required
|
|
# --req-cn must be used with --batch,
|
|
# otherwise use default
|
|
if [ "$EASYRSA_BATCH" ]; then
|
|
ssl_batch=1
|
|
else
|
|
export EASYRSA_REQ_CN=ChangeMe
|
|
fi
|
|
|
|
# Default CA commonName
|
|
if [ "$EASYRSA_REQ_CN" = ChangeMe ]; then
|
|
if [ "$sub_ca" ]; then
|
|
export EASYRSA_REQ_CN="Easy-RSA Sub-CA"
|
|
else
|
|
export EASYRSA_REQ_CN="Easy-RSA CA"
|
|
fi
|
|
fi
|
|
|
|
# Assign cert and key temp files
|
|
out_key_tmp="$(easyrsa_mktemp)" || \
|
|
die "Failed to create temp-key file"
|
|
out_file_tmp="$(easyrsa_mktemp)" || \
|
|
die "Failed to create temp-cert file"
|
|
|
|
# Get passphrase from user if necessary
|
|
if [ "$EASYRSA_NO_PASS" ]
|
|
then
|
|
: # No passphrase required
|
|
|
|
elif [ "$EASYRSA_PASSOUT" ] && [ "$EASYRSA_PASSIN" ]
|
|
then
|
|
: # passphrase defined
|
|
|
|
else
|
|
# Assign passphrase vars and temp file
|
|
in_key_pass_tmp="$(easyrsa_mktemp)" || \
|
|
die "in_key_pass_tmp: create"
|
|
out_key_pass_tmp="$(easyrsa_mktemp)" || \
|
|
die "out_key_pass_tmp: create"
|
|
|
|
p=""
|
|
q=""
|
|
# Get passphrase p
|
|
get_passphrase p \
|
|
"Enter New CA Key Passphrase: "
|
|
|
|
# Confirm passphrase q
|
|
get_passphrase q \
|
|
"Confirm New CA Key Passphrase: "
|
|
|
|
# Validate passphrase
|
|
if [ "$p" ] && [ "$p" = "$q" ]; then
|
|
printf "%s" "$p" > "$in_key_pass_tmp" || \
|
|
die "in_key_pass_tmp: write"
|
|
printf "%s" "$p" > "$out_key_pass_tmp" || \
|
|
die "out_key_pass_tmp: write"
|
|
unset -v p q
|
|
else
|
|
die "Passphrases do not match!"
|
|
fi
|
|
fi
|
|
|
|
# Insert x509-types COMMON and 'ca' and EASYRSA_EXTRA_EXTS
|
|
# shellcheck disable=SC2016 # vars don't expand in ''
|
|
awkscript='\
|
|
{if ( match($0, "^#%CA_X509_TYPES_EXTRA_EXTS%") )
|
|
{ while ( getline<"/dev/stdin" ) {print} next }
|
|
{print}
|
|
}'
|
|
|
|
conf_tmp="$(easyrsa_mktemp)" || die "conf_tmp: create"
|
|
{
|
|
cat "$EASYRSA_EXT_DIR/ca" "$EASYRSA_EXT_DIR/COMMON"
|
|
[ "$EASYRSA_EXTRA_EXTS" ] && print "$EASYRSA_EXTRA_EXTS"
|
|
} | \
|
|
awk "$awkscript" "$EASYRSA_SSL_CONF" \
|
|
> "$conf_tmp" \
|
|
|| die "Copying X509_TYPES to config file failed"
|
|
# Use this new SSL config for the rest of this function
|
|
EASYRSA_SSL_CONF="$conf_tmp"
|
|
|
|
# Generate CA Key
|
|
case "$EASYRSA_ALGO" in
|
|
rsa)
|
|
easyrsa_openssl genpkey -algorithm "$EASYRSA_ALGO" \
|
|
-pkeyopt rsa_keygen_bits:"$EASYRSA_ALGO_PARAMS" \
|
|
-out "$out_key_tmp" \
|
|
${cipher+ "$cipher"} \
|
|
${EASYRSA_PASSOUT:+ -pass "$EASYRSA_PASSOUT"} \
|
|
${out_key_pass_tmp:+ -pass file:"$out_key_pass_tmp"} \
|
|
|| die "Failed create CA private key"
|
|
;;
|
|
ec)
|
|
easyrsa_openssl genpkey -paramfile "$EASYRSA_ALGO_PARAMS" \
|
|
-out "$out_key_tmp" \
|
|
${cipher+ "$cipher"} \
|
|
${EASYRSA_PASSOUT:+ -pass "$EASYRSA_PASSOUT"} \
|
|
${out_key_pass_tmp:+ -pass file:"$out_key_pass_tmp"} \
|
|
|| die "Failed create CA private key"
|
|
;;
|
|
ed)
|
|
easyrsa_openssl genpkey -algorithm "$EASYRSA_CURVE" \
|
|
-out "$out_key_tmp" \
|
|
${cipher+ "$cipher"} \
|
|
${EASYRSA_PASSOUT:+ -pass "$EASYRSA_PASSOUT"} \
|
|
${out_key_pass_tmp:+ -pass file:"$out_key_pass_tmp"} \
|
|
|| die "Failed create CA private key"
|
|
;;
|
|
*) die "Unknown algorithm: $EASYRSA_ALGO"
|
|
esac
|
|
|
|
# Generate the CA keypair:
|
|
# shellcheck disable=SC2086 # Double quote to prevent ..
|
|
easyrsa_openssl req -utf8 -new \
|
|
-key "$out_key_tmp" -keyout "$out_key_tmp" \
|
|
-out "$out_file_tmp" \
|
|
${ssl_batch+ -batch} \
|
|
${x509+ -x509} \
|
|
${date_stamp+ -days "$EASYRSA_CA_EXPIRE"} \
|
|
${EASYRSA_DIGEST+ -"$EASYRSA_DIGEST"} \
|
|
${EASYRSA_NO_PASS+ "$no_password"} \
|
|
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
|
|
${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} \
|
|
${in_key_pass_tmp:+ -passin file:"$in_key_pass_tmp"} \
|
|
${out_key_pass_tmp:+ -passout file:"$out_key_pass_tmp"} \
|
|
|| die "Failed to build the CA certificate"
|
|
|
|
# Remove passphrase temp-file
|
|
if [ -f "$out_key_pass_tmp" ]; then
|
|
rm "$out_key_pass_tmp" || die "\
|
|
Failed to remove the CA passphrase temp-file!"
|
|
fi
|
|
|
|
mv "$out_key_tmp" "$out_key"
|
|
mv "$out_file_tmp" "$out_file"
|
|
|
|
# Success messages
|
|
if [ "$sub_ca" ]; then
|
|
notice "\
|
|
Your intermediate CA request is at:
|
|
* $out_file
|
|
and now must be sent to your parent CA for signing.
|
|
|
|
Place your resulting cert at:
|
|
* $EASYRSA_PKI/ca.crt
|
|
prior to signing operations."
|
|
else
|
|
notice "\
|
|
CA creation complete. Your new CA certificate is at:
|
|
* $out_file"
|
|
fi
|
|
|
|
return 0
|
|
} # => build_ca()
|
|
|
|
# gen-dh backend:
|
|
gen_dh() {
|
|
# Verify PKI has been initialised
|
|
verify_pki_init
|
|
|
|
out_file="$EASYRSA_PKI/dh.pem"
|
|
|
|
# check to see if we already have a dh parameters file
|
|
if [ -e "$out_file" ]; then
|
|
if [ "$EASYRSA_BATCH" ]; then
|
|
# if batch is enabled, die
|
|
die "\
|
|
DH parameters file already exists
|
|
at: $out_file"
|
|
else
|
|
# warn the user, allow to force overwrite
|
|
confirm "Overwrite? " "yes" "\
|
|
DH parameters file already exists
|
|
at: $out_file"
|
|
fi
|
|
fi
|
|
|
|
# Create a temp file
|
|
# otherwise user abort leaves an incomplete dh.pem
|
|
tmp_dh_file="$(easyrsa_mktemp)" || \
|
|
die "Failed to create temp DH file"
|
|
|
|
# Generate dh.pem
|
|
"$EASYRSA_OPENSSL" dhparam -out "$tmp_dh_file" \
|
|
"$EASYRSA_KEY_SIZE" || \
|
|
die "Failed to generate DH params"
|
|
|
|
# Validate dh.pem
|
|
"$EASYRSA_OPENSSL" dhparam -in "$tmp_dh_file" \
|
|
-check -noout || \
|
|
die "Failed to validate DH params"
|
|
|
|
mv -f "$tmp_dh_file" "$out_file" || \
|
|
die "Failed to move temp DH file"
|
|
|
|
notice "
|
|
DH parameters of size $EASYRSA_KEY_SIZE created at:
|
|
* $out_file"
|
|
|
|
return 0
|
|
} # => gen_dh()
|
|
|
|
# gen-req and key backend:
|
|
gen_req() {
|
|
# pull filename, use as default interactive CommonName
|
|
[ "$1" ] || die "\
|
|
Error: gen-req must have a file base as the first argument.
|
|
Run easyrsa without commands for usage and commands."
|
|
|
|
# Initialisation
|
|
unset -v text ssl_batch
|
|
|
|
# Set ssl batch mode and Default commonName, as required
|
|
if [ "$EASYRSA_BATCH" ]; then
|
|
ssl_batch=1
|
|
# If EASYRSA_REQ_CN is set to something other than
|
|
# ChangeMe then keep user defined value
|
|
[ "$EASYRSA_REQ_CN" = ChangeMe ] && \
|
|
export EASYRSA_REQ_CN="$1"
|
|
else
|
|
# --req-cn must be used with --batch
|
|
# otherwise use file-name
|
|
export EASYRSA_REQ_CN="$1"
|
|
fi
|
|
|
|
# Output files
|
|
key_out="$EASYRSA_PKI/private/$1.key"
|
|
req_out="$EASYRSA_PKI/reqs/$1.req"
|
|
|
|
shift # scrape off file-name
|
|
|
|
# Verify PKI has been initialised
|
|
verify_pki_init
|
|
|
|
# function opts support
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
text) text=1 ;;
|
|
nopass)
|
|
[ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1
|
|
;;
|
|
# batch flag supports internal caller build_full()
|
|
batch) ssl_batch=1 ;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# don't wipe out an existing private key without confirmation
|
|
[ -f "$key_out" ] && confirm "Confirm key overwrite: " "yes" "\
|
|
|
|
WARNING!!!
|
|
|
|
An existing private key was found at $key_out
|
|
Continuing with key generation will replace this key."
|
|
|
|
# When EASYRSA_EXTRA_EXTS is defined,
|
|
# append it to openssl's [req] section:
|
|
if [ "$EASYRSA_EXTRA_EXTS" ]; then
|
|
# Check for insert-marker in ssl config file
|
|
if ! grep -q '^#%EXTRA_EXTS%' "$EASYRSA_SSL_CONF"
|
|
then
|
|
die "\
|
|
The copy of openssl-easyrsa.cnf in use \
|
|
does not support request extensions.
|
|
* $EASYRSA_SSL_CONF
|
|
Please update openssl-easyrsa.cnf \
|
|
to the latest official release."
|
|
fi
|
|
|
|
# Setup & insert the extra ext data keyed by a magic line
|
|
extra_exts="
|
|
req_extensions = req_extra
|
|
[ req_extra ]
|
|
$EASYRSA_EXTRA_EXTS"
|
|
# vars don't expand in single quote
|
|
# shellcheck disable=SC2016
|
|
awkscript='
|
|
{if ( match($0, "^#%EXTRA_EXTS%") )
|
|
{ while ( getline<"/dev/stdin" ) {print} next }
|
|
{print}
|
|
}'
|
|
conf_tmp="$(easyrsa_mktemp)" || die "\
|
|
gen_req - easyrsa_mktemp - conf_tmp"
|
|
print "$extra_exts" | \
|
|
awk "$awkscript" "$EASYRSA_SSL_CONF" \
|
|
> "$conf_tmp" \
|
|
|| die "Writing SSL config to temp file failed"
|
|
# Use this SSL config for the rest of this function
|
|
EASYRSA_SSL_CONF="$conf_tmp"
|
|
fi
|
|
|
|
# Name temp files
|
|
key_out_tmp="$(easyrsa_mktemp)" || die "\
|
|
gen_req - easyrsa_mktemp - key_out_tmp"
|
|
req_out_tmp="$(easyrsa_mktemp)" || die "\
|
|
gen_req - easyrsa_mktemp - req_out_tmp"
|
|
|
|
# Set Edwards curve name or elliptic curve parameters file
|
|
algo_opts=""
|
|
if [ "ed" = "$EASYRSA_ALGO" ]; then
|
|
algo_opts="$EASYRSA_CURVE"
|
|
else
|
|
algo_opts="$EASYRSA_ALGO:$EASYRSA_ALGO_PARAMS"
|
|
fi
|
|
|
|
# Generate request
|
|
easyrsa_openssl req -utf8 -new -newkey "$algo_opts" \
|
|
-keyout "$key_out_tmp" -out "$req_out_tmp" \
|
|
${EASYRSA_NO_PASS+ "$no_password"} \
|
|
${text+ -text} \
|
|
${ssl_batch+ -batch} \
|
|
${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} \
|
|
|| die "Failed to generate request"
|
|
|
|
# Move temp-files to target-files
|
|
mv "$key_out_tmp" "$key_out"
|
|
mv "$req_out_tmp" "$req_out"
|
|
|
|
# Success messages
|
|
notice "\
|
|
Keypair and certificate request completed. Your files are:
|
|
* req: $req_out
|
|
* key: $key_out${build_full+ $NL}"
|
|
|
|
return 0
|
|
} # => gen_req()
|
|
|
|
# common signing backend
|
|
sign_req() {
|
|
crt_type="$1"
|
|
req_in="$EASYRSA_PKI/reqs/$2.req"
|
|
crt_out="$EASYRSA_PKI/issued/$2.crt"
|
|
|
|
# Randomize Serial number
|
|
if [ "$EASYRSA_RAND_SN" != "no" ]; then
|
|
i=""
|
|
serial=""
|
|
check_serial=""
|
|
unset -v unique_serial
|
|
for i in 1 2 3 4 5; do
|
|
serial="$(
|
|
easyrsa_random 16
|
|
)" || die "sign_req - easyrsa_random"
|
|
|
|
# Print random $serial to pki/serial file
|
|
# for use by SSL config
|
|
print "$serial" > "$EASYRSA_PKI/serial" || \
|
|
die "sign_req - serial"
|
|
|
|
# Check for duplicate serial in CA db
|
|
# Always errors out - Do not capture error
|
|
check_serial="$(
|
|
easyrsa_openssl ca -status "$serial" 2>&1
|
|
)" || :
|
|
|
|
case "$check_serial" in
|
|
*"not present in db"*)
|
|
unique_serial=1
|
|
break
|
|
esac
|
|
done
|
|
|
|
# Check for unique_serial
|
|
[ "$unique_serial" ] || die "\
|
|
sign_req - Randomize Serial number failed:
|
|
|
|
$check_serial"
|
|
fi
|
|
|
|
verify_ca_init
|
|
|
|
# Check argument sanity:
|
|
[ "$2" ] || die "\
|
|
Incorrect number of arguments provided to sign-req:
|
|
expected 2, got $# (see command help for usage)"
|
|
|
|
# Cert type must exist under the EASYRSA_EXT_DIR
|
|
[ -e "$EASYRSA_EXT_DIR/$crt_type" ] || die "\
|
|
Missing X509-type '$crt_type'"
|
|
[ -e "$EASYRSA_EXT_DIR/COMMON" ] || die "\
|
|
Missing X509-type 'COMMON'"
|
|
|
|
# Cert type must NOT be COMMON
|
|
[ "$crt_type" != COMMON ] || die "\
|
|
Invalid certificate type: '$crt_type'"
|
|
|
|
# Request file must exist
|
|
[ -e "$req_in" ] || die "\
|
|
No request found for the input: '$2'
|
|
Expected to find the request at: $req_in"
|
|
|
|
# Certificate file must NOT exist
|
|
[ ! -e "$crt_out" ] || die "\
|
|
Cannot sign this request for '$2'.
|
|
Conflicting certificate already exists at:
|
|
* $crt_out"
|
|
|
|
# Confirm input is a cert req
|
|
verify_file req "$req_in" || die "\
|
|
The certificate request file is not in a valid X509 format:
|
|
* $req_in"
|
|
|
|
# Get fixed dates by --fix-offset
|
|
if [ "$EASYRSA_FIX_OFFSET" ]; then
|
|
fixed_cert_dates "$EASYRSA_FIX_OFFSET" \
|
|
start_fixdate end_fixdate
|
|
fi
|
|
|
|
# When EASYRSA_CP_EXT is defined,
|
|
# adjust openssl's [default_ca] section:
|
|
if [ "$EASYRSA_CP_EXT" ]; then
|
|
# Check for insert-marker in ssl config file
|
|
if ! grep -q '^#%COPY_EXTS%' "$EASYRSA_SSL_CONF"
|
|
then
|
|
die "\
|
|
The copy of openssl-easyrsa.cnf in use \
|
|
does not support --copy-ext.
|
|
* $EASYRSA_SSL_CONF
|
|
Please update openssl-easyrsa.cnf \
|
|
to the latest official release."
|
|
fi
|
|
|
|
# Setup & insert the copy_extensions data
|
|
# keyed by a magic line
|
|
copy_exts="copy_extensions = copy"
|
|
# shellcheck disable=SC2016 # vars don't expand ''
|
|
awkscript='
|
|
{if ( match($0, "^#%COPY_EXTS%") )
|
|
{ while ( getline<"/dev/stdin" ) {print} next }
|
|
{print}
|
|
}'
|
|
conf_tmp="$(easyrsa_mktemp)" || \
|
|
die "sign_req - easyrsa_mktemp - conf_tmp"
|
|
print "$copy_exts" | \
|
|
awk "$awkscript" "$EASYRSA_SSL_CONF" \
|
|
> "$conf_tmp" \
|
|
|| die "Writing SSL config to temp file failed"
|
|
# Use this SSL config for the rest of this function
|
|
EASYRSA_SSL_CONF="$conf_tmp"
|
|
fi
|
|
|
|
# Generate the extensions file for this cert:
|
|
ext_tmp="$(easyrsa_mktemp)" || \
|
|
die "sign_req - easyrsa_mktemp - ext_tmp"
|
|
{
|
|
# Append COMMON and cert-type extensions
|
|
cat "$EASYRSA_EXT_DIR/COMMON" || \
|
|
die "Failed to read X509-type COMMON"
|
|
cat "$EASYRSA_EXT_DIR/$crt_type" || \
|
|
die "Failed to read X509-type $crt_type"
|
|
|
|
# Support a dynamic CA path length when present:
|
|
if [ "$crt_type" = "ca" ] && [ "$EASYRSA_SUBCA_LEN" ]
|
|
then
|
|
# Print the last occurence of basicContraints in
|
|
# x509-types/ca
|
|
# If basicContraints is not defined then bail
|
|
# shellcheck disable=SC2016 # vars don't expand ''
|
|
awkscript='\
|
|
/^[[:blank:]]*basicConstraints[[:blank:]]*=/ { bC=$0 }
|
|
END { if (length(bC) == 0 ) exit 1; print bC }'
|
|
basicConstraints="$(
|
|
awk "$awkscript" "$EASYRSA_EXT_DIR/$crt_type"
|
|
)" || die "\
|
|
basicConstraints is not defined, cannot use 'pathlen'"
|
|
print "$basicConstraints, pathlen:$EASYRSA_SUBCA_LEN"
|
|
unset -v basicConstraints
|
|
fi
|
|
|
|
# Deprecated Netscape extension support
|
|
case "$EASYRSA_NS_SUPPORT" in
|
|
[yY][eE][sS])
|
|
|
|
# Netscape extension
|
|
case "$crt_type" in
|
|
serverClient)
|
|
print "nsCertType = serverClient" ;;
|
|
server)
|
|
print "nsCertType = server" ;;
|
|
client)
|
|
print "nsCertType = client" ;;
|
|
ca)
|
|
print "nsCertType = sslCA" ;;
|
|
*)
|
|
die "Unknown certificate type: $crt_type"
|
|
esac
|
|
|
|
# Netscape comment
|
|
[ "$EASYRSA_NS_COMMENT" ] && \
|
|
print "nsComment = \"$EASYRSA_NS_COMMENT\""
|
|
;;
|
|
*)
|
|
: # ok No NS support required
|
|
esac
|
|
|
|
# Add user SAN from --subject-alt-name
|
|
if [ "$user_san_true" ]; then
|
|
print "$EASYRSA_EXTRA_EXTS"
|
|
else
|
|
# or default server SAN
|
|
# If type is server and no subjectAltName was
|
|
# requested then add one to the extensions file
|
|
if [ "$crt_type" = 'server' ] || \
|
|
[ "$crt_type" = 'serverClient' ];
|
|
then
|
|
# req san or default server SAN
|
|
san="$(display_san req "$req_in")"
|
|
if [ "$san" ]; then
|
|
print "subjectAltName = $san"
|
|
else
|
|
default_server_san "$req_in"
|
|
fi
|
|
fi
|
|
|
|
# Add user set EASYRSA_EXTRA_EXTS
|
|
[ -z "$EASYRSA_EXTRA_EXTS" ] || \
|
|
print "$EASYRSA_EXTRA_EXTS"
|
|
fi
|
|
} > "$ext_tmp" || die "\
|
|
Failed to create temp extension file (bad permissions?) at:
|
|
* $ext_tmp"
|
|
|
|
# Display the request subject in an easy-to-read format
|
|
# Confirm the user wishes to sign this request
|
|
# Support batch by internal caller:
|
|
confirm "Confirm request details: " "yes" "\
|
|
You are about to sign the following certificate.
|
|
Please check over the details shown below for accuracy. \
|
|
Note that this request
|
|
has not been cryptographically verified. Please be sure \
|
|
it came from a trusted
|
|
source or that you have verified the request checksum \
|
|
with the sender.
|
|
|
|
Request subject, to be signed as a $crt_type certificate \
|
|
for $EASYRSA_CERT_EXPIRE days:
|
|
|
|
$(display_dn req "$req_in")
|
|
" # => confirm end
|
|
|
|
# Assign temp cert file
|
|
crt_out_tmp="$(easyrsa_mktemp)" || \
|
|
die "sign_req - easyrsa_mktemp - crt_out_tmp"
|
|
|
|
# sign request
|
|
easyrsa_openssl ca -utf8 -in "$req_in" \
|
|
-out "$crt_out_tmp" -extfile "$ext_tmp" \
|
|
-days "$EASYRSA_CERT_EXPIRE" -batch \
|
|
${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} \
|
|
${EASYRSA_NO_TEXT:+-notext} \
|
|
${EASYRSA_FIX_OFFSET+ -startdate "$start_fixdate"} \
|
|
${EASYRSA_FIX_OFFSET+ -enddate "$end_fixdate"} \
|
|
|| die "\
|
|
Signing failed (openssl output above may have more detail)"
|
|
|
|
mv "$crt_out_tmp" "$crt_out" || \
|
|
die "Failed to move temp-file to certificate."
|
|
|
|
# Success messages
|
|
notice "\
|
|
Certificate created at:
|
|
* $crt_out"
|
|
|
|
return 0
|
|
} # => sign_req()
|
|
|
|
# common build backend
|
|
# used to generate+sign in 1 step
|
|
build_full() {
|
|
verify_ca_init
|
|
|
|
# pull filename base:
|
|
[ "$2" ] || die "\
|
|
Error: didn't find a file base name as the first argument.
|
|
Run easyrsa without commands for usage and commands."
|
|
|
|
crt_type="$1"
|
|
name="$2"
|
|
shift 2
|
|
|
|
req_out="$EASYRSA_PKI/reqs/$name.req"
|
|
key_out="$EASYRSA_PKI/private/$name.key"
|
|
crt_out="$EASYRSA_PKI/issued/$name.crt"
|
|
|
|
# function opts support
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
nopass)
|
|
[ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1
|
|
;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# abort on existing req/key/crt files
|
|
err_exists="\
|
|
file already exists. Aborting build to avoid overwriting this file.
|
|
If you wish to continue, please use a different name.
|
|
Matching file found at: "
|
|
[ -e "$req_out" ] && die "Request $err_exists $req_out"
|
|
[ -e "$key_out" ] && die "Key $err_exists $key_out"
|
|
[ -e "$crt_out" ] && die "Certificate $err_exists $crt_out"
|
|
unset -v err_exists
|
|
|
|
# Make inline directory
|
|
[ -d "$EASYRSA_PKI/inline" ] || \
|
|
mkdir -p "$EASYRSA_PKI/inline" || \
|
|
die "Failed to create inline directoy."
|
|
|
|
# Confirm over write inline file
|
|
inline_out="$EASYRSA_PKI/inline/$name.inline"
|
|
[ -e "$inline_out" ] && \
|
|
confirm "Confirm OVER-WRITE existing inline file ? " y "\
|
|
Warning!
|
|
|
|
An inline file for name '$name' already exists:
|
|
* $inline_out"
|
|
|
|
# Set commonName
|
|
[ "$EASYRSA_REQ_CN" = ChangeMe ] || die "\
|
|
Option conflict:
|
|
* '$cmd' does not support setting an external commonName"
|
|
EASYRSA_REQ_CN="$name"
|
|
|
|
# create request
|
|
build_full=1
|
|
gen_req "$name" batch
|
|
|
|
# Sign it
|
|
error_build_full_cleanup=1
|
|
if sign_req "$crt_type" "$name"; then
|
|
unset -v error_build_full_cleanup
|
|
else
|
|
die "\
|
|
Failed to sign '$name' - \
|
|
See error messages above for details."
|
|
fi
|
|
|
|
# inline it
|
|
if inline_creds "$name" > "$inline_out"; then
|
|
notice "\
|
|
Inline file created:
|
|
* $inline_out"
|
|
else
|
|
warn "\
|
|
Failed to write inline file:
|
|
* $inline_out"
|
|
fi
|
|
|
|
return 0
|
|
} # => build_full()
|
|
|
|
# Create inline credentials file for this node
|
|
inline_creds ()
|
|
{
|
|
[ "$1" ] || die "inline_creds - Name missing"
|
|
printf "%s\n\n" "# $crt_type: $1"
|
|
printf "%s\n" "<cert>"
|
|
cat "$crt_out"
|
|
printf "%s\n\n" "</cert>"
|
|
printf "%s\n" "<key>"
|
|
[ -e "$key_out" ] && cat "$key_out"
|
|
printf "%s\n\n" "</key>"
|
|
printf "%s\n" "<ca>"
|
|
cat "$EASYRSA_PKI/ca.crt"
|
|
printf "%s\n\n" "</ca>"
|
|
} # => inline_creds ()
|
|
|
|
# revoke backend
|
|
revoke() {
|
|
# pull filename base:
|
|
[ "$1" ] || die "\
|
|
Error: didn't find a file base name as the first argument.
|
|
Run easyrsa without commands for usage and command help."
|
|
|
|
verify_ca_init
|
|
|
|
# Assign file_name_base and dust off!
|
|
file_name_base="$1"
|
|
shift
|
|
|
|
in_dir="$EASYRSA_PKI"
|
|
crt_in="$in_dir/issued/$file_name_base.crt"
|
|
key_in="$in_dir/private/$file_name_base.key"
|
|
req_in="$in_dir/reqs/$file_name_base.req"
|
|
creds_in="$in_dir/$file_name_base.creds"
|
|
inline_in="$in_dir/inline/$file_name_base.inline"
|
|
|
|
# Assign possible "crl_reason"
|
|
if [ "$1" ]; then
|
|
crl_reason="$1"
|
|
shift
|
|
|
|
case "$crl_reason" in
|
|
unspecified) : ;;
|
|
keyCompromise) : ;;
|
|
CACompromise) : ;;
|
|
affiliationChanged) : ;;
|
|
superseded) : ;;
|
|
cessationOfOperation) : ;;
|
|
certificateHold) : ;;
|
|
*) die "Illegal reason: $crl_reason"
|
|
esac
|
|
else
|
|
unset -v crl_reason
|
|
fi
|
|
|
|
# Enforce syntax
|
|
if [ "$1" ]; then
|
|
die "Syntax error: $1"
|
|
fi
|
|
|
|
# referenced cert must exist:
|
|
[ -e "$crt_in" ] || die "\
|
|
Unable to revoke as no certificate was found. Certificate was expected
|
|
at: $crt_in"
|
|
|
|
# Verify certificate
|
|
verify_file x509 "$crt_in" || die "\
|
|
Unable to revoke as the input file is not a valid certificate. Unexpected
|
|
input in file: $crt_in"
|
|
|
|
# Verify request
|
|
if [ -e "$req_in" ]; then
|
|
verify_file req "$req_in" || die "\
|
|
Unable to verify request. The file is not a valid request.
|
|
Unexpected input in file: $req_in"
|
|
fi
|
|
|
|
# get the serial number of the certificate
|
|
ssl_cert_serial "$crt_in" cert_serial
|
|
|
|
duplicate_crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem"
|
|
|
|
# Set out_dir
|
|
out_dir="$EASYRSA_PKI/revoked"
|
|
crt_out="$out_dir/certs_by_serial/$cert_serial.crt"
|
|
key_out="$out_dir/private_by_serial/$cert_serial.key"
|
|
req_out="$out_dir/reqs_by_serial/$cert_serial.req"
|
|
|
|
# NEVER over-write a revoked cert, serial number must be unique
|
|
deny_msg="\
|
|
Cannot revoke this certificate because a conflicting file exists.
|
|
*"
|
|
[ -e "$crt_out" ] && die "$deny_msg certificate: $crt_out"
|
|
[ -e "$key_out" ] && die "$deny_msg private key: $key_out"
|
|
[ -e "$req_out" ] && die "$deny_msg request : $req_out"
|
|
unset -v deny_msg
|
|
|
|
# Check for key and request files
|
|
unset -v if_exist_key_in if_exist_req_in
|
|
[ -e "$key_in" ] && if_exist_key_in="
|
|
* $key_in"
|
|
[ -e "$req_in" ] && if_exist_req_in="
|
|
* $req_in"
|
|
|
|
# confirm operation by displaying DN:
|
|
warn "\
|
|
This process is destructive!
|
|
|
|
These files will be MOVED to the 'revoked' storage directory:
|
|
* $crt_in${if_exist_key_in}${if_exist_req_in}
|
|
|
|
These files will be DELETED:
|
|
All PKCS files for commonName : $file_name_base
|
|
|
|
The inline credentials files:
|
|
* $creds_in
|
|
* $inline_in
|
|
|
|
The duplicate certificate:
|
|
* $duplicate_crt_by_serial"
|
|
|
|
confirm " Continue with revocation: " "yes" "\
|
|
Please confirm you wish to revoke the certificate
|
|
with the following subject:
|
|
|
|
$(display_dn x509 "$crt_in")
|
|
|
|
serial-number: $cert_serial
|
|
|
|
Reason: ${crl_reason-None given}"
|
|
|
|
# Revoke certificate
|
|
easyrsa_openssl ca -utf8 -revoke "$crt_in" \
|
|
${crl_reason+ -crl_reason "$crl_reason"} \
|
|
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
|
|
|| die "\
|
|
Failed to revoke certificate: revocation command failed."
|
|
|
|
# move revoked files
|
|
# so we can reissue certificates with the same name
|
|
revoke_move
|
|
|
|
notice "\
|
|
* IMPORTANT *
|
|
|
|
Revocation was successful. You must run 'gen-crl' and upload a new CRL to your
|
|
infrastructure in order to prevent the revoked certificate from being accepted."
|
|
|
|
return 0
|
|
} # => revoke()
|
|
|
|
# revoke_move
|
|
# moves revoked certificates to the 'revoked' folder
|
|
# allows reissuing certificates with the same name
|
|
revoke_move() {
|
|
for target in "$out_dir" \
|
|
"$out_dir/certs_by_serial" \
|
|
"$out_dir/private_by_serial" \
|
|
"$out_dir/reqs_by_serial"
|
|
do
|
|
[ -d "$target" ] && continue
|
|
mkdir -p "$target" ||
|
|
die "Failed to mkdir: $target"
|
|
done
|
|
|
|
# move crt, key and req file to renewed_then_revoked folders
|
|
mv "$crt_in" "$crt_out" || die "Failed to move: $crt_in"
|
|
|
|
# only move the key if we have it
|
|
if [ -e "$key_in" ]; then
|
|
mv "$key_in" "$key_out" || warn "Failed to move: $key_in"
|
|
fi
|
|
|
|
# only move the req if we have it
|
|
if [ -e "$req_in" ]; then
|
|
mv "$req_in" "$req_out" || warn "Failed to move: $req_in"
|
|
fi
|
|
|
|
# remove any pkcs files
|
|
for pkcs in p12 p7b p8 p1; do
|
|
if [ -e "$in_dir/issued/$file_name_base.$pkcs" ]; then
|
|
# issued
|
|
rm "$in_dir/issued/$file_name_base.$pkcs" ||
|
|
warn "Failed to remove: $file_name_base.$pkcs"
|
|
|
|
elif [ -e "$in_dir/private/$file_name_base.$pkcs" ]; then
|
|
# private
|
|
rm "$in_dir/private/$file_name_base.$pkcs" ||
|
|
warn "Failed to remove: $file_name_base.$pkcs"
|
|
else
|
|
: # ok
|
|
fi
|
|
done
|
|
|
|
# remove the duplicate certificate
|
|
if [ -e "$duplicate_crt_by_serial" ]; then
|
|
rm "$duplicate_crt_by_serial" || warn "\
|
|
Failed to remove the duplicate certificate:
|
|
* $duplicate_crt_by_serial"
|
|
fi
|
|
|
|
# remove credentials file
|
|
if [ -e "$creds_in" ]; then
|
|
rm "$creds_in" || warn "\
|
|
Failed to remove credentials file:
|
|
* $creds_in"
|
|
fi
|
|
|
|
# remove inline file
|
|
if [ -e "$inline_in" ]; then
|
|
rm "$inline_in" || warn "\
|
|
Failed to remove inline file:
|
|
* $inline_in"
|
|
fi
|
|
|
|
return 0
|
|
} # => revoke_move()
|
|
|
|
# renew backend
|
|
renew() {
|
|
# pull filename base:
|
|
[ "$1" ] || die "\
|
|
Error: didn't find a file base name as the first argument.
|
|
Run easyrsa without commands for usage and command help."
|
|
|
|
verify_ca_init
|
|
|
|
# Assign file_name_base and dust off!
|
|
file_name_base="$1"
|
|
shift
|
|
|
|
# Assign input files
|
|
in_dir="$EASYRSA_PKI"
|
|
crt_in="$in_dir/issued/$file_name_base.crt"
|
|
key_in="$in_dir/private/$file_name_base.key"
|
|
# key_out is used by inline_creds()
|
|
key_out="$in_dir/private/$file_name_base.key"
|
|
req_in="$in_dir/reqs/$file_name_base.req"
|
|
creds_in="$in_dir/$file_name_base.creds"
|
|
inline_in="$in_dir/inline/$file_name_base.inline"
|
|
|
|
# Upgrade CA index.txt.attr - unique_subject = no
|
|
up23_upgrade_ca || \
|
|
die "Failed to upgrade CA to support renewal."
|
|
|
|
# deprecate ALL options
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
nopass)
|
|
warn "\
|
|
Option 'nopass' is not supported by command 'renew'."
|
|
;;
|
|
*) die "Unknown option: $1"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# Verify certificate
|
|
if [ -f "$crt_in" ]; then
|
|
verify_file x509 "$crt_in" || die "\
|
|
Input file is not a valid certificate:
|
|
* $crt_in"
|
|
else
|
|
die "\
|
|
Missing certificate file:
|
|
* $crt_in"
|
|
fi
|
|
|
|
# Verify request
|
|
if [ -e "$req_in" ]; then
|
|
verify_file req "$req_in" || die "\
|
|
Input file is not a valid request:
|
|
* $req_in"
|
|
else
|
|
die "\
|
|
Missing request file:
|
|
* $req_in"
|
|
fi
|
|
|
|
# get the serial number of the certificate
|
|
ssl_cert_serial "$crt_in" cert_serial
|
|
|
|
duplicate_crt_by_serial="\
|
|
$EASYRSA_PKI/certs_by_serial/$cert_serial.pem"
|
|
|
|
# Set out_dir
|
|
out_dir="$EASYRSA_PKI/renewed"
|
|
crt_out="$out_dir/issued/$file_name_base.crt"
|
|
|
|
# NEVER over-write a renewed cert, revoke it first
|
|
deny_msg="\
|
|
Cannot renew this certificate, a conflicting file exists:
|
|
*"
|
|
[ -e "$crt_out" ] && die "$deny_msg certificate: $crt_out"
|
|
unset -v deny_msg
|
|
|
|
# Make inline directory
|
|
[ -d "$EASYRSA_PKI/inline" ] || \
|
|
mkdir -p "$EASYRSA_PKI/inline" || \
|
|
die "Failed to create inline directoy."
|
|
|
|
# Extract certificate usage from old cert
|
|
cert_ext_key_usage="$(
|
|
easyrsa_openssl x509 -in "$crt_in" -noout -text |
|
|
sed -n "/X509v3 Extended Key Usage:/{n;s/^ *//g;p;}"
|
|
)"
|
|
|
|
case "$cert_ext_key_usage" in
|
|
"TLS Web Client Authentication")
|
|
cert_type=client
|
|
;;
|
|
"TLS Web Server Authentication")
|
|
cert_type=server
|
|
;;
|
|
"TLS Web Server Authentication, TLS Web Client Authentication")
|
|
cert_type=serverClient
|
|
;;
|
|
*) die "Unknown key usage: $cert_ext_key_usage"
|
|
esac
|
|
|
|
# Use SAN from --san if set else use SAN from old cert
|
|
if echo "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltName; then
|
|
: # ok - Use current subjectAltName
|
|
else
|
|
san="$(
|
|
easyrsa_openssl x509 -in "$crt_in" -noout -text | sed -n \
|
|
"/X509v3 Subject Alternative Name:\
|
|
/{n;s/IP Address:/IP:/g;s/ //g;p;}"
|
|
)"
|
|
|
|
[ "$san" ] && export EASYRSA_EXTRA_EXTS="\
|
|
$EASYRSA_EXTRA_EXTS
|
|
subjectAltName = $san"
|
|
fi
|
|
|
|
# confirm operation by displaying DN:
|
|
warn "\
|
|
This process is destructive!
|
|
|
|
These files will be MOVED to 'renewed' storage directory:
|
|
* $crt_in
|
|
|
|
These files will be DELETED:
|
|
All PKCS files for commonName: $file_name_base
|
|
|
|
The inline credentials files:
|
|
* $creds_in
|
|
* $inline_in
|
|
|
|
The duplicate certificate:
|
|
* $duplicate_crt_by_serial"
|
|
|
|
confirm " Continue with renewal: " "yes" "\
|
|
Please confirm you wish to renew the certificate
|
|
with the following subject:
|
|
|
|
$(display_dn x509 "$crt_in")
|
|
|
|
serial-number: $cert_serial"
|
|
|
|
# move renewed files
|
|
# so we can reissue certificate with the same name
|
|
renew_move
|
|
error_undo_renew_move=1
|
|
|
|
# renew certificate
|
|
if EASYRSA_BATCH=1 sign_req "$cert_type" "$file_name_base"
|
|
then
|
|
unset -v error_undo_renew_move
|
|
else
|
|
# If renew failed then restore cert.
|
|
# Otherwise, issue a warning
|
|
renew_restore_move
|
|
die "\
|
|
Renewal has failed to build a new certificate."
|
|
fi
|
|
|
|
# inline it
|
|
# Over write existing because renew is successful
|
|
if inline_creds "$file_name_base" > "$inline_in"; then
|
|
notice "\
|
|
Inline file created:
|
|
* $inline_in"
|
|
else
|
|
warn "\
|
|
Failed to write inline file:
|
|
* $inline_in"
|
|
fi
|
|
|
|
# Success messages
|
|
notice "\
|
|
Renew was successful.
|
|
|
|
* IMPORTANT *
|
|
|
|
Renew has created a new certificate, to replace the old certificate.
|
|
|
|
To revoke the old certificate, once the new one has been deployed,
|
|
use: 'revoke-renewed $file_name_base reason' ('reason' is optional)"
|
|
|
|
return 0
|
|
} # => renew()
|
|
|
|
# Restore files on failure to renew
|
|
renew_restore_move() {
|
|
unset -v rrm_err error_undo_renew_move
|
|
# restore crt file to PKI folders
|
|
if mv "$restore_crt_out" "$restore_crt_in"; then
|
|
: # ok
|
|
else
|
|
warn "Failed to restore: $restore_crt_out"
|
|
rrm_err=1
|
|
fi
|
|
|
|
# messages
|
|
if [ "$rrm_err" ]; then
|
|
warn "Failed to restore renewed files."
|
|
else
|
|
notice "\
|
|
Renew FAILED but files have been successfully restored."
|
|
fi
|
|
|
|
return 0
|
|
} # => renew_restore_move()
|
|
|
|
# renew_move
|
|
# moves renewed certificates to the 'renewed' folder
|
|
# allows reissuing certificates with the same name
|
|
renew_move() {
|
|
# make sure renewed dirs exist
|
|
for target in "$out_dir" \
|
|
"$out_dir/issued" \
|
|
"$out_dir/private" \
|
|
"$out_dir/reqs"
|
|
do
|
|
[ -d "$target" ] && continue
|
|
mkdir -p "$target" ||
|
|
die "Failed to mkdir: $target"
|
|
done
|
|
|
|
# move crt, key and req file to renewed folders
|
|
# After this point, renew is possible!
|
|
restore_crt_in="$crt_in"
|
|
restore_crt_out="$crt_out"
|
|
mv "$crt_in" "$crt_out" || \
|
|
die "Failed to move: $crt_in"
|
|
|
|
# Further file removal is a convenience, only.
|
|
# remove any pkcs files
|
|
for pkcs in p12 p7b p8 p1; do
|
|
# issued
|
|
rm -f "$in_dir/issued/$file_name_base.$pkcs"
|
|
# private
|
|
rm -f "$in_dir/private/$file_name_base.$pkcs"
|
|
done
|
|
|
|
# remove the duplicate certificate
|
|
if [ -e "$duplicate_crt_by_serial" ]; then
|
|
rm "$duplicate_crt_by_serial" || warn "\
|
|
Failed to remove the duplicate certificate:
|
|
* $duplicate_crt_by_serial"
|
|
fi
|
|
|
|
# remove credentials file
|
|
if [ -e "$creds_in" ]; then
|
|
rm "$creds_in" || warn "\
|
|
Failed to remove credentials file:
|
|
* $creds_in"
|
|
fi
|
|
|
|
# remove inline file
|
|
if [ -e "$inline_in" ]; then
|
|
rm "$inline_in" || warn "\
|
|
Failed to remove inline file:
|
|
* $inline_in"
|
|
fi
|
|
|
|
return 0
|
|
} # => renew_move()
|
|
|
|
# revoke-renewed backend
|
|
revoke_renewed() {
|
|
# pull filename base:
|
|
[ "$1" ] || die "\
|
|
Error: didn't find a file base name as the first argument.
|
|
Run easyrsa without commands for usage and command help."
|
|
|
|
verify_ca_init
|
|
|
|
# Assign file_name_base and dust off!
|
|
file_name_base="$1"
|
|
shift
|
|
|
|
in_dir="$EASYRSA_PKI/renewed"
|
|
crt_in="$in_dir/issued/$file_name_base.crt"
|
|
key_in="$in_dir/private/$file_name_base.key"
|
|
req_in="$in_dir/reqs/$file_name_base.req"
|
|
#creds_in="$EASYRSA_PKI/$file_name_base.creds"
|
|
|
|
# Assign possible "crl_reason"
|
|
if [ "$1" ]; then
|
|
crl_reason="$1"
|
|
shift
|
|
|
|
case "$crl_reason" in
|
|
unspecified) : ;;
|
|
keyCompromise) : ;;
|
|
CACompromise) : ;;
|
|
affiliationChanged) : ;;
|
|
superseded) : ;;
|
|
cessationOfOperation) : ;;
|
|
certificateHold) : ;;
|
|
*) die "Illegal reason: $crl_reason"
|
|
esac
|
|
else
|
|
unset -v crl_reason
|
|
fi
|
|
|
|
# Enforce syntax
|
|
if [ "$1" ]; then
|
|
die "Syntax error: $1"
|
|
fi
|
|
|
|
# referenced cert must exist:
|
|
[ -f "$crt_in" ] || die "\
|
|
Unable to revoke as no renewed certificate was found.
|
|
Certificate was expected at: $crt_in"
|
|
|
|
# Verify certificate
|
|
verify_file x509 "$crt_in" || die "\
|
|
Unable to revoke as the input file is not a valid certificate. Unexpected
|
|
input in file: $crt_in"
|
|
|
|
# Verify request
|
|
if [ -e "$req_in" ]; then
|
|
verify_file req "$req_in" || die "\
|
|
Unable to verify request. The file is not a valid request.
|
|
Unexpected input in file: $req_in"
|
|
fi
|
|
|
|
# get the serial number of the certificate
|
|
ssl_cert_serial "$crt_in" cert_serial
|
|
|
|
duplicate_crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem"
|
|
|
|
# output
|
|
out_dir="$EASYRSA_PKI/revoked"
|
|
crt_out="$out_dir/certs_by_serial/$cert_serial.crt"
|
|
key_out="$out_dir/private_by_serial/$cert_serial.key"
|
|
req_out="$out_dir/reqs_by_serial/$cert_serial.req"
|
|
|
|
# NEVER over-write a revoked cert, serial number must be unique
|
|
deny_msg="\
|
|
Cannot revoke this certificate because a conflicting file exists.
|
|
*"
|
|
[ -e "$crt_out" ] && die "$deny_msg certificate: $crt_out"
|
|
[ -e "$key_out" ] && die "$deny_msg private key: $key_out"
|
|
[ -e "$req_out" ] && die "$deny_msg request : $req_out"
|
|
unset -v deny_msg
|
|
|
|
# confirm operation by displaying DN:
|
|
unset -v if_exist_key_in if_exist_req_in
|
|
[ -e "$key_in" ] && if_exist_key_in="
|
|
* $key_in"
|
|
[ -e "$req_in" ] && if_exist_req_in="
|
|
* $req_in"
|
|
warn "\
|
|
This process is destructive!
|
|
|
|
These files will be moved to the 'revoked' storage sub-directory:
|
|
* $crt_in${if_exist_key_in}${if_exist_req_in}"
|
|
|
|
confirm " Continue with revocation: " "yes" "\
|
|
Please confirm you wish to revoke the renewed certificate
|
|
with the following subject:
|
|
|
|
$(display_dn x509 "$crt_in")
|
|
|
|
serial-number: $cert_serial
|
|
|
|
Reason: ${crl_reason-None given}"
|
|
|
|
# Revoke the old (already renewed) certificate
|
|
easyrsa_openssl ca -utf8 -revoke "$crt_in" \
|
|
${crl_reason+ -crl_reason "$crl_reason"} \
|
|
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
|
|
|| die "Failed to revoke renewed certificate: revocation command failed."
|
|
|
|
# move revoked files
|
|
revoke_renewed_move
|
|
|
|
notice " * IMPORTANT *
|
|
|
|
Revocation was successful. You must run 'gen-crl' and upload a new CRL to your
|
|
infrastructure in order to prevent the revoked certificate from being accepted."
|
|
|
|
return 0
|
|
} # => revoke_renewed()
|
|
|
|
# move-renewed-revoked
|
|
# moves renewed then revoked certificates to the 'revoked' folder
|
|
revoke_renewed_move() {
|
|
# make sure revoked dirs exist
|
|
for target in "$out_dir" \
|
|
"$out_dir/certs_by_serial" \
|
|
"$out_dir/private_by_serial" \
|
|
"$out_dir/reqs_by_serial"
|
|
do
|
|
[ -d "$target" ] && continue
|
|
mkdir -p "$target" ||
|
|
die "Failed to mkdir: $target"
|
|
done
|
|
|
|
# move crt, key and req file to renewed_then_revoked folders
|
|
mv "$crt_in" "$crt_out" || die "Failed to move: $crt_in"
|
|
|
|
# only move the key if we have it
|
|
if [ -e "$key_in" ]; then
|
|
mv "$key_in" "$key_out" || warn "Failed to move: $key_in"
|
|
fi
|
|
|
|
# only move the req if we have it
|
|
if [ -e "$req_in" ]; then
|
|
mv "$req_in" "$req_out" || warn "Failed to move: $req_in"
|
|
fi
|
|
|
|
return 0
|
|
} # => revoke_renewed_move()
|
|
|
|
# Move renewed certs_by_serial to the new renew layout
|
|
rewind_renew() {
|
|
# pull filename base: serial number
|
|
[ "$1" ] || die "\
|
|
Error: didn't find a serial number as the first argument.
|
|
Run easyrsa without commands for usage and command help."
|
|
|
|
verify_ca_init
|
|
|
|
# Assign file_name_base and dust off!
|
|
file_name_base="$1"
|
|
shift "$#" # No options supported
|
|
|
|
cert_serial="$file_name_base"
|
|
in_dir="$EASYRSA_PKI/renewed"
|
|
crt_in="$in_dir/certs_by_serial/$file_name_base.crt"
|
|
key_in="$in_dir/private_by_serial/$file_name_base.key"
|
|
req_in="$in_dir/reqs_by_serial/$file_name_base.req"
|
|
|
|
# referenced cert must exist:
|
|
[ -f "$crt_in" ] || die "\
|
|
Unable to rewind as no certificate was found. Certificate was expected
|
|
at: $crt_in"
|
|
|
|
# Verify certificate
|
|
verify_file x509 "$crt_in" || die "\
|
|
Unable to rewind as the input file is not a valid certificate. Unexpected
|
|
input in file: $crt_in"
|
|
|
|
# Verify request
|
|
if [ -e "$req_in" ]; then
|
|
verify_file req "$req_in" || die "\
|
|
Unable to verify request. The file is not a valid request.
|
|
Unexpected input in file: $req_in"
|
|
fi
|
|
|
|
# get the commonName of the certificate via DN
|
|
crt_cn="$(
|
|
easyrsa_openssl x509 -in "$crt_in" -noout -subject -nameopt \
|
|
utf8,multiline | grep '^[[:blank:]]*commonName[[:blank:]]*= '
|
|
)" || die "Failed to find commonName in certificate"
|
|
crt_cn="${crt_cn#*= }"
|
|
|
|
# Set out_dir
|
|
out_dir="$EASYRSA_PKI/renewed"
|
|
crt_out="$out_dir/issued/$crt_cn.crt"
|
|
key_out="$out_dir/private/$crt_cn.key"
|
|
req_out="$out_dir/reqs/$crt_cn.req"
|
|
|
|
# Create out_dir
|
|
for newdir in issued private reqs; do
|
|
mkdir -p "$out_dir/$newdir" || die "Failed to create: $out_dir/$newdir"
|
|
done
|
|
|
|
# NEVER over-write a renewed cert, revoke it first
|
|
deny_msg="\
|
|
Cannot rewind this certificate because a conflicting file exists.
|
|
*"
|
|
[ -e "$crt_out" ] && die "$deny_msg certificate: $crt_out"
|
|
[ -e "$key_out" ] && die "$deny_msg private key: $key_out"
|
|
[ -e "$req_out" ] && die "$deny_msg request : $req_out"
|
|
unset -v deny_msg
|
|
|
|
warn "\
|
|
This process is destructive!
|
|
|
|
These files will be moved to the NEW 'renewed' storage sub-directory:
|
|
* $crt_in
|
|
* $key_in
|
|
* $req_in"
|
|
|
|
confirm " Continue with rewind-renew: " "yes" "\
|
|
Please confirm you wish to rewind-renew the certificate
|
|
with the following subject:
|
|
|
|
$(display_dn x509 "$crt_in")
|
|
|
|
serial-number: $cert_serial
|
|
" # => confirm end
|
|
|
|
# move crt, key and req file to renewed folders
|
|
mv "$crt_in" "$crt_out" || die "Failed to move: $crt_in"
|
|
|
|
# only move the key if we have it
|
|
if [ -e "$key_in" ]; then
|
|
if mv "$key_in" "$key_out"; then
|
|
: # ok
|
|
else
|
|
# Attempt restore
|
|
mv -f "$crt_out" "$crt_in"
|
|
die "Failed to move: $key_in"
|
|
fi
|
|
fi
|
|
|
|
# only move the req if we have it
|
|
if [ -e "$req_in" ]; then
|
|
if mv "$req_in" "$req_out"; then
|
|
: # ok
|
|
else
|
|
# Attempt restore
|
|
mv -f "$crt_out" "$crt_in"
|
|
mv -f "$key_out" "$key_in"
|
|
die "Failed to move: $req_in"
|
|
fi
|
|
fi
|
|
|
|
# Success message
|
|
notice "\
|
|
Rewind is successful.
|
|
|
|
Common Name : $crt_cn
|
|
Serial number: $cert_serial
|
|
|
|
To revoke use: 'revoke-renewed $crt_cn'"
|
|
} # => rewind_renew()
|
|
|
|
# rebuild backend
|
|
rebuild() {
|
|
# pull filename base:
|
|
[ "$1" ] || die "\
|
|
Error: didn't find a file base name as the first argument.
|
|
Run easyrsa without commands for usage and command help."
|
|
|
|
verify_ca_init
|
|
|
|
# Assign file_name_base and dust off!
|
|
file_name_base="$1"
|
|
shift
|
|
|
|
in_dir="$EASYRSA_PKI"
|
|
crt_in="$in_dir/issued/$file_name_base.crt"
|
|
key_in="$in_dir/private/$file_name_base.key"
|
|
req_in="$in_dir/reqs/$file_name_base.req"
|
|
creds_in="$in_dir/$file_name_base.creds"
|
|
inline_in="$in_dir/inline/$file_name_base.inline"
|
|
|
|
# Upgrade CA index.txt.attr - unique_subject = no
|
|
up23_upgrade_ca || die "Failed to upgrade CA to support renewal."
|
|
|
|
# Set 'nopass'
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
nopass)
|
|
[ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1
|
|
;;
|
|
*) die "Unknown option: $1"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# referenced cert must exist:
|
|
[ -f "$crt_in" ] || die "\
|
|
Unable to rebuild as no certificate was found. Certificate was expected
|
|
at: $crt_in"
|
|
|
|
# Verify certificate
|
|
verify_file x509 "$crt_in" || die "\
|
|
Unable to rebuild as the input file is not a valid certificate. Unexpected
|
|
input in file: $crt_in"
|
|
|
|
# Verify request
|
|
if [ -e "$req_in" ]; then
|
|
verify_file req "$req_in" || die "\
|
|
Unable to verify request. The file is not a valid request.
|
|
Unexpected input in file: $req_in"
|
|
fi
|
|
|
|
# get the serial number of the certificate
|
|
ssl_cert_serial "$crt_in" cert_serial
|
|
|
|
duplicate_crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem"
|
|
|
|
# Set out_dir
|
|
out_dir="$EASYRSA_PKI/renewed"
|
|
crt_out="$out_dir/issued/$file_name_base.crt"
|
|
key_out="$out_dir/private/$file_name_base.key"
|
|
req_out="$out_dir/reqs/$file_name_base.req"
|
|
|
|
# NEVER over-write a renewed cert, revoke it first
|
|
deny_msg="\
|
|
Cannot rebuild this certificate because a conflicting file exists.
|
|
*"
|
|
[ -e "$crt_out" ] && die "$deny_msg certificate: $crt_out"
|
|
[ -e "$key_out" ] && die "$deny_msg private key: $key_out"
|
|
[ -e "$req_out" ] && die "$deny_msg request : $req_out"
|
|
unset -v deny_msg
|
|
|
|
# # Check if old cert is expired or expires within 30
|
|
# cert_dates "$crt_in"
|
|
#
|
|
# [ "$expire_date_s" -lt "$allow_renew_date_s" ] || die "\
|
|
#Certificate expires in more than $EASYRSA_CERT_RENEW days.
|
|
#Renewal not allowed."
|
|
|
|
# Extract certificate usage from old cert
|
|
cert_ext_key_usage="$(
|
|
easyrsa_openssl x509 -in "$crt_in" -noout -text |
|
|
sed -n "/X509v3 Extended Key Usage:/{n;s/^ *//g;p;}"
|
|
)"
|
|
|
|
case "$cert_ext_key_usage" in
|
|
"TLS Web Client Authentication")
|
|
cert_type=client
|
|
;;
|
|
"TLS Web Server Authentication")
|
|
cert_type=server
|
|
;;
|
|
"TLS Web Server Authentication, TLS Web Client Authentication")
|
|
cert_type=serverClient
|
|
;;
|
|
*) die "Unknown key usage: $cert_ext_key_usage"
|
|
esac
|
|
|
|
# Use SAN from --subject-alt-name if set else use SAN from old cert
|
|
if echo "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltName; then
|
|
: # ok - Use current subjectAltName
|
|
else
|
|
san="$(
|
|
easyrsa_openssl x509 -in "$crt_in" -noout -text | sed -n \
|
|
"/X509v3 Subject Alternative Name:/{n;s/IP Address:/IP:/g;s/ //g;p;}"
|
|
)"
|
|
|
|
[ "$san" ] && export EASYRSA_EXTRA_EXTS="\
|
|
$EASYRSA_EXTRA_EXTS
|
|
subjectAltName = $san"
|
|
fi
|
|
|
|
# confirm operation by displaying DN:
|
|
unset -v if_exist_key_in if_exist_req_in
|
|
[ -e "$key_in" ] && if_exist_key_in="
|
|
* $key_in"
|
|
[ -e "$req_in" ] && if_exist_req_in="
|
|
* $req_in"
|
|
warn "\
|
|
This process is destructive!
|
|
|
|
These files will be moved to the 'renewed' storage directory:
|
|
* $crt_in${if_exist_key_in}${if_exist_req_in}
|
|
|
|
These files will be DELETED:
|
|
All PKCS files for commonName : $file_name_base
|
|
|
|
The inline credentials files:
|
|
* $creds_in
|
|
* $inline_in
|
|
|
|
The duplicate certificate:
|
|
* $duplicate_crt_by_serial
|
|
|
|
IMPORTANT: The new key will${EASYRSA_NO_PASS+ NOT} be password protected."
|
|
|
|
confirm " Continue with rebuild: " "yes" "\
|
|
Please confirm you wish to renew the certificate
|
|
with the following subject:
|
|
|
|
$(display_dn x509 "$crt_in")
|
|
|
|
serial-number: $cert_serial"
|
|
|
|
# move renewed files so we can reissue certificate with the same name
|
|
rebuild_move
|
|
error_undo_rebuild_move=1
|
|
|
|
# rebuild certificate
|
|
if EASYRSA_BATCH=1 build_full "$cert_type" "$file_name_base"; then
|
|
unset -v error_undo_rebuild_move
|
|
else
|
|
# If rebuild failed then restore cert, key and req. Otherwise,
|
|
# issue a warning. If *restore* fails then at least the file-names
|
|
# are not serial-numbers
|
|
rebuild_restore_move
|
|
die "\
|
|
Rebuild has failed to build a new certificate/key pair."
|
|
fi
|
|
|
|
# Success messages
|
|
notice "Rebuild was successful.
|
|
|
|
* IMPORTANT *
|
|
|
|
Rebuild has created a new certificate and key, to replace both old files.
|
|
|
|
To revoke the old certificate, once the new one has been deployed,
|
|
use: 'revoke-renewed $file_name_base reason' ('reason' is optional)"
|
|
|
|
return 0
|
|
} # => rebuild()
|
|
|
|
# Restore files on failure to rebuild
|
|
rebuild_restore_move() {
|
|
unset -v rrm_err error_undo_renew_move
|
|
# restore crt, key and req file to PKI folders
|
|
if mv "$restore_crt_out" "$restore_crt_in"; then
|
|
: # ok
|
|
else
|
|
warn "Failed to restore: $restore_crt_out"
|
|
rrm_err=1
|
|
fi
|
|
|
|
# only restore the key if we have it
|
|
if [ -e "$restore_key_out" ]; then
|
|
if mv "$restore_key_out" "$restore_key_in"; then
|
|
: # ok
|
|
else
|
|
warn "Failed to restore: $restore_key_out"
|
|
rrm_err=1
|
|
fi
|
|
fi
|
|
|
|
# only restore the req if we have it
|
|
if [ -e "$restore_req_out" ]; then
|
|
if mv "$restore_req_out" "$restore_req_in"; then
|
|
: # ok
|
|
else
|
|
warn "Failed to restore: $restore_req_out"
|
|
rrm_err=1
|
|
fi
|
|
fi
|
|
|
|
# messages
|
|
if [ "$rrm_err" ]; then
|
|
warn "Failed to restore renewed files."
|
|
else
|
|
notice "Rebuild FAILED but files have been successfully restored."
|
|
fi
|
|
|
|
return 0
|
|
} # => rebuild_restore_move()
|
|
|
|
# rebuild_move
|
|
# moves renewed certificates to the 'renewed' folder
|
|
# allows reissuing certificates with the same name
|
|
rebuild_move() {
|
|
# make sure renewed dirs exist
|
|
for target in "$out_dir" \
|
|
"$out_dir/issued" \
|
|
"$out_dir/private" \
|
|
"$out_dir/reqs"
|
|
do
|
|
[ -d "$target" ] && continue
|
|
mkdir -p "$target" ||
|
|
die "Failed to mkdir: $target"
|
|
done
|
|
|
|
# move crt, key and req file to renewed folders
|
|
restore_crt_in="$crt_in"
|
|
restore_crt_out="$crt_out"
|
|
mv "$crt_in" "$crt_out" || die "Failed to move: $crt_in"
|
|
|
|
# only move the key if we have it
|
|
restore_key_in="$key_in"
|
|
restore_key_out="$key_out"
|
|
if [ -e "$key_in" ]; then
|
|
mv "$key_in" "$key_out" || warn "Failed to move: $key_in"
|
|
fi
|
|
|
|
# only move the req if we have it
|
|
restore_req_in="$req_in"
|
|
restore_req_out="$req_out"
|
|
if [ -e "$req_in" ]; then
|
|
mv "$req_in" "$req_out" || warn "Failed to move: $req_in"
|
|
fi
|
|
|
|
# remove any pkcs files
|
|
for pkcs in p12 p7b p8 p1; do
|
|
if [ -e "$in_dir/issued/$file_name_base.$pkcs" ]; then
|
|
# issued
|
|
rm "$in_dir/issued/$file_name_base.$pkcs" ||
|
|
warn "Failed to remove: $file_name_base.$pkcs"
|
|
|
|
elif [ -e "$in_dir/private/$file_name_base.$pkcs" ]; then
|
|
# private
|
|
rm "$in_dir/private/$file_name_base.$pkcs" ||
|
|
warn "Failed to remove: $file_name_base.$pkcs"
|
|
else
|
|
: # ok
|
|
fi
|
|
done
|
|
|
|
# remove the duplicate certificate
|
|
if [ -e "$duplicate_crt_by_serial" ]; then
|
|
rm "$duplicate_crt_by_serial" || warn "\
|
|
Failed to remove the duplicate certificate:
|
|
* $duplicate_crt_by_serial"
|
|
fi
|
|
|
|
# remove credentials file
|
|
if [ -e "$creds_in" ]; then
|
|
rm "$creds_in" || warn "\
|
|
Failed to remove credentials file:
|
|
* $creds_in"
|
|
fi
|
|
|
|
# remove inline file
|
|
if [ -e "$inline_in" ]; then
|
|
rm "$inline_in" || warn "\
|
|
Failed to remove inline file:
|
|
* $inline_in"
|
|
fi
|
|
|
|
return 0
|
|
} # => rebuild_move()
|
|
|
|
# gen-crl backend
|
|
gen_crl() {
|
|
verify_ca_init
|
|
|
|
out_file="$EASYRSA_PKI/crl.pem"
|
|
out_file_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"
|
|
|
|
easyrsa_openssl ca -utf8 -gencrl -out "$out_file_tmp" \
|
|
${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} || die "\
|
|
CRL Generation failed."
|
|
|
|
mv "$out_file_tmp" "$out_file"
|
|
|
|
notice "\
|
|
An updated CRL has been created.
|
|
CRL file: $out_file"
|
|
|
|
return 0
|
|
} # => gen_crl()
|
|
|
|
# import-req backend
|
|
import_req() {
|
|
# Verify PKI has been initialised
|
|
verify_pki_init
|
|
|
|
# pull passed paths
|
|
in_req="$1"
|
|
short_name="$2"
|
|
out_req="$EASYRSA_PKI/reqs/$2.req"
|
|
|
|
[ "$short_name" ] || die "\
|
|
Unable to import: incorrect command syntax.
|
|
Run easyrsa without commands for usage and command help."
|
|
|
|
verify_file req "$in_req" || die "\
|
|
The input file does not appear to be a certificate request. Aborting import.
|
|
File Path: $in_req"
|
|
|
|
# destination must not exist
|
|
[ -e "$out_req" ] && die "\
|
|
Unable to import the request as the destination file already exists.
|
|
Please choose a different name for your imported request file.
|
|
Existing file at: $out_req"
|
|
|
|
# now import it
|
|
cp "$in_req" "$out_req"
|
|
|
|
notice "\
|
|
The request has been successfully imported with a short name of: $short_name
|
|
You may now use this name to perform signing operations on this request."
|
|
|
|
return 0
|
|
} # => import_req()
|
|
|
|
# export pkcs#12, pkcs#7, pkcs#8 or pkcs#1
|
|
export_pkcs() {
|
|
pkcs_type="$1"
|
|
shift
|
|
|
|
[ "$1" ] || die "\
|
|
Unable to export p12: incorrect command syntax.
|
|
Run easyrsa without commands for usage and command help."
|
|
|
|
short_name="$1"
|
|
shift
|
|
|
|
crt_in="$EASYRSA_PKI/issued/$short_name.crt"
|
|
key_in="$EASYRSA_PKI/private/$short_name.key"
|
|
crt_ca="$EASYRSA_PKI/ca.crt"
|
|
|
|
# Verify PKI has been initialised
|
|
verify_pki_init
|
|
|
|
# opts support
|
|
cipher=-aes256
|
|
want_ca=1
|
|
want_key=1
|
|
unset -v pkcs_friendly_name
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
noca) want_ca="" ;;
|
|
nokey) want_key="" ;;
|
|
nopass)
|
|
[ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1
|
|
;;
|
|
usefn) pkcs_friendly_name="$short_name" ;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
pkcs_certfile_path=
|
|
if [ "$want_ca" ]; then
|
|
verify_file x509 "$crt_ca" || die "\
|
|
Unable to include CA cert in the $pkcs_type output (missing file, or use noca option.)
|
|
Missing file expected at: $crt_ca"
|
|
pkcs_certfile_path="$crt_ca"
|
|
fi
|
|
|
|
# input files must exist
|
|
verify_file x509 "$crt_in" || die "\
|
|
Unable to export $pkcs_type for short name '$short_name' without the certificate.
|
|
Missing cert expected at: $crt_in"
|
|
|
|
# For 'nopass' PKCS requires an explicit empty password 'pass:'
|
|
if [ "$EASYRSA_NO_PASS" ]; then
|
|
EASYRSA_PASSIN=pass:
|
|
EASYRSA_PASSOUT=pass:
|
|
unset -v cipher # pkcs#1 only
|
|
fi
|
|
|
|
case "$pkcs_type" in
|
|
p12)
|
|
pkcs_out="$EASYRSA_PKI/private/$short_name.p12"
|
|
|
|
if [ "$want_key" ]; then
|
|
[ -e "$key_in" ] || die "\
|
|
Unable to export p12 for short name '$short_name' without the key
|
|
(if you want a p12 without the private key, use nokey option.)
|
|
Missing key expected at: $key_in"
|
|
else
|
|
nokeys=1
|
|
fi
|
|
|
|
# export the p12:
|
|
easyrsa_openssl pkcs12 -in "$crt_in" -inkey "$key_in" -export \
|
|
-out "$pkcs_out" \
|
|
${nokeys:+ -nokeys} \
|
|
${pkcs_certfile_path:+ -certfile "$pkcs_certfile_path"} \
|
|
${pkcs_friendly_name:+ -name "$pkcs_friendly_name"} \
|
|
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
|
|
${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} \
|
|
|| die "Failed to export PKCS#12"
|
|
;;
|
|
p7)
|
|
pkcs_out="$EASYRSA_PKI/issued/$short_name.p7b"
|
|
|
|
# export the p7:
|
|
easyrsa_openssl crl2pkcs7 -nocrl -certfile "$crt_in" \
|
|
-out "$pkcs_out" \
|
|
${pkcs_certfile_path:+ -certfile "$pkcs_certfile_path"} \
|
|
|| die "Failed to export PKCS#7"
|
|
;;
|
|
p8)
|
|
pkcs_out="$EASYRSA_PKI/private/$short_name.p8"
|
|
|
|
# export the p8:
|
|
easyrsa_openssl pkcs8 -in "$key_in" -topk8 \
|
|
-out "$pkcs_out" \
|
|
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
|
|
${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} \
|
|
|| die "Failed to export PKCS#8"
|
|
;;
|
|
p1)
|
|
pkcs_out="$EASYRSA_PKI/private/$short_name.p1"
|
|
|
|
# export the p1:
|
|
easyrsa_openssl rsa -in "$key_in" \
|
|
-out "$pkcs_out" \
|
|
${cipher:+ "$cipher"} \
|
|
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
|
|
${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} \
|
|
|| die "Failed to export PKCS#1"
|
|
;;
|
|
*) die "Unknown PKCS type: $pkcs_type"
|
|
esac
|
|
|
|
notice "\
|
|
Successful export of $pkcs_type file. Your exported file is at the following
|
|
location: $pkcs_out"
|
|
|
|
return 0
|
|
} # => export_pkcs()
|
|
|
|
# set-pass backend legacy
|
|
set_pass_legacy() {
|
|
# Verify PKI has been initialised
|
|
verify_pki_init
|
|
|
|
# key type, supplied internally from frontend command call (rsa/ec)
|
|
key_type="$1"
|
|
shift
|
|
|
|
# values supplied by the user:
|
|
raw_file="$1"
|
|
shift
|
|
|
|
file="$EASYRSA_PKI/private/$raw_file.key"
|
|
|
|
[ "$raw_file" ] || die "\
|
|
Missing argument to 'set-$key_type-pass' command: no name/file supplied.
|
|
See help output for usage details."
|
|
|
|
# parse command options
|
|
cipher="-aes256"
|
|
unset -v nopass
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
nopass)
|
|
[ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1
|
|
;;
|
|
file) file="$raw_file" ;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# If nopass then do not encrypt else encrypt with password.
|
|
if [ "$EASYRSA_NO_PASS" ]; then
|
|
unset -v cipher
|
|
fi
|
|
|
|
[ -e "$file" ] || die "\
|
|
Missing private key: expected to find the private key component at:
|
|
$file"
|
|
|
|
notice "\
|
|
If the key is currently encrypted you must supply the decryption passphrase.
|
|
${cipher:+You will then enter a new PEM passphrase for this key.$NL}"
|
|
|
|
# Set password
|
|
out_key_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"
|
|
easyrsa_openssl "$key_type" -in "$file" -out "$out_key_tmp" \
|
|
${cipher:+ "$cipher"} \
|
|
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
|
|
${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} || die "\
|
|
Failed to change the private key passphrase. See above for possible openssl
|
|
error messages."
|
|
|
|
mv "$out_key_tmp" "$file" || die "\
|
|
Failed to change the private key passphrase. See above for error messages."
|
|
|
|
notice "Key passphrase successfully changed"
|
|
|
|
return 0
|
|
} # => set_pass_legacy()
|
|
|
|
# set-pass backend
|
|
set_pass() {
|
|
# Verify PKI has been initialised
|
|
verify_pki_init
|
|
|
|
# values supplied by the user:
|
|
raw_file="$1"
|
|
file="$EASYRSA_PKI/private/$raw_file.key"
|
|
|
|
if [ "$raw_file" ]; then
|
|
shift
|
|
else
|
|
die "\
|
|
Missing argument: no name/file supplied."
|
|
fi
|
|
|
|
# parse command options
|
|
cipher="-aes256"
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
nopass)
|
|
[ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1
|
|
;;
|
|
file) file="$raw_file" ;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# If nopass then do not encrypt else encrypt with password.
|
|
if [ "$EASYRSA_NO_PASS" ]; then
|
|
unset -v cipher
|
|
fi
|
|
|
|
[ -e "$file" ] || die "\
|
|
Missing private key: expected to find the private key component at:
|
|
$file"
|
|
|
|
warn "\
|
|
If the key is encrypted then you must supply the decryption pass phrase.
|
|
${cipher:+You will then enter and verify a new PEM pass phrase for this key.}"
|
|
|
|
# Set password
|
|
out_key_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"
|
|
|
|
easyrsa_openssl pkey -in "$file" -out "$out_key_tmp" \
|
|
${cipher:+ "$cipher"} \
|
|
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
|
|
${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} || die "\
|
|
Failed to change the private key passphrase."
|
|
|
|
mv "$out_key_tmp" "$file" || die "\
|
|
Failed to update the private key file."
|
|
|
|
key_update=changed
|
|
[ "$EASYRSA_NO_PASS" ] && key_update=removed
|
|
notice "Key passphrase successfully $key_update"
|
|
} # => set_pass()
|
|
|
|
# update-db backend
|
|
update_db() {
|
|
verify_ca_init
|
|
|
|
easyrsa_openssl ca -utf8 -updatedb \
|
|
${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} || die "\
|
|
Failed to perform update-db: see above for related openssl errors."
|
|
|
|
return 0
|
|
} # => update_db()
|
|
|
|
# Display subjectAltName
|
|
display_san() {
|
|
[ "$#" = 2 ] || die "display_dn - Incorrect input parameters"
|
|
format="$1"
|
|
path="$2"
|
|
shift 2
|
|
|
|
if echo "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltName; then
|
|
# Print user defined SAN
|
|
print "$(\
|
|
echo "$EASYRSA_EXTRA_EXTS" | grep subjectAltName | \
|
|
sed 's/^[[:space:]]*subjectAltName[[:space:]]*=[[:space:]]*//'
|
|
)"
|
|
|
|
else
|
|
# Generate a SAN
|
|
san="$(
|
|
x509v3san="X509v3 Subject Alternative Name:"
|
|
easyrsa_openssl "$format" -in "$path" -noout -text | sed -n \
|
|
"/${x509v3san}/{n;s/ //g;s/IPAddress:/IP:/g;s/RegisteredID/RID/;p;}"
|
|
)"
|
|
|
|
# Print auto SAN
|
|
[ "$san" ] && print "$san"
|
|
fi
|
|
} # => display_san()
|
|
|
|
# display cert DN info on a req/X509, passed by full pathname
|
|
display_dn() {
|
|
[ "$#" = 2 ] || die "display_dn - Incorrect input parameters"
|
|
format="$1"
|
|
path="$2"
|
|
shift 2
|
|
|
|
# Display DN
|
|
name_opts="utf8,sep_multiline,space_eq,lname,align"
|
|
print "$(
|
|
easyrsa_openssl "$format" -in "$path" -noout -subject \
|
|
-nameopt "$name_opts"
|
|
)"
|
|
|
|
# Display SAN, if present
|
|
san="$(display_san "$format" "$path")"
|
|
if [ "$san" ]; then
|
|
print ""
|
|
print "X509v3 Subject Alternative Name:"
|
|
print " $san"
|
|
fi
|
|
} # => display_dn()
|
|
|
|
# generate default SAN from req/X509, passed by full pathname
|
|
default_server_san() {
|
|
[ "$#" = 1 ] || die "display_dn - Incorrect input parameters"
|
|
path="$1"
|
|
shift
|
|
|
|
# Extract CN from DN
|
|
cn="$(
|
|
easyrsa_openssl req -in "$path" -noout -subject \
|
|
-nameopt sep_multiline |
|
|
awk -F'=' '/^ *CN=/{print $2}'
|
|
)"
|
|
|
|
# See: https://github.com/OpenVPN/easy-rsa/issues/576
|
|
# Select default SAN
|
|
if echo "$cn" | grep -q \
|
|
-E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
|
|
then
|
|
print "subjectAltName = IP:$cn"
|
|
else
|
|
print "subjectAltName = DNS:$cn"
|
|
fi
|
|
} # => default_server_san()
|
|
|
|
# Verify certificate against CA
|
|
verify_cert() {
|
|
# pull filename base:
|
|
[ "$1" ] || die "\
|
|
Error: didn't find a file base name as the first argument.
|
|
Run easyrsa without commands for usage and command help."
|
|
|
|
verify_ca_init
|
|
|
|
# Assign file_name_base and dust off!
|
|
file_name_base="$1"
|
|
shift
|
|
|
|
# Support global --batch mode
|
|
unset -v exit_with_error
|
|
if [ "$EASYRSA_BATCH" ]; then
|
|
exit_with_error=1
|
|
EASYRSA_SILENT=1
|
|
fi
|
|
|
|
# function opts support
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
# batch flag, return status [0/1] to calling program
|
|
# Otherwise, exit 0 on successful completion
|
|
batch)
|
|
exit_with_error=1
|
|
EASYRSA_SILENT=1
|
|
;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
in_dir="$EASYRSA_PKI"
|
|
ca_crt="$in_dir/ca.crt"
|
|
crt_in="$in_dir/issued/$file_name_base.crt"
|
|
|
|
# Cert file must exist
|
|
[ -e "$crt_in" ] || die "\
|
|
No certificate found for the input: '$crt_in'"
|
|
|
|
# Verify file is a valid cert
|
|
verify_file x509 "$crt_in" || die "\
|
|
Input is not a valid certificate: $crt_in"
|
|
|
|
# Test SSL out
|
|
if easyrsa_openssl verify -CAfile "$ca_crt" \
|
|
"$crt_in" 1>/dev/null
|
|
then
|
|
notice "\
|
|
Certificate name: $file_name_base
|
|
Verfication status: GOOD"
|
|
# easyrsa_error_exit=1 # Simple 'proof of concept' test
|
|
else
|
|
notice "\
|
|
Certificate name: $file_name_base
|
|
Verfication status: FAILED"
|
|
# Exit with error (batch mode), otherwise term msg only
|
|
if [ "$exit_with_error" ]; then
|
|
easyrsa_error_exit=1
|
|
# Return error for internal callers (status reports)
|
|
# or command line in --batch mode
|
|
return 1
|
|
fi
|
|
fi
|
|
} # => verify_cert()
|
|
|
|
# verify a file seems to be a valid req/X509
|
|
verify_file() {
|
|
format="$1"
|
|
path="$2"
|
|
easyrsa_openssl "$format" -in "$path" -noout 2>/dev/null
|
|
} # => verify_file()
|
|
|
|
# show-* command backend
|
|
# Prints req/cert details in a readable format
|
|
show() {
|
|
type="$1"
|
|
name="$2"
|
|
in_file=""
|
|
format=""
|
|
[ "$name" ] || die "\
|
|
Missing expected <file_name_base> argument.
|
|
Run easyrsa without commands for usage help."
|
|
shift 2
|
|
|
|
# opts support
|
|
type_opts="-${type}opt"
|
|
out_opts="no_pubkey,no_sigdump"
|
|
name_opts="utf8,sep_multiline,space_eq,lname,align"
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
full) out_opts= ;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# Determine cert/req type (v2)
|
|
case "$type" in
|
|
cert)
|
|
verify_ca_init
|
|
in_file="$EASYRSA_PKI/issued/$name.crt"
|
|
format="x509"
|
|
;;
|
|
req)
|
|
verify_pki_init
|
|
in_file="$EASYRSA_PKI/reqs/$name.req"
|
|
format="req"
|
|
;;
|
|
crl)
|
|
verify_ca_init
|
|
in_file="$EASYRSA_PKI/$name.pem"
|
|
format="crl"
|
|
unset -v type_opts out_opts name_opts
|
|
;;
|
|
*) die "Unrecognised type: $type"
|
|
esac
|
|
|
|
# Verify file exists and is of the correct type
|
|
[ -e "$in_file" ] || die "\
|
|
No such '$type' type file with a <file_name_base> of '$name' is present.
|
|
Expected to find this file at:
|
|
$in_file"
|
|
|
|
verify_file "$format" "$in_file" || die "\
|
|
This file is not a valid $type file:
|
|
$in_file"
|
|
|
|
notice "\
|
|
Showing $type details for: '$name'
|
|
|
|
This file is stored at:
|
|
* $in_file"
|
|
|
|
easyrsa_openssl "$format" -in "$in_file" -noout -text \
|
|
${type_opts+ "$type_opts" "$out_opts"} \
|
|
${name_opts+ -nameopt "$name_opts"} \
|
|
|| die "\
|
|
OpenSSL failure to process the input"
|
|
|
|
[ "$EASYRSA_SILENT" ] || print # Separate certificate above
|
|
} # => show()
|
|
|
|
# show-ca command backend
|
|
# Prints CA cert details in a readable format
|
|
show_ca() {
|
|
# opts support
|
|
out_opts="no_pubkey,no_sigdump"
|
|
name_opts="utf8,sep_multiline,space_eq,lname,align"
|
|
while [ "$1" ]; do
|
|
case "$1" in
|
|
full) out_opts= ;;
|
|
*) warn "Ignoring unknown command option: '$1'"
|
|
esac
|
|
shift
|
|
done
|
|
|
|
verify_ca_init
|
|
in_file="$EASYRSA_PKI/ca.crt"
|
|
format="x509"
|
|
|
|
# Verify file exists and is of the correct type
|
|
[ -e "$in_file" ] || die "\
|
|
No such $type file with a basename of '$name' is present.
|
|
Expected to find this file at:
|
|
$in_file"
|
|
|
|
verify_file "$format" "$in_file" || die "\
|
|
This file is not a valid $type file:
|
|
$in_file"
|
|
|
|
notice "
|
|
Showing $type details for 'ca'.
|
|
|
|
This file is stored at:
|
|
* $in_file"
|
|
|
|
easyrsa_openssl "$format" -in "$in_file" -noout -text \
|
|
-nameopt "$name_opts" -certopt "$out_opts" || die "\
|
|
OpenSSL failure to process the input"
|
|
|
|
[ "$EASYRSA_SILENT" ] || print # Separate certificate above
|
|
} # => show_ca()
|
|
|
|
# Set fixed offset dates
|
|
fixed_cert_dates() {
|
|
# check input
|
|
[ "$#" -eq 3 ] || die "fixed_cert_dates - input error"
|
|
|
|
# Set the start fixed day-number of the Year
|
|
start_fix_day_n="$1"
|
|
|
|
# Check offset range
|
|
if [ "$start_fix_day_n" -lt 1 ] || \
|
|
[ "$start_fix_day_n" -gt 365 ]
|
|
then
|
|
die "\
|
|
Fixed off-set range [1-365 days]: $start_fix_day_n"
|
|
fi
|
|
|
|
# Final offset is off-by-one, adjust now
|
|
start_fix_day_n="$(( start_fix_day_n - 1 ))"
|
|
|
|
# Set the end fixed day-number of the Year
|
|
end_fix_day_n="$((
|
|
start_fix_day_n + EASYRSA_CERT_EXPIRE
|
|
))"
|
|
|
|
# OS dependencies
|
|
|
|
# busybox
|
|
if busybox date --help > /dev/null 2>&1; then
|
|
|
|
this_year_n="$(busybox date -u +%y)"
|
|
#today_n="$(busybox date -u +%j)"
|
|
|
|
New_Year_day_s="$(
|
|
busybox date -u -d \
|
|
"${this_year_n}01010000.01" '+%s'
|
|
)" || die "\
|
|
fixed_cert_dates - New_Year_day_s - busybox"
|
|
|
|
start_fix_day_s="$((
|
|
New_Year_day_s + start_fix_day_n * 86400
|
|
))"
|
|
|
|
end_fix_day_s="$((
|
|
start_fix_day_s + EASYRSA_CERT_EXPIRE * 86400
|
|
))"
|
|
|
|
# Convert to date-stamps for SSL input
|
|
start_fix_day_d="$(
|
|
busybox date -u -d @"${start_fix_day_s}" \
|
|
+%Y%m%d%H%M%SZ
|
|
)" || die "\
|
|
fixed_cert_dates - start_fix_day_d - busybox"
|
|
|
|
end_fix_day_d="$(
|
|
busybox date -u -d @"${end_fix_day_s}" \
|
|
+%Y%m%d%H%M%SZ
|
|
)" || die "\
|
|
fixed_cert_dates - end_fix_day_d - busybox"
|
|
|
|
# Darwin, BSD
|
|
elif date -j > /dev/null 2>&1; then
|
|
|
|
this_year_n="$(date -j +%y)"
|
|
#today_n="$(date -u -j +%j)"
|
|
|
|
New_Year_day_d="$(
|
|
date -u -j -f %y%m%d%H%M%S \
|
|
"${this_year_n}0101000001" \
|
|
+%Y%m%d%H%M.%SZ
|
|
)" || die "\
|
|
fixed_cert_dates - New_Year_day_d - Darwin"
|
|
|
|
# Convert to date-stamps for SSL input
|
|
start_fix_day_d="$(
|
|
date -u -j -f %Y%m%d%H%M.%SZ -v \
|
|
"+${start_fix_day_n}d" \
|
|
"$New_Year_day_d" +%Y%m%d%H%M%SZ
|
|
)" || die "\
|
|
fixed_cert_dates - start_fix_day_d - Darwin"
|
|
|
|
end_fix_day_d="$(
|
|
date -u -j -f %Y%m%d%H%M.%SZ -v \
|
|
"+${end_fix_day_n}d" \
|
|
"$New_Year_day_d" +%Y%m%d%H%M%SZ
|
|
)" || die "\
|
|
fixed_cert_dates - end_fix_day_d - Darwin"
|
|
|
|
end_fix_day_s="$(
|
|
date -u -j -f %Y%m%d%H%M.%SZ -v \
|
|
"+${end_fix_day_n}d" \
|
|
"$New_Year_day_d" +%s
|
|
)" || die "\
|
|
fixed_cert_dates - end_fix_day_s - Darwin"
|
|
|
|
# Linux and Windows
|
|
# date.exe does not allow +%s as input
|
|
# MacPorts GNU date
|
|
elif this_year_n="$(date -u +%y)"; then
|
|
|
|
# Day of Year number today
|
|
#today_n="$(date -u +%j)"
|
|
|
|
# New Years day date
|
|
New_Year_day_d="$(
|
|
date -u -d \
|
|
"${this_year_n}-01-01 00:00:01Z" \
|
|
'+%Y-%m-%d %H:%M:%SZ'
|
|
)" || die "\
|
|
fixed_cert_dates - New_Year_day_d - Linux"
|
|
|
|
# Convert to date-stamps for SSL input
|
|
start_fix_day_d="$(
|
|
date -u -d "$New_Year_day_d \
|
|
+${start_fix_day_n}days" \
|
|
+%Y%m%d%H%M%SZ
|
|
)" || die "\
|
|
fixed_cert_dates - start_fix_day_d - Linux"
|
|
|
|
end_fix_day_d="$(
|
|
date -u -d "$New_Year_day_d \
|
|
+${end_fix_day_n}days" \
|
|
+%Y%m%d%H%M%SZ
|
|
)" || die "\
|
|
fixed_cert_dates - end_fix_day_d - Linux"
|
|
|
|
end_fix_day_s="$(
|
|
date -u -d "$New_Year_day_d \
|
|
+${end_fix_day_n}days" +%s
|
|
)" || die "\
|
|
fixed_cert_dates - end_fix_day_s - Linux"
|
|
|
|
else
|
|
die "\
|
|
Unsupported 'date' program, upgrade your Matrix."
|
|
fi
|
|
|
|
# Return FINAL dates for use in the certificate
|
|
force_set_var "$2" "$start_fix_day_d" || die "\
|
|
fixed_cert_dates - force_set_var - $2 - $start_fix_day_d"
|
|
|
|
force_set_var "$3" "$end_fix_day_d" || die "\
|
|
fixed_cert_dates - force_set_var - $3 - $end_fix_day_d"
|
|
|
|
# cleanup
|
|
unset -v start_fix_day_n start_fix_day_d \
|
|
end_fix_day_d end_fix_day_s \
|
|
this_year_n New_Year_day_d
|
|
|
|
} # => fixed_cert_dates()
|
|
|
|
# Convert certificate date to timestamp seconds since epoch
|
|
cert_date_to_timestamp_s() {
|
|
# check input
|
|
[ "$#" -eq 2 ] || die "\
|
|
cert_date_to_timestamp_s - input error"
|
|
|
|
in_date="$1"
|
|
|
|
# busybox
|
|
if busybox date --help > /dev/null 2>&1
|
|
then
|
|
timestamp_s="$(
|
|
busybox date -D "%b %e %H:%M:%S %Y" \
|
|
-d "$in_date" +%s 2>/dev/null
|
|
)" || die "\
|
|
cert_date_to_timestamp_s - out_date_s - busybox"
|
|
|
|
# Darwin, BSD
|
|
elif timestamp_s="$(
|
|
date -j -f '%b %d %T %Y %Z' \
|
|
"$in_date" +%s 2>/dev/null
|
|
)"
|
|
then : # ok
|
|
|
|
# OS dependencies
|
|
# Linux and Windows
|
|
# date.exe does not allow +%s as input
|
|
# MacPorts GNU date
|
|
elif timestamp_s="$(
|
|
date -d "$in_date" +%s \
|
|
2>/dev/null
|
|
)"
|
|
then : # ok
|
|
|
|
# Something else
|
|
else
|
|
die "\
|
|
cert_date_to_timestamp_s:
|
|
'date' failed for 'in_date': $in_date"
|
|
fi
|
|
|
|
# Return timestamp_s
|
|
force_set_var "$2" "$timestamp_s" || die "\
|
|
cert_date_to_timestamp_s - force_set_var - $2 - $timestamp_s"
|
|
|
|
unset -v in_date timestamp_s
|
|
} # => cert_date_to_timestamp_s()
|
|
|
|
# Convert system date plus offset days
|
|
# to X509 certificate style date (+)offset
|
|
offset_days_to_cert_date() {
|
|
# check input
|
|
[ "$#" -eq 2 ] || die "\
|
|
offset_days_to_cert_date - input error"
|
|
|
|
in_offset="$1"
|
|
|
|
# busybox (Alpine)
|
|
if busybox date --help > /dev/null 2>&1
|
|
then
|
|
offset_date="$(
|
|
busybox date -u -d \
|
|
"@$(( $(busybox date +%s) \
|
|
+ in_offset * 86400 ))" \
|
|
"+%b %d %H:%M:%S %Y %Z" \
|
|
2>/dev/null
|
|
)" || die "\
|
|
offset_days_to_cert_date - offset_date - busybox"
|
|
|
|
# Darwin, BSD
|
|
elif offset_date="$(
|
|
date -u -j -v "+${in_offset}d" \
|
|
"+%b %d %H:%M:%S %Y %Z" \
|
|
2>/dev/null
|
|
)"
|
|
then : # ok
|
|
|
|
# OS dependencies
|
|
# Linux and Windows
|
|
# date.exe does not allow +%s as input
|
|
# MacPorts GNU date
|
|
elif offset_date="$(
|
|
date -u -d "+${in_offset}days" \
|
|
"+%b %d %H:%M:%S %Y %Z" \
|
|
2>/dev/null
|
|
)"
|
|
then : # ok
|
|
|
|
# Something else
|
|
else
|
|
die "\
|
|
offset_days_to_cert_date:
|
|
'date' failed for 'in_offset': $in_offset"
|
|
fi
|
|
|
|
# Return offset_date
|
|
force_set_var "$2" "$offset_date" || die "\
|
|
offset_days_to_cert_date \
|
|
- force_set_var - $2 - $offset_date"
|
|
|
|
unset -v in_offset offset_date
|
|
} # => offset_days_to_cert_date()
|
|
|
|
# Convert fixed format date to X509 certificate style date
|
|
ff_date_to_cert_date() {
|
|
# check input
|
|
[ "$#" -eq 2 ] || die "\
|
|
ff_date_to_cert_date - input error"
|
|
|
|
in_date="$1"
|
|
|
|
# busybox
|
|
if busybox date --help > /dev/null 2>&1
|
|
then
|
|
out_date="$(
|
|
busybox date -u \
|
|
-D "%y-%m-%d %H:%M:%S%Z" \
|
|
-d "$in_date" "+%b %d %H:%M:%S %Y %Z" \
|
|
2>/dev/null
|
|
)" || die "\
|
|
ff_date_to_cert_date - out_date - busybox"
|
|
|
|
# Darwin, BSD
|
|
elif out_date="$(
|
|
date -u -j -f '%y-%m-%d %TZ' \
|
|
"$in_date" "+%b %d %H:%M:%S %Y %Z" \
|
|
2>/dev/null
|
|
)"
|
|
then : # ok
|
|
|
|
# OS dependencies
|
|
# Linux and Windows
|
|
# * date.exe does not support format +%s as input
|
|
# MacPorts GNU date
|
|
elif out_date="$(
|
|
date -u -d "$in_date" \
|
|
"+%b %d %H:%M:%S %Y %Z" \
|
|
2>/dev/null
|
|
)"
|
|
then : # ok
|
|
|
|
# Something else
|
|
else
|
|
die "\
|
|
ff_date_to_cert_date:
|
|
'date' failed for 'in_date': $in_date"
|
|
fi
|
|
|
|
# Return out_date
|
|
force_set_var "$2" "$out_date" || die "\
|
|
ff_date_to_cert_date \
|
|
- force_set_var - $2 - $out_date"
|
|
|
|
unset -v in_date out_date
|
|
} # => ff_date_to_cert_date()
|
|
|
|
# Fixed format date
|
|
# Build a Windows date.exe compatible input field
|
|
db_date_to_ff_date() {
|
|
# check input
|
|
[ "$#" -eq 2 ] || die "\
|
|
db_date_to_ff_date - input error"
|
|
|
|
in_date="$1"
|
|
|
|
yy="${in_date%???????????}"
|
|
in_date="${in_date#"$yy"}"
|
|
mm="${in_date%?????????}"
|
|
in_date="${in_date#"$mm"}"
|
|
dd="${in_date%???????}"
|
|
in_date="${in_date#"$dd"}"
|
|
HH="${in_date%?????}"
|
|
in_date="${in_date#"$HH"}"
|
|
MM="${in_date%???}"
|
|
in_date="${in_date#"$MM"}"
|
|
SS="${in_date%?}"
|
|
in_date="${in_date#"$SS"}"
|
|
TZ="$in_date"
|
|
out_date="${yy}-${mm}-${dd} ${HH}:${MM}:${SS}${TZ}"
|
|
|
|
# Return out_date
|
|
force_set_var "$2" "$out_date" || die "\
|
|
db_date_to_ff_date \
|
|
- force_set_var - $2 - $out_date"
|
|
|
|
unset -v in_date out_date yy mm dd HH MM SS TZ
|
|
} # => db_date_to_ff_date()
|
|
|
|
# sanatize and set var
|
|
force_set_var() {
|
|
[ "$#" -eq 2 ] || die "force_set_var - input"
|
|
unset -v "$1" || die "force_set_var - unset"
|
|
set_var "$1" "$2" || die "force_set_var - set_var"
|
|
} # => force_set_var()
|
|
|
|
# get the serial number of the certificate -> serial=XXXX
|
|
ssl_cert_serial() {
|
|
[ "$#" = 2 ] || die "ssl_cert_serial - invalid input"
|
|
[ -f "$1" ] || die "ssl_cert_serial - missing cert"
|
|
|
|
fn_ssl_out="$(
|
|
unset -v EASYRSA_DEBUG
|
|
easyrsa_openssl x509 -in "$1" -noout -serial
|
|
)" || die "ssl_cert_serial - failed: -serial"
|
|
# remove the serial= part -> we only need the XXXX part
|
|
fn_ssl_out="${fn_ssl_out##*=}"
|
|
|
|
force_set_var "$2" "$fn_ssl_out" || \
|
|
die "ssl_cert_serial - failed to set var '$*'"
|
|
|
|
unset -v fn_ssl_out
|
|
} # => ssl_cert_serial()
|
|
|
|
# Get certificate start date
|
|
ssl_cert_not_before_date() {
|
|
[ "$#" = 2 ] || die "\
|
|
ssl_cert_not_before_date - invalid input"
|
|
[ -f "$1" ] || die "\
|
|
ssl_cert_not_before_date - missing cert"
|
|
|
|
fn_ssl_out="$(
|
|
unset -v EASYRSA_DEBUG
|
|
easyrsa_openssl x509 -in "$1" -noout -startdate
|
|
)" || die "\
|
|
ssl_cert_not_before_date - failed: -startdate"
|
|
|
|
fn_ssl_out="${fn_ssl_out#*=}"
|
|
|
|
force_set_var "$2" "$fn_ssl_out" || die "\
|
|
ssl_cert_not_before_date - failed to set var '$*'"
|
|
|
|
unset -v fn_ssl_out
|
|
} # => ssl_cert_not_before_date()
|
|
|
|
# Get certificate end date
|
|
ssl_cert_not_after_date() {
|
|
[ "$#" = 2 ] || die "\
|
|
ssl_cert_not_after_date - invalid input"
|
|
[ -f "$1" ] || die "\
|
|
ssl_cert_not_after_date - missing cert"
|
|
|
|
fn_ssl_out="$(
|
|
unset -v EASYRSA_DEBUG
|
|
easyrsa_openssl x509 -in "$1" -noout -enddate
|
|
)" || die "\
|
|
ssl_cert_not_after_date - failed: -enddate"
|
|
|
|
fn_ssl_out="${fn_ssl_out#*=}"
|
|
|
|
force_set_var "$2" "$fn_ssl_out" || die "\
|
|
ssl_cert_not_after_date - failed to set var '$*'"
|
|
|
|
unset -v fn_ssl_out
|
|
} # => ssl_cert_not_after_date()
|
|
|
|
# SC2295: Expansion inside ${..} need to be quoted separately,
|
|
# otherwise they match as patterns. (what-ever that means ;-)
|
|
# Unfortunately, Windows sh.exe has an weird bug.
|
|
# Try in sh.exe: t=' '; s="a${t}b${t}c"; echo "${s%%"${t}"*}"
|
|
|
|
# Read db
|
|
# shellcheck disable=SC2295
|
|
read_db() {
|
|
TCT=' ' # tab character
|
|
db_in="$EASYRSA_PKI/index.txt"
|
|
pki_r_issued="$EASYRSA_PKI/renewed/issued"
|
|
pki_r_by_sno="$EASYRSA_PKI/renewed/certs_by_serial"
|
|
while read -r db_status db_notAfter db_record; do
|
|
|
|
# Interpret the db/certificate record
|
|
unset -v db_serial db_cn db_revoke_date db_reason
|
|
case "$db_status" in
|
|
V|E)
|
|
# Valid
|
|
db_serial="${db_record%%${TCT}*}"
|
|
db_record="${db_record#*${TCT}}"
|
|
db_cn="${db_record#*/CN=}"; db_cn="${db_cn%%/*}"
|
|
cert_issued="$EASYRSA_PKI/issued/$db_cn.crt"
|
|
cert_r_issued="$pki_r_issued/$db_cn.crt"
|
|
cert_r_by_sno="$pki_r_by_sno/$db_serial.crt"
|
|
;;
|
|
R)
|
|
# Revoked
|
|
db_revoke_date="${db_record%%${TCT}*}"
|
|
db_reason="${db_revoke_date#*,}"
|
|
if [ "$db_reason" = "$db_revoke_date" ]; then
|
|
db_reason="None given"
|
|
else
|
|
db_revoke_date="${db_revoke_date%,*}"
|
|
fi
|
|
db_record="${db_record#*${TCT}}"
|
|
|
|
db_serial="${db_record%%${TCT}*}"
|
|
db_record="${db_record#*${TCT}}"
|
|
db_cn="${db_record#*/CN=}"; db_cn="${db_cn%%/*}"
|
|
;;
|
|
*) die "Unexpected status: $db_status"
|
|
esac
|
|
|
|
# Output selected status report for this record
|
|
case "$report" in
|
|
expire)
|
|
# Certs which expire before EASYRSA_CERT_RENEW days
|
|
case "$db_status" in
|
|
V|E)
|
|
case "$target" in
|
|
'') expire_status ;;
|
|
*) [ "$target" = "$db_cn" ] && expire_status
|
|
esac
|
|
;;
|
|
*)
|
|
: # Ignore ok
|
|
esac
|
|
;;
|
|
revoke)
|
|
# Certs which have been revoked
|
|
if [ "$db_status" = R ]; then
|
|
case "$target" in
|
|
'') revoke_status ;;
|
|
*) [ "$target" = "$db_cn" ] && revoke_status
|
|
esac
|
|
fi
|
|
;;
|
|
renew)
|
|
# Certs which have been renewed but not revoked
|
|
if [ "$db_status" = V ]; then
|
|
case "$target" in
|
|
'') renew_status ;;
|
|
*) [ "$target" = "$db_cn" ] && renew_status
|
|
esac
|
|
fi
|
|
;;
|
|
*) die "Unrecognised report: $report"
|
|
esac
|
|
done < "$db_in"
|
|
} # => read_db()
|
|
|
|
# Expire status
|
|
expire_status() {
|
|
if [ -e "$cert_issued" ]; then
|
|
|
|
# get the serial number of the certificate
|
|
ssl_cert_serial "$cert_issued" cert_serial
|
|
|
|
# db serial must match certificate serial, otherwise
|
|
# this is a renewed cert which has been replaced by
|
|
# an issued cert
|
|
if [ "$db_serial" != "$cert_serial" ]; then
|
|
information "\
|
|
serial mismatch:
|
|
db_serial: $db_serial
|
|
cert_serial: $cert_serial
|
|
commonName: $db_cn
|
|
cert_issued: $cert_issued"
|
|
return 0
|
|
fi
|
|
|
|
#cert_source=issued
|
|
ssl_cert_not_after_date \
|
|
"$cert_issued" cert_not_after_date
|
|
|
|
else
|
|
# Translate db date to usable date
|
|
#cert_source=database
|
|
ff_date=
|
|
db_date_to_ff_date "$db_notAfter" ff_date
|
|
cert_type_date=
|
|
ff_date_to_cert_date "$ff_date" cert_type_date
|
|
# Use db translated date
|
|
cert_not_after_date="$cert_type_date"
|
|
fi
|
|
|
|
# Get timestamp seconds for certificate expiry date
|
|
cert_expire_date_s=
|
|
cert_date_to_timestamp_s \
|
|
"$cert_not_after_date" cert_expire_date_s
|
|
|
|
# Set the cutoff date for expiry comparison
|
|
cert_type_date=
|
|
offset_days_to_cert_date \
|
|
"$EASYRSA_CERT_RENEW" cert_type_date
|
|
cutoff_date_s=
|
|
cert_date_to_timestamp_s \
|
|
"$cert_type_date" cutoff_date_s
|
|
|
|
# Set NOW date for expiry comparison
|
|
cert_type_date=
|
|
offset_days_to_cert_date \
|
|
0 cert_type_date
|
|
now_date_s=
|
|
cert_date_to_timestamp_s \
|
|
"$cert_type_date" now_date_s
|
|
|
|
if [ "$cert_expire_date_s" -lt "$cutoff_date_s" ]; then
|
|
# Cert expires in less than grace period
|
|
if [ "$cert_expire_date_s" -gt "$now_date_s" ]; then
|
|
printf '%s%s\n' \
|
|
"$db_status | Serial: $db_serial | " \
|
|
"Expires: $cert_not_after_date | CN: $db_cn"
|
|
else
|
|
printf '%s%s\n' \
|
|
"$db_status | Serial: $db_serial | " \
|
|
"Expired: $cert_not_after_date | CN: $db_cn"
|
|
fi
|
|
fi
|
|
} # => expire_status()
|
|
|
|
# Revoke status
|
|
revoke_status() {
|
|
# Translate db date to usable date
|
|
#source_date=database
|
|
ff_date=
|
|
db_date_to_ff_date "$db_revoke_date" ff_date
|
|
cert_type_date=
|
|
ff_date_to_cert_date "$ff_date" cert_type_date
|
|
# Use db translated date
|
|
cert_revoke_date="$cert_type_date"
|
|
|
|
printf '%s%s%s\n' \
|
|
"$db_status | Serial: $db_serial | " \
|
|
"Revoked: $cert_revoke_date | " \
|
|
"Reason: $db_reason | CN: $db_cn"
|
|
|
|
} # => revoke_status()
|
|
|
|
# Renewed status
|
|
# renewed certs only remain in the renewed folder until revoked
|
|
# Only ONE renewed cert with unique CN can exist in renewed folder
|
|
renew_status() {
|
|
# Does a Renewed cert exist ?
|
|
# files in issued are file name, or in serial are SerialNumber
|
|
unset -v cert_file_in cert_is_issued cert_is_serial renew_is_old
|
|
|
|
# Find renewed/issued/CN
|
|
if [ -e "$cert_r_issued" ]; then
|
|
cert_file_in="$cert_r_issued"
|
|
cert_is_issued=1
|
|
fi
|
|
|
|
# Find renewed/cert_by_serial/SN
|
|
if [ -e "$cert_r_by_sno" ]; then
|
|
cert_file_in="$cert_r_by_sno"
|
|
cert_is_serial=1
|
|
renew_is_old=1
|
|
fi
|
|
|
|
# Both should not exist
|
|
if [ "$cert_is_issued" ] && [ "$cert_is_serial" ]; then
|
|
die "Too many certs"
|
|
fi
|
|
|
|
# If a renewed cert exists
|
|
if [ "$cert_file_in" ]; then
|
|
# get the serial number of the certificate
|
|
ssl_cert_serial "$cert_file_in" cert_serial
|
|
|
|
# db serial must match certificate serial, otherwise
|
|
# this is an issued cert that replaces a renewed cert
|
|
if [ "$db_serial" != "$cert_serial" ]; then
|
|
information "\
|
|
serial mismatch:
|
|
db_serial: $db_serial
|
|
cert_serial: $cert_serial
|
|
cert_file_in: $cert_file_in"
|
|
return 0
|
|
fi
|
|
|
|
# Use cert date
|
|
# Assigns cert_not_after_date
|
|
ssl_cert_not_after_date "$cert_file_in" cert_not_after_date
|
|
|
|
# Highlight renewed/cert_by_serial
|
|
if [ "$renew_is_old" ]; then
|
|
printf '%s%s\n' \
|
|
"*** $db_status | Serial: $db_serial | " \
|
|
"Expires: $cert_not_after_date | CN: $db_cn"
|
|
else
|
|
printf '%s%s\n' \
|
|
"$db_status | Serial: $db_serial | " \
|
|
"Expires: $cert_not_after_date | CN: $db_cn"
|
|
fi
|
|
|
|
else
|
|
# Cert is valid but not renewed
|
|
: # ok - ignore
|
|
fi
|
|
} # => renew_status()
|
|
|
|
# cert status reports
|
|
status() {
|
|
|
|
[ "$#" -gt 0 ] || die "status - Incorrect input parameters"
|
|
report="$1"
|
|
target="$2"
|
|
|
|
verify_ca_init
|
|
|
|
# This does not build certs, so do not need fixed dates
|
|
unset -v EASYRSA_FIX_OFFSET EASYRSA_BATCH EASYRSA_SILENT
|
|
|
|
# test fix: https://github.com/OpenVPN/easy-rsa/issues/819
|
|
export LC_TIME=C.UTF-8
|
|
|
|
# If no target file then add Notice
|
|
if [ -z "$target" ]; then
|
|
# Select correct Notice
|
|
case "$report" in
|
|
expire)
|
|
notice "\
|
|
* Showing certificates which expire in less than \
|
|
$EASYRSA_CERT_RENEW days (--days):"
|
|
;;
|
|
revoke)
|
|
notice "\
|
|
* Showing certificates which are revoked:"
|
|
;;
|
|
renew)
|
|
notice "\
|
|
* Showing certificates which have been renewed but NOT revoked:
|
|
|
|
*** Marks those which require 'rewind-renew' \
|
|
before they can be revoked."
|
|
;;
|
|
*) warn "Unrecognised report: $report"
|
|
esac
|
|
fi
|
|
|
|
# Create report
|
|
read_db
|
|
|
|
} # => status()
|
|
|
|
# set_var is not known by shellcheck, therefore:
|
|
# Fake declare known variables for shellcheck
|
|
# Use these options without this function:
|
|
# -o all -e 2250,2244,2248 easyrsa
|
|
satisfy_shellcheck() {
|
|
die "Security feature enabled!"
|
|
# Add more as/if required
|
|
|
|
# Enable the heredoc for a peek
|
|
#cat << SC2154
|
|
EASYRSA=
|
|
EASYRSA_OPENSSL=
|
|
EASYRSA_PKI=
|
|
EASYRSA_DN=
|
|
EASYRSA_REQ_COUNTRY=
|
|
EASYRSA_REQ_PROVINCE=
|
|
EASYRSA_REQ_CITY=
|
|
EASYRSA_REQ_ORG=
|
|
EASYRSA_REQ_EMAIL=
|
|
EASYRSA_REQ_OU=
|
|
EASYRSA_ALGO=
|
|
EASYRSA_KEY_SIZE=
|
|
EASYRSA_CURVE=
|
|
EASYRSA_EC_DIR=
|
|
EASYRSA_CA_EXPIRE=
|
|
EASYRSA_CERT_EXPIRE=
|
|
EASYRSA_CERT_RENEW=
|
|
EASYRSA_CRL_DAYS=
|
|
EASYRSA_NS_SUPPORT=
|
|
EASYRSA_NS_COMMENT=
|
|
EASYRSA_TEMP_DIR=
|
|
EASYRSA_REQ_CN=
|
|
EASYRSA_DIGEST=
|
|
|
|
EASYRSA_SSL_CONF=
|
|
EASYRSA_SAFE_CONF=
|
|
OPENSSL_CONF=
|
|
|
|
#EASYRSA_KDC_REALM=
|
|
|
|
EASYRSA_RAND_SN=
|
|
KSH_VERSION=
|
|
#SC2154
|
|
|
|
} # => satisfy_shellcheck()
|
|
|
|
# Identify host OS
|
|
detect_host() {
|
|
unset -v easyrsa_ver_test easyrsa_host_os easyrsa_host_test \
|
|
easyrsa_win_git_bash
|
|
|
|
# Detect Windows
|
|
[ "${OS}" ] && easyrsa_host_test="${OS}"
|
|
|
|
# shellcheck disable=SC2016 # expansion inside '' blah
|
|
easyrsa_ksh=\
|
|
'@(#)MIRBSD KSH R39-w32-beta14 $Date: 2013/06/28 21:28:57 $'
|
|
|
|
[ "${KSH_VERSION}" = "${easyrsa_ksh}" ] && \
|
|
easyrsa_host_test="${easyrsa_ksh}"
|
|
unset -v easyrsa_ksh
|
|
|
|
# If not Windows then nix
|
|
if [ "${easyrsa_host_test}" ]; then
|
|
easyrsa_host_os=win
|
|
easyrsa_uname="${easyrsa_host_test}"
|
|
easyrsa_shell="$SHELL"
|
|
# Detect Windows git/bash
|
|
if [ "${EXEPATH}" ]; then
|
|
easyrsa_shell="$SHELL (Git)"
|
|
easyrsa_win_git_bash="${EXEPATH}"
|
|
# If found then set openssl NOW!
|
|
#[ -e /usr/bin/openssl ] && \
|
|
# set_var EASYRSA_OPENSSL /usr/bin/openssl
|
|
fi
|
|
else
|
|
easyrsa_host_os=nix
|
|
easyrsa_uname="$(uname 2>/dev/null)"
|
|
easyrsa_shell="${SHELL:-undefined}"
|
|
fi
|
|
|
|
easyrsa_ver_test="${EASYRSA_version%%~*}"
|
|
if [ "$easyrsa_ver_test" ]; then
|
|
host_out="Host: $EASYRSA_version"
|
|
else
|
|
host_out="Host: dev"
|
|
fi
|
|
|
|
host_out="\
|
|
${host_out} | $easyrsa_host_os | $easyrsa_uname | $easyrsa_shell"
|
|
host_out="\
|
|
${host_out}${easyrsa_win_git_bash+ | "$easyrsa_win_git_bash"}"
|
|
unset -v easyrsa_ver_test easyrsa_host_test
|
|
} # => detect_host()
|
|
|
|
# Extra diagnostics
|
|
show_host() {
|
|
[ "$EASYRSA_SILENT" ] && return
|
|
print_version
|
|
print "$host_out"
|
|
[ "$EASYRSA_DEBUG" ] || return 0
|
|
case "$easyrsa_host_os" in
|
|
win) set ;;
|
|
nix) env ;;
|
|
*) print "Unknown host OS: $easyrsa_host_os"
|
|
esac
|
|
} # => show_host()
|
|
|
|
# Verify the selected algorithm parameters
|
|
verify_algo_params() {
|
|
# EASYRSA_ALGO_PARAMS must be set depending on selected algo
|
|
case "$EASYRSA_ALGO" in
|
|
rsa)
|
|
# Set RSA key size
|
|
EASYRSA_ALGO_PARAMS="$EASYRSA_KEY_SIZE"
|
|
;;
|
|
ec)
|
|
# Verify Elliptic curve
|
|
EASYRSA_ALGO_PARAMS="$(easyrsa_mktemp)"
|
|
|
|
# Create the required ecparams file
|
|
easyrsa_openssl ecparam -name "$EASYRSA_CURVE" \
|
|
-out "$EASYRSA_ALGO_PARAMS" \
|
|
1>/dev/null || die "\
|
|
Failed to generate ecparam file (permissions?) at:
|
|
* $EASYRSA_ALGO_PARAMS"
|
|
;;
|
|
ed)
|
|
# Verify Edwards curve
|
|
easyrsa_openssl genpkey -algorithm "$EASYRSA_CURVE" \
|
|
> /dev/null || die "\
|
|
Edwards Curve $EASYRSA_CURVE not found."
|
|
;;
|
|
*) die "\
|
|
Alg '$EASYRSA_ALGO' is invalid: Must be 'rsa', 'ec' or 'ed'"
|
|
esac
|
|
} # => verify_algo_params()
|
|
|
|
# vars setup
|
|
# Here sourcing of 'vars' if present occurs. If not present, defaults are used
|
|
# to support running without a sourced config format
|
|
vars_setup() {
|
|
# Try to locate a 'vars' file in order of location preference.
|
|
# If one is found, source it
|
|
# NOTE: EASYRSA_PKI is never set here, unless cmd-line --pki-dir=<DIR> is set.
|
|
# NOTE: EASYRSA is never set here, unless done so outside of easyrsa.
|
|
vars=
|
|
|
|
# set up program path
|
|
prog_file="$0"
|
|
# Removed for basic sanity - To re-enable provide a REASON
|
|
#prog_file2="$(which -- "$prog_file" 2>/dev/null)" && prog_file="$prog_file2"
|
|
#prog_file2="$(readlink -f "$prog_file" 2>/dev/null)" && prog_file="$prog_file2"
|
|
prog_dir="${prog_file%/*}"
|
|
if [ "$prog_dir" = . ] || [ "$prog_dir" = "$PWD" ]; then
|
|
prog_in_pwd=1
|
|
else
|
|
unset -v prog_in_pwd
|
|
fi
|
|
|
|
# Program dir vars - This location is least wanted.
|
|
prog_vars="${prog_dir}/vars"
|
|
|
|
# set up PKI path vars - Top preference
|
|
pki_vars="${EASYRSA_PKI:-$PWD/pki}/vars"
|
|
expected_pki_vars="$pki_vars"
|
|
|
|
# Some other place vars, out of scope.
|
|
if [ "$EASYRSA" ]; then
|
|
easy_vars="${EASYRSA}/vars"
|
|
else
|
|
unset -v easy_vars
|
|
fi
|
|
|
|
# vars of last resort - Eventually this file must be removed from EasyRSA
|
|
pwd_vars="$PWD/vars"
|
|
|
|
# Find vars
|
|
# Explicit command-line path:
|
|
if [ "$user_vars_true" ]; then
|
|
if [ -e "$EASYRSA_VARS_FILE" ]; then
|
|
vars="$EASYRSA_VARS_FILE"
|
|
else
|
|
# If the --vars option does not point to a file, show helpful error.
|
|
die "The file '$EASYRSA_VARS_FILE' was not found."
|
|
fi
|
|
unset -v prog_vars pwd_vars easy_vars pki_vars expected_pki_vars
|
|
|
|
# Otherwise, find vars 'the new way'
|
|
else
|
|
# Clear flags - This is the preferred order to find:
|
|
unset -v e_pki_vars e_easy_vars e_pwd_vars e_prog_vars \
|
|
found_vars vars_in_pki
|
|
|
|
# PKI location, if present:
|
|
[ -e "$pki_vars" ] && e_pki_vars=1
|
|
|
|
# EASYRSA, if defined:
|
|
[ -e "$easy_vars" ] && e_easy_vars=1
|
|
|
|
# vars of last resort
|
|
[ -e "$pwd_vars" ] && e_pwd_vars=1
|
|
|
|
# program location:
|
|
[ -e "$prog_vars" ] && e_prog_vars=1
|
|
|
|
# Filter duplicates
|
|
if [ "$e_prog_vars" ] && [ "$e_pwd_vars" ] && [ "$prog_in_pwd" ]
|
|
then
|
|
unset -v prog_vars e_prog_vars
|
|
fi
|
|
|
|
# Allow only one vars to be found, No exceptions!
|
|
found_vars="$((e_pki_vars + e_easy_vars + e_pwd_vars + e_prog_vars))"
|
|
|
|
# If found_vars greater than 1 then output user info and exit
|
|
case "$found_vars" in
|
|
0) unset -v found_vars ;;
|
|
1)
|
|
# If a SINGLE vars file is found then assign $vars
|
|
[ "$e_prog_vars" ] && vars="$prog_vars"
|
|
[ "$e_pwd_vars" ] && vars="$pwd_vars"
|
|
[ "$e_easy_vars" ] && vars="$easy_vars"
|
|
[ "$e_pki_vars" ] && vars="$pki_vars" && vars_in_pki=1
|
|
: # Wipe error status
|
|
;;
|
|
*)
|
|
# For init-pki, skip this check
|
|
if [ "$pki_is_required" ]; then
|
|
[ "$e_pki_vars" ] && print "Found: $pki_vars"
|
|
[ "$e_easy_vars" ] && print "Found: $easy_vars"
|
|
[ "$e_pwd_vars" ] && print "Found: $pwd_vars"
|
|
[ "$e_prog_vars" ] && print "Found: $prog_vars"
|
|
die "\
|
|
Conflicting 'vars' files found.
|
|
|
|
Priority should be given to your PKI vars file:
|
|
* $expected_pki_vars"
|
|
fi
|
|
|
|
# For init-pki, pki/vars will be deleted
|
|
# Another vars file exists, so don't create pki/vars
|
|
no_new_vars=1
|
|
esac
|
|
|
|
# Clean up
|
|
unset -v prog_vars pwd_vars easy_vars pki_vars
|
|
# END: Find vars
|
|
fi
|
|
|
|
# Find vars 'the old way'
|
|
# If $EASYRSA_NO_VARS is defined (not blank) then do not use vars.
|
|
# If $no_pki_required then located vars files are not required.
|
|
if [ "$EASYRSA_NO_VARS" ] || [ "$no_pki_required" ]; then
|
|
: # ok
|
|
|
|
# If a vars file was located then source it
|
|
else
|
|
# $vars remains undefined .. no vars found
|
|
# 'install_data_to_pki vars-setup' will NOT create a default PKI/vars
|
|
if [ -z "$vars" ]; then
|
|
information "No Easy-RSA 'vars' configuration file exists!"
|
|
no_new_vars=1
|
|
|
|
else
|
|
# 'vars' now MUST exist
|
|
[ -e "$vars" ] || die "Missing vars file, expected: $vars"
|
|
|
|
# Sanitize vars
|
|
if grep -Eq 'EASYRSA_PASSIN|EASYRSA_PASSOUT' "$vars"; then
|
|
die "\
|
|
Variable EASYRSA_PASSIN or EASYRSA_PASSOUT has been found in the configuration
|
|
file. Storing sensitive information in the configuration file is not
|
|
recommended - please remove it from there before continuing."
|
|
fi
|
|
|
|
# Warning: Unsupported characters
|
|
if grep '^[[:blank:]]*set_var[[:blank:]]*.*$' "$vars" | \
|
|
grep -q -e '`'
|
|
then
|
|
warn '\
|
|
Unsupported characters are present in the vars file.
|
|
These characters are not supported: ` backtick
|
|
Sourcing the vars file and building certificates will probably fail ..'
|
|
fi
|
|
|
|
# Enable sourcing 'vars'
|
|
# shellcheck disable=SC2034 # EASYRSA_CALLER appears unused.
|
|
EASYRSA_CALLER=1
|
|
|
|
# Test souring 'vars' in a subshell
|
|
# shellcheck disable=1090 # can't follow non-constant source. vars
|
|
( . "$vars" ) || die "Failed to source the vars file."
|
|
|
|
# Source 'vars' now
|
|
# shellcheck disable=1090 # can't follow non-constant source. vars
|
|
. "$vars" 2>/dev/null
|
|
unset -v EASYRSA_CALLER
|
|
fi
|
|
fi
|
|
|
|
# Set defaults, preferring existing env-vars if present
|
|
set_var EASYRSA "$PWD"
|
|
set_var EASYRSA_OPENSSL openssl
|
|
set_var EASYRSA_PKI "$EASYRSA/pki"
|
|
set_var EASYRSA_DN cn_only
|
|
set_var EASYRSA_REQ_COUNTRY "US"
|
|
set_var EASYRSA_REQ_PROVINCE "California"
|
|
set_var EASYRSA_REQ_CITY "San Francisco"
|
|
set_var EASYRSA_REQ_ORG "Copyleft Certificate Co"
|
|
set_var EASYRSA_REQ_EMAIL me@example.net
|
|
set_var EASYRSA_REQ_OU "My Organizational Unit"
|
|
set_var EASYRSA_ALGO rsa
|
|
|
|
case "$EASYRSA_ALGO" in
|
|
rsa)
|
|
set_var EASYRSA_KEY_SIZE 2048
|
|
;;
|
|
ec)
|
|
set_var EASYRSA_CURVE secp384r1
|
|
set_var EASYRSA_EC_DIR "$EASYRSA_PKI/ecparams"
|
|
;;
|
|
ed)
|
|
set_var EASYRSA_CURVE ed25519
|
|
;;
|
|
*) die "Unknown algorithm '$EASYRSA_ALGO'"
|
|
esac
|
|
|
|
set_var EASYRSA_CA_EXPIRE 3650
|
|
set_var EASYRSA_CERT_EXPIRE 825 # new default of 36 months
|
|
set_var EASYRSA_CERT_RENEW 90
|
|
set_var EASYRSA_CRL_DAYS 180
|
|
set_var EASYRSA_NS_SUPPORT no
|
|
set_var EASYRSA_NS_COMMENT "Easy-RSA (~VER~) Generated Certificate"
|
|
set_var EASYRSA_TEMP_DIR "$EASYRSA_PKI"
|
|
set_var EASYRSA_REQ_CN ChangeMe
|
|
set_var EASYRSA_DIGEST sha256
|
|
|
|
set_var EASYRSA_SSL_CONF "$EASYRSA_PKI/openssl-easyrsa.cnf"
|
|
set_var EASYRSA_SAFE_CONF "$EASYRSA_PKI/safessl-easyrsa.cnf"
|
|
|
|
set_var EASYRSA_KDC_REALM "CHANGEME.EXAMPLE.COM"
|
|
|
|
# Find x509-types but do not fail - Not fatal here, used by 'help'
|
|
install_data_to_pki x509-types-only
|
|
|
|
# For commands which 'require a PKI' and the PKI exists
|
|
if [ "$pki_is_required" ] && [ -d "$EASYRSA_PKI" ]; then
|
|
|
|
# Verify SSL Lib - One time ONLY
|
|
verify_ssl_lib
|
|
|
|
# mkdir Temp dir session
|
|
secure_session || die "Temporary directory secure-session failed."
|
|
|
|
if [ -d "$EASYRSA_TEMP_DIR" ]; then
|
|
|
|
#TODO: This should be removed. Not really suitable for packaging.
|
|
#set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types"
|
|
|
|
# Hard break from 'old' Easy-RSA, see obsolete comment above.
|
|
# Install data-files into ALL PKIs
|
|
# This will find x509-types and export EASYRSA_EXT_DIR or die.
|
|
# Other errors only require warning.
|
|
install_data_to_pki vars-setup || \
|
|
warn "install_data_to_pki vars-setup Failed"
|
|
|
|
# Installation information
|
|
[ "$no_new_vars" ] || \
|
|
information "Using Easy-RSA configuration: $vars"
|
|
# if the vars file in use is not in the PKI and not user defined
|
|
# Show messages
|
|
if [ "$vars_in_pki" ] || [ "$user_vars_true" ] || \
|
|
[ "$no_new_vars" ]
|
|
then
|
|
: # ok - No message required
|
|
else
|
|
prefer_vars_in_pki_msg
|
|
fi
|
|
|
|
# Verify selected algorithm and parameters
|
|
verify_algo_params
|
|
|
|
# Last setup msg
|
|
information "\
|
|
Using SSL: $EASYRSA_OPENSSL $ssl_version
|
|
"
|
|
|
|
else
|
|
# If the directory does not exist then we have not run init-pki
|
|
# The temp-dir is ALWAYS verified by secure_session()
|
|
die "Temporary directory does not exist: $EASYRSA_TEMP_DIR"
|
|
fi
|
|
fi
|
|
} # vars_setup()
|
|
|
|
# variable assignment by indirection when undefined; merely exports
|
|
# the variable when it is already defined (even if currently null)
|
|
# Sets $1 as the value contained in $2 and exports (may be blank)
|
|
set_var() {
|
|
var=$1
|
|
shift
|
|
value="$*"
|
|
eval "export $var=\"\${$var-$value}\""
|
|
} #=> set_var()
|
|
|
|
|
|
|
|
############################################################################
|
|
# Upgrade v2 PKI to v3 PKI
|
|
|
|
# You can report problems on the normal openvpn support channels:
|
|
# --------------------------------------------------------------------------
|
|
# 1. The Openvpn Forum: https://forums.openvpn.net/viewforum.php?f=31
|
|
# 2. The #easyrsa IRC channel at libera.chat
|
|
# 3. Info: https://community.openvpn.net/openvpn/wiki/easyrsa-upgrade
|
|
# --------------------------------------------------------------------------
|
|
#
|
|
|
|
up23_fail_upgrade ()
|
|
{
|
|
# Replace die()
|
|
unset -v EASYRSA_BATCH
|
|
notice "
|
|
============================================================================
|
|
The update has failed but NOTHING has been lost.
|
|
|
|
ERROR: $1
|
|
----------------------------------------------------------------------------
|
|
|
|
Further info:
|
|
* https://community.openvpn.net/openvpn/wiki/easyrsa-upgrade#ersa-up23-fails
|
|
|
|
Easyrsa3 upgrade FAILED
|
|
============================================================================
|
|
"
|
|
exit 9
|
|
} #=> up23_fail_upgrade ()
|
|
|
|
up23_verbose ()
|
|
{
|
|
[ "$VERBOSE" ] || return 0
|
|
printf "%s\n" "$1"
|
|
} #=> up23_verbose ()
|
|
|
|
up23_verify_new_pki ()
|
|
{
|
|
# Fail now, before any changes are made
|
|
|
|
up23_verbose "> Verify DEFAULT NEW PKI does not exist .."
|
|
EASYRSA_NEW_PKI="$EASYRSA/pki"
|
|
[ -d "$EASYRSA_NEW_PKI" ] \
|
|
&& up23_fail_upgrade "DEFAULT NEW PKI exists: $EASYRSA_NEW_PKI"
|
|
|
|
up23_verbose "> Verify VERY-SAFE-PKI does not exist .."
|
|
EASYRSA_SAFE_PKI="$EASYRSA/VERY-SAFE-PKI"
|
|
[ -d "$EASYRSA_SAFE_PKI" ] \
|
|
&& up23_fail_upgrade "VERY-SAFE-PKI exists: $EASYRSA_SAFE_PKI"
|
|
|
|
up23_verbose "> Verify openssl-easyrsa.cnf does exist .."
|
|
EASYRSA_SSL_CNFFILE="$EASYRSA/openssl-easyrsa.cnf"
|
|
[ -f "$EASYRSA_SSL_CNFFILE" ] \
|
|
|| up23_fail_upgrade "cannot find $EASYRSA_SSL_CNFFILE"
|
|
|
|
up23_verbose "> Verify vars.example does exist .."
|
|
EASYRSA_VARSV3_EXMP="$EASYRSA/vars.example"
|
|
[ -f "$EASYRSA_VARSV3_EXMP" ] \
|
|
|| up23_fail_upgrade "cannot find $EASYRSA_VARSV3_EXMP"
|
|
|
|
up23_verbose "> OK"
|
|
up23_verbose " Initial dirs & files are in a workable state."
|
|
} #=> up23_verify_new_pki ()
|
|
|
|
# shellcheck disable=SC2154
|
|
up23_verify_current_pki ()
|
|
{
|
|
up23_verbose "> Verify CURRENT PKI vars .."
|
|
|
|
# This can probably be improved
|
|
EASYRSA_NO_REM="$(grep '^set ' "$EASYRSA_VER2_VARSFILE")"
|
|
|
|
# This list may not be complete
|
|
# Not required: DH_KEY_SIZE PKCS11_MODULE_PATH PKCS11_PIN
|
|
for i in KEY_DIR KEY_SIZE KEY_COUNTRY KEY_PROVINCE \
|
|
KEY_CITY KEY_ORG KEY_EMAIL KEY_CN KEY_NAME KEY_OU
|
|
do
|
|
# Effectively, source the v2 vars file
|
|
UNIQUE="set $i"
|
|
KEY_grep="$(printf "%s\n" "$EASYRSA_NO_REM" | grep "$UNIQUE")"
|
|
KEY_value="${KEY_grep##*=}"
|
|
set_var $i "$KEY_value"
|
|
done
|
|
|
|
[ -d "$KEY_DIR" ] || up23_fail_upgrade "Cannot find CURRENT PKI KEY_DIR: $KEY_DIR"
|
|
|
|
up23_verbose "> OK"
|
|
up23_verbose " Current CURRENT PKI vars uses PKI in: $KEY_DIR"
|
|
} #=> up23_verify_current_pki ()
|
|
|
|
# shellcheck disable=SC2154
|
|
up23_verify_current_ca ()
|
|
{
|
|
up23_verbose "> Find CA .."
|
|
# $KEY_DIR is assigned in up23_verify_current_pki ()
|
|
[ -f "$KEY_DIR/ca.crt" ] \
|
|
|| up23_fail_upgrade "Cannot find current ca.crt: $KEY_DIR/ca.crt"
|
|
up23_verbose "> OK"
|
|
|
|
# If CA is already verified then return
|
|
in_file="$KEY_DIR/ca.crt"
|
|
[ "$CURRENT_CA_IS_VERIFIED" = "$in_file" ] && return 0
|
|
format="x509"
|
|
|
|
# Current CA is unverified
|
|
# Extract the current CA details
|
|
name_opts="utf8,sep_multiline,space_eq,lname,align"
|
|
CA_SUBJECT="$(
|
|
easyrsa_openssl $format -in "$in_file" -subject -noout \
|
|
-nameopt "$name_opts"
|
|
)"
|
|
|
|
# Extract individual elements
|
|
CA_countryName="$(printf "%s\n" "$CA_SUBJECT" \
|
|
| grep countryName | sed "s\`^.*=\ \`\`g")"
|
|
CA_stateOrProvinceName="$(printf "%s\n" "$CA_SUBJECT" \
|
|
| grep stateOrProvinceName | sed "s\`^.*=\ \`\`g")"
|
|
CA_localityName="$(printf "%s\n" "$CA_SUBJECT" \
|
|
| grep localityName | sed "s\`^.*=\ \`\`g")"
|
|
CA_organizationName="$(printf "%s\n" "$CA_SUBJECT" \
|
|
| grep organizationName | sed "s\`^.*=\ \`\`g")"
|
|
CA_organizationalUnitName="$(printf "%s\n" "$CA_SUBJECT" \
|
|
| grep organizationalUnitName | sed "s\`^.*=\ \`\`g")"
|
|
CA_emailAddress="$(printf "%s\n" "$CA_SUBJECT" \
|
|
| grep emailAddress | sed "s\`^.*=\ \`\`g")"
|
|
|
|
# Match the current CA elements to the vars file settings
|
|
CA_vars_match=1
|
|
[ "$CA_countryName" = "$KEY_COUNTRY" ] || CA_vars_match=0
|
|
[ "$CA_stateOrProvinceName" = "$KEY_PROVINCE" ] || CA_vars_match=0
|
|
[ "$CA_localityName" = "$KEY_CITY" ] || CA_vars_match=0
|
|
[ "$CA_organizationName" = "$KEY_ORG" ] || CA_vars_match=0
|
|
[ "$CA_organizationalUnitName" = "$KEY_OU" ] || CA_vars_match=0
|
|
[ "$CA_emailAddress" = "$KEY_EMAIL" ] || CA_vars_match=0
|
|
|
|
if [ "$CA_vars_match" -eq 1 ]
|
|
then
|
|
CURRENT_CA_IS_VERIFIED="partially"
|
|
else
|
|
up23_fail_upgrade "CA certificate does not match vars file settings"
|
|
fi
|
|
|
|
opts="-certopt no_pubkey,no_sigdump"
|
|
if [ ! "$EASYRSA_BATCH" ]
|
|
then
|
|
up23_show_current_ca
|
|
elif [ "$VERBOSE" ]
|
|
then
|
|
up23_show_current_ca
|
|
fi
|
|
confirm "* Confirm CA shown above is correct: " "yes" \
|
|
"Found current CA at: $KEY_DIR/ca.crt"
|
|
CURRENT_CA_IS_VERIFIED="$in_file"
|
|
} #=> up23_verify_current_ca ()
|
|
|
|
up23_show_current_ca ()
|
|
{
|
|
name_opts="utf8,sep_multiline,space_eq,lname,align"
|
|
printf "%s\n" "-------------------------------------------------------------------------"
|
|
# $opts is always set here
|
|
# shellcheck disable=SC2086 # Ignore unquoted variables
|
|
easyrsa_openssl $format -in "$in_file" -noout -text \
|
|
-nameopt "$name_opts" $opts || die "\
|
|
OpenSSL failure to process the input CA certificate: $in_file"
|
|
printf "%s\n" "-------------------------------------------------------------------------"
|
|
} #=> up23_show_current_ca ()
|
|
|
|
up23_backup_current_pki ()
|
|
{
|
|
up23_verbose "> Backup current PKI .."
|
|
|
|
mkdir -p "$EASYRSA_SAFE_PKI" \
|
|
|| up23_fail_upgrade "Failed to create safe PKI dir: $EASYRSA_SAFE_PKI"
|
|
|
|
cp -r "$KEY_DIR" "$EASYRSA_SAFE_PKI" \
|
|
|| up23_fail_upgrade "Failed to copy $KEY_DIR to $EASYRSA_SAFE_PKI"
|
|
|
|
# EASYRSA_VER2_VARSFILE is either version 2 *nix ./vars or Win vars.bat
|
|
cp "$EASYRSA_VER2_VARSFILE" "$EASYRSA_SAFE_PKI" \
|
|
|| up23_fail_upgrade "Failed to copy $EASYRSA_VER2_VARSFILE to EASYRSA_SAFE_PKI"
|
|
|
|
up23_verbose "> OK"
|
|
up23_verbose " Current PKI backup created in: $EASYRSA_SAFE_PKI"
|
|
} #=> up23_backup_current_pki ()
|
|
|
|
up23_create_new_pki ()
|
|
{
|
|
# Dirs: renewed and revoked are created when used.
|
|
up23_verbose "> Create NEW PKI .."
|
|
up23_verbose ">> Create NEW PKI dirs .."
|
|
for i in private reqs issued certs_by_serial
|
|
do
|
|
mkdir -p "$EASYRSA_PKI/$i" \
|
|
|| up23_fail_upgrade "Failed to Create NEW PKI dir: $EASYRSA_PKI/$i"
|
|
done
|
|
up23_verbose ">> OK"
|
|
|
|
up23_verbose ">> Copy database to NEW PKI .."
|
|
# Failure for these is not optional
|
|
# Files ignored: index.txt.old serial.old
|
|
for i in index.txt serial ca.crt index.txt.attr
|
|
do
|
|
cp "$KEY_DIR/$i" "$EASYRSA_PKI" \
|
|
|| up23_fail_upgrade "Failed to copy $KEY_DIR/$i to $EASYRSA_PKI"
|
|
done
|
|
up23_verbose ">> OK"
|
|
|
|
up23_verbose ">> Copy current PKI to NEW PKI .."
|
|
for i in "csr.reqs" "pem.certs_by_serial" "crt.issued" "key.private" \
|
|
"p12.private" "p8.private" "p7b.issued"
|
|
do
|
|
FILE_EXT="${i%%.*}"
|
|
DEST_DIR="${i##*.}"
|
|
if ls "$KEY_DIR/"*".$FILE_EXT" > /dev/null 2>&1; then
|
|
cp "$KEY_DIR/"*".$FILE_EXT" "$EASYRSA_PKI/$DEST_DIR" \
|
|
|| up23_fail_upgrade "Failed to copy .$FILE_EXT"
|
|
else
|
|
up23_verbose " Note: No .$FILE_EXT files found"
|
|
fi
|
|
done
|
|
up23_verbose ">> OK"
|
|
up23_verbose "> OK"
|
|
|
|
# Todo: CRL - Or generate a new CRL on completion
|
|
up23_verbose " New PKI created in: $EASYRSA_PKI"
|
|
} #=> up23_create_new_pki ()
|
|
|
|
up23_upgrade_ca ()
|
|
{
|
|
[ -d "$EASYRSA_PKI" ] || return 0
|
|
up23_verbose "> Confirm that index.txt.attr exists and 'unique_subject = no'"
|
|
if [ -f "$EASYRSA_PKI/index.txt.attr" ]
|
|
then
|
|
if grep -q 'unique_subject = no' "$EASYRSA_PKI/index.txt.attr"
|
|
then
|
|
# If index.txt.attr exists and "unique_suject = no" then do nothing
|
|
return 0
|
|
fi
|
|
else
|
|
# If index.txt.attr does not exists then do nothing
|
|
return 0
|
|
fi
|
|
|
|
# Otherwise this is required for all easyrsa v3
|
|
#confirm "Set 'unique_subject = no' in index.txt.attr for your current CA: " \
|
|
#"yes" "This version of easyrsa requires that 'unique_subject = no' is set correctly"
|
|
|
|
printf "%s\n" "unique_subject = no" > "$EASYRSA_PKI/index.txt.attr"
|
|
up23_verbose "> OK"
|
|
up23_verbose " Upgraded index.txt.attr to v306+"
|
|
} #=> up23_upgrade_index_txt_attr ()
|
|
|
|
up23_create_openssl_cnf ()
|
|
{
|
|
up23_verbose "> OpenSSL config .."
|
|
EASYRSA_PKI_SSL_CNFFILE="$EASYRSA_PKI/openssl-easyrsa.cnf"
|
|
EASYRSA_PKI_SAFE_CNFFILE="$EASYRSA_PKI/safessl-easyrsa.cnf"
|
|
cp "$EASYRSA_SSL_CNFFILE" "$EASYRSA_PKI_SSL_CNFFILE" \
|
|
|| up23_fail_upgrade "create $EASYRSA_PKI_SSL_CNFFILE"
|
|
up23_verbose "> OK"
|
|
up23_verbose " New OpenSSL config file created in: $EASYRSA_PKI_SSL_CNFFILE"
|
|
|
|
# Create secure session
|
|
# Because the upgrade runs twice, once as a test and then for real
|
|
# EASYRSA_TEMP_DIR_session must be cleared to avoid overload error
|
|
[ "$EASYRSA_TEMP_DIR_session" ] && unset -v EASYRSA_TEMP_DIR_session
|
|
up23_verbose "> Create secure session"
|
|
secure_session || die "up23_create_openssl_cnf - secure_session failed."
|
|
up23_verbose "> OK"
|
|
up23_verbose " secure session: $EASYRSA_TEMP_DIR_session"
|
|
|
|
# Create $EASYRSA_PKI/safessl-easyrsa.cnf
|
|
easyrsa_openssl makesafeconf
|
|
if [ -f "$EASYRSA_PKI_SAFE_CNFFILE" ]
|
|
then
|
|
up23_verbose " New SafeSSL config file created in: $EASYRSA_PKI_SAFE_CNFFILE"
|
|
else
|
|
up23_verbose " FAILED to create New SafeSSL config file in: $EASYRSA_PKI_SAFE_CNFFILE"
|
|
fi
|
|
} #=> up23_create_openssl_cnf ()
|
|
|
|
up23_move_easyrsa2_programs ()
|
|
{
|
|
# These files may not exist here
|
|
up23_verbose "> Move easyrsa2 programs to SAFE PKI .."
|
|
for i in build-ca build-dh build-inter build-key build-key-pass \
|
|
build-key-pkcs12 build-key-server build-req build-req-pass \
|
|
clean-all inherit-inter list-crl pkitool revoke-full sign-req \
|
|
whichopensslcnf build-ca-pass build-key-server-pass init-config \
|
|
make-crl revoke-crt openssl-0.9.6.cnf openssl-0.9.8.cnf \
|
|
openssl-1.0.0.cnf openssl.cnf README.txt index.txt.start \
|
|
vars.bat.sample serial.start
|
|
do
|
|
# Although unlikely, both files could exist
|
|
# EG: ./build-ca and ./build-ca.bat
|
|
NIX_FILE="$EASYRSA/$i"
|
|
WIN_FILE="$EASYRSA/$i.bat"
|
|
if [ -f "$NIX_FILE" ]
|
|
then
|
|
cp "$NIX_FILE" "$EASYRSA_SAFE_PKI" \
|
|
|| up23_fail_upgrade "copy $NIX_FILE $EASYRSA_SAFE_PKI"
|
|
fi
|
|
|
|
if [ -f "$WIN_FILE" ]
|
|
then
|
|
cp "$WIN_FILE" "$EASYRSA_SAFE_PKI" \
|
|
|| up23_fail_upgrade "copy $WIN_FILE $EASYRSA_SAFE_PKI"
|
|
fi
|
|
|
|
if [ ! -f "$NIX_FILE" ] && [ ! -f "$WIN_FILE" ]
|
|
then
|
|
up23_verbose "File does not exist, ignoring: $i(.bat)"
|
|
fi
|
|
|
|
# These files are not removed on TEST run
|
|
[ "$NOSAVE" -eq 1 ] && rm -f "$NIX_FILE" "$WIN_FILE"
|
|
done
|
|
|
|
up23_verbose "> OK"
|
|
up23_verbose " Easyrsa2 programs successfully moved to: $EASYRSA_SAFE_PKI"
|
|
} #=> up23_move_easyrsa2_programs ()
|
|
|
|
# shellcheck disable=SC2154
|
|
up23_build_v3_vars ()
|
|
{
|
|
up23_verbose "> Build v3 vars file .."
|
|
|
|
EASYRSA_EXT="easyrsa-upgrade-23"
|
|
EASYRSA_VARSV2_TMP="$EASYRSA/vars-v2.tmp.$EASYRSA_EXT"
|
|
rm -f "$EASYRSA_VARSV2_TMP"
|
|
EASYRSA_VARSV3_TMP="$EASYRSA/vars-v3.tmp.$EASYRSA_EXT"
|
|
rm -f "$EASYRSA_VARSV3_TMP"
|
|
EASYRSA_VARSV3_NEW="$EASYRSA/vars-v3.new.$EASYRSA_EXT"
|
|
rm -f "$EASYRSA_VARSV3_NEW"
|
|
EASYRSA_VARSV3_WRN="$EASYRSA/vars-v3.wrn.$EASYRSA_EXT"
|
|
rm -f "$EASYRSA_VARSV3_WRN"
|
|
|
|
printf "%s\n" "\
|
|
########################++++++++++#########################
|
|
### ###
|
|
### WARNING: THIS FILE WAS AUTOMATICALLY GENERATED ###
|
|
### ALL SETTINGS ARE AT THE END OF THE FILE ###
|
|
### ###
|
|
########################++++++++++#########################
|
|
|
|
" > "$EASYRSA_VARSV3_WRN" || up23_fail_upgrade "Failed to create $EASYRSA_VARSV3_WRN"
|
|
|
|
# Create vars v3 temp file from sourced vars v2 key variables
|
|
{
|
|
printf "%s\n" "set_var EASYRSA_KEY_SIZE $KEY_SIZE"
|
|
printf "%s\n" "set_var EASYRSA_REQ_COUNTRY \"$KEY_COUNTRY\""
|
|
printf "%s\n" "set_var EASYRSA_REQ_PROVINCE \"$KEY_PROVINCE\""
|
|
printf "%s\n" "set_var EASYRSA_REQ_CITY \"$KEY_CITY\""
|
|
printf "%s\n" "set_var EASYRSA_REQ_ORG \"$KEY_ORG\""
|
|
printf "%s\n" "set_var EASYRSA_REQ_EMAIL \"$KEY_EMAIL\""
|
|
printf "%s\n" "set_var EASYRSA_REQ_OU \"$KEY_OU\""
|
|
printf "%s\n" 'set_var EASYRSA_NS_SUPPORT "yes"'
|
|
printf "%s\n" 'set_var EASYRSA_DN "org"'
|
|
printf "%s\n" 'set_var EASYRSA_RAND_SN "no"'
|
|
printf "%s\n" ""
|
|
} > "$EASYRSA_VARSV3_TMP" \
|
|
|| up23_fail_upgrade "Failed to create $EASYRSA_VARSV3_TMP"
|
|
|
|
# cat temp files into new v3 vars
|
|
cat "$EASYRSA_VARSV3_WRN" "$EASYRSA_VARSV3_EXMP" "$EASYRSA_VARSV3_TMP" \
|
|
> "$EASYRSA_VARSV3_NEW" \
|
|
|| up23_fail_upgrade "Failed to create $EASYRSA_VARSV3_NEW"
|
|
|
|
# This file must be created and restored at the end of TEST
|
|
# for the REAL update to to succeed
|
|
EASYRSA_VARS_LIVEBKP="$EASYRSA_TARGET_VARSFILE.livebackup"
|
|
cp "$EASYRSA_VER2_VARSFILE" "$EASYRSA_VARS_LIVEBKP" \
|
|
|| up23_fail_upgrade "Failed to create $EASYRSA_VARS_LIVEBKP"
|
|
rm -f "$EASYRSA_VER2_VARSFILE"
|
|
|
|
# "$EASYRSA_TARGET_VARSFILE" is always $EASYRSA/vars
|
|
cp "$EASYRSA_VARSV3_NEW" "$EASYRSA_TARGET_VARSFILE" \
|
|
|| up23_fail_upgrade "copy $EASYRSA_VARSV3_NEW to $EASYRSA_TARGET_VARSFILE"
|
|
|
|
# Delete temp files
|
|
rm -f "$EASYRSA_VARSV2_TMP" "$EASYRSA_VARSV3_TMP" \
|
|
"$EASYRSA_VARSV3_NEW" "$EASYRSA_VARSV3_WRN"
|
|
|
|
up23_verbose "> OK"
|
|
up23_verbose " New v3 vars file created in: $EASYRSA_TARGET_VARSFILE"
|
|
} #=> up23_build_v3_vars ()
|
|
|
|
# shellcheck disable=SC2154
|
|
up23_do_upgrade_23 ()
|
|
{
|
|
up23_verbose "============================================================================"
|
|
up23_verbose "Begin ** $1 ** upgrade process .."
|
|
up23_verbose ""
|
|
up23_verbose "Easyrsa upgrade version: $EASYRSA_UPGRADE_23"
|
|
up23_verbose ""
|
|
|
|
up23_verify_new_pki
|
|
up23_verify_current_pki
|
|
up23_verify_current_ca
|
|
up23_backup_current_pki
|
|
up23_create_new_pki
|
|
up23_upgrade_ca
|
|
up23_move_easyrsa2_programs
|
|
up23_build_v3_vars
|
|
up23_create_openssl_cnf
|
|
|
|
if [ "$NOSAVE" -eq 0 ]
|
|
then
|
|
# Must stay in this order
|
|
# New created dirs: EASYRSA_NEW_PKI and EASYRSA_SAFE_PKI
|
|
rm -rf "$EASYRSA_NEW_PKI"
|
|
rm -rf "$EASYRSA_SAFE_PKI"
|
|
# EASYRSA_TARGET_VARSFILE is always the new created v3 vars
|
|
# Need to know if this fails
|
|
rm "$EASYRSA_TARGET_VARSFILE" \
|
|
|| up23_fail_upgrade "remove new vars file: $EASYRSA_TARGET_VARSFILE"
|
|
# EASYRSA_VER2_VARSFILE is either v2 *nix ./vars or Win vars.bat
|
|
# Need this dance because v2 vars is same name as v3 vars above
|
|
cp "$EASYRSA_VARS_LIVEBKP" "$EASYRSA_VER2_VARSFILE"
|
|
fi
|
|
rm -f "$EASYRSA_VARS_LIVEBKP"
|
|
} #= up23_do_upgrade_23 ()
|
|
|
|
up23_manage_upgrade_23 ()
|
|
{
|
|
EASYRSA_UPGRADE_VERSION="v1.0a (2020/01/08)"
|
|
EASYRSA_UPGRADE_TYPE="$1"
|
|
EASYRSA_FOUND_VARS=0
|
|
|
|
# Verify all existing versions of vars/vars.bat
|
|
if [ -f "$vars" ]
|
|
then
|
|
if grep -q 'Complain if a user tries to do this:' "$vars"
|
|
then
|
|
EASYRSA_FOUND_VARS=1
|
|
EASYRSA_VARS_IS_VER3=1
|
|
fi
|
|
|
|
# Easyrsa v3 does not use NOR allow use of `export`.
|
|
if grep -q 'export' "$vars"
|
|
then
|
|
EASYRSA_FOUND_VARS=1
|
|
EASYRSA_VARS_IS_VER2=1
|
|
EASYRSA_VER2_VARSFILE="$vars"
|
|
EASYRSA_TARGET_VARSFILE="$vars"
|
|
fi
|
|
fi
|
|
|
|
if [ -f "$EASYRSA/vars.bat" ]
|
|
then
|
|
EASYRSA_FOUND_VARS=1
|
|
EASYRSA_VARS_IS_WIN2=1
|
|
EASYRSA_VER2_VARSFILE="$EASYRSA/vars.bat"
|
|
EASYRSA_TARGET_VARSFILE="$EASYRSA/vars"
|
|
fi
|
|
|
|
if [ $EASYRSA_FOUND_VARS -ne 1 ];
|
|
then
|
|
die "vars file not found"
|
|
fi
|
|
|
|
# Only allow specific vars/vars.bat to exist
|
|
if [ "$EASYRSA_VARS_IS_VER3" ] && [ "$EASYRSA_VARS_IS_VER2" ]
|
|
then
|
|
die "Verify your current vars file, v3 cannot use 'export'."
|
|
fi
|
|
|
|
if [ "$EASYRSA_VARS_IS_VER3" ] && [ "$EASYRSA_VARS_IS_WIN2" ]
|
|
then
|
|
die "Verify your current vars/vars.bat file, cannot have both."
|
|
fi
|
|
|
|
if [ "$EASYRSA_VARS_IS_VER2" ] && [ "$EASYRSA_VARS_IS_WIN2" ]
|
|
then
|
|
die "Verify your current vars/vars.bat file, cannot have both."
|
|
fi
|
|
|
|
# Die on invalid upgrade type or environment
|
|
if [ "$EASYRSA_UPGRADE_TYPE" = "ca" ]
|
|
then
|
|
if [ "$EASYRSA_VARS_IS_VER3" ]
|
|
then
|
|
# v3 ensure index.txt.attr "unique_subject = no"
|
|
up23_upgrade_ca
|
|
unset -v EASYRSA_BATCH
|
|
notice "Your CA is fully up to date."
|
|
return 0
|
|
else
|
|
die "Only v3 PKI CA can be upgraded."
|
|
fi
|
|
fi
|
|
|
|
if [ "$EASYRSA_UPGRADE_TYPE" = "pki" ]
|
|
then
|
|
if [ "$EASYRSA_VARS_IS_VER3" ]
|
|
then
|
|
unset -v EASYRSA_BATCH
|
|
notice "Your PKI is fully up to date."
|
|
return 0
|
|
fi
|
|
else
|
|
die "upgrade type must be 'pki' or 'ca'."
|
|
fi
|
|
|
|
# PKI is potentially suitable for upgrade
|
|
|
|
warn "
|
|
=========================================================================
|
|
|
|
* WARNING *
|
|
|
|
Found settings from EasyRSA-v2 which are not compatible with EasyRSA-v3.
|
|
Before you can continue, EasyRSA must upgrade your settings and PKI.
|
|
* Found EASYRSA and vars file:
|
|
$EASYRSA
|
|
$EASYRSA_VER2_VARSFILE :
|
|
|
|
Further info:
|
|
* https://community.openvpn.net/openvpn/wiki/easyrsa-upgrade
|
|
|
|
Easyrsa upgrade version: $EASYRSA_UPGRADE_VERSION
|
|
=========================================================================
|
|
"
|
|
|
|
# Test upgrade
|
|
|
|
NOSAVE=0
|
|
|
|
confirm "* EasyRSA **TEST** upgrade (Changes will NOT be written): " "yes" "
|
|
This upgrade will TEST that the upgrade works BEFORE making any changes."
|
|
|
|
up23_do_upgrade_23 "TEST"
|
|
|
|
notice "
|
|
=========================================================================
|
|
|
|
* NOTICE *
|
|
|
|
EasyRSA upgrade **TEST** has successfully completed.
|
|
"
|
|
# Upgrade for REAL
|
|
|
|
NOSAVE=1
|
|
|
|
confirm "* EasyRSA **REAL** upgrade (Changes WILL be written): " "yes" "
|
|
=========================================================================
|
|
|
|
* WARNING *
|
|
|
|
Run REAL upgrade: Answer yes (Once completed you will have a version 3 PKI)
|
|
Terminate upgrade: Answer no (No changes have been made to your current PKI)
|
|
"
|
|
|
|
confirm "* Confirm **REAL** upgrade (Changes will be written): " "yes" "
|
|
=========================================================================
|
|
|
|
* SECOND WARNING *
|
|
|
|
This upgrade will permanently write changes to your PKI !
|
|
(With full backup backout)
|
|
"
|
|
up23_do_upgrade_23 "REAL"
|
|
|
|
notice "
|
|
=========================================================================
|
|
|
|
* NOTICE *
|
|
|
|
Your settings and PKI have been successfully upgraded to EasyRSA version3
|
|
|
|
A backup of your current PKI is here:
|
|
$EASYRSA_SAFE_PKI
|
|
|
|
* IMPORTANT NOTICE *
|
|
|
|
1. YOU MUST VERIFY THAT YOUR NEW ./vars FILE IS SETUP CORRECTLY
|
|
2. IF YOU ARE USING WINDOWS YOU MUST ENSURE THAT openssl IS CORRECTLY DEFINED
|
|
IN ./vars (example follows)
|
|
|
|
#
|
|
# This sample is in Windows syntax -- edit it for your path if not using PATH:
|
|
# set_var EASYRSA_OPENSSL \"C:/Program Files/OpenSSL-Win32/bin/openssl.exe\"
|
|
#
|
|
# Alternate location (Note: Forward slash '/' is correct for Windpws):
|
|
# set_var EASYRSA_OPENSSL \"C:/Program Files/Openvpn/bin/openssl.exe\"
|
|
#
|
|
|
|
3. Finally, you can verify that easyrsa works by using these two commands:
|
|
./easyrsa show-ca (Verify that your CA is intact and correct)
|
|
./easyrsa gen-crl ((re)-generate a CRL file)
|
|
|
|
Further info:
|
|
* https://community.openvpn.net/openvpn/wiki/easyrsa-upgrade"
|
|
up23_verbose "
|
|
* UPGRADE COMPLETED SUCCESSFULLY *
|
|
"
|
|
|
|
return 0
|
|
|
|
} # => up23_manage_upgrade_23 ()
|
|
|
|
print_version()
|
|
{
|
|
ssl_version="$("${EASYRSA_OPENSSL:-openssl}" version 2>/dev/null)"
|
|
cat << VERSION_TEXT
|
|
EasyRSA Version Information
|
|
Version: $EASYRSA_version
|
|
Generated: ~DATE~
|
|
SSL Lib: $ssl_version
|
|
Git Commit: ~GITHEAD~
|
|
Source Repo: https://github.com/OpenVPN/easy-rsa${EASYRSA_DEBUG+
|
|
* debug enabled}
|
|
VERSION_TEXT
|
|
} # => print_version ()
|
|
|
|
|
|
########################################
|
|
# Invocation entry point:
|
|
|
|
EASYRSA_version="~VER~"
|
|
NL='
|
|
'
|
|
|
|
# Be secure with a restrictive umask
|
|
[ "$EASYRSA_NO_UMASK" ] || umask "${EASYRSA_UMASK:-077}"
|
|
|
|
# Register cleanup on EXIT
|
|
trap 'cleanup $?' EXIT
|
|
# When SIGHUP, SIGINT, SIGQUIT, SIGABRT and SIGTERM,
|
|
# explicitly exit to signal EXIT (non-bash shells)
|
|
trap "exit 1" 1
|
|
trap "exit 2" 2
|
|
trap "exit 3" 3
|
|
trap "exit 6" 6
|
|
trap "exit 14" 15
|
|
|
|
# Get host details - No configurable input allowed
|
|
detect_host
|
|
|
|
# Initialisation requirements
|
|
unset -v \
|
|
easyrsa_error_exit \
|
|
prohibit_no_pass \
|
|
user_vars_true \
|
|
user_san_true \
|
|
alias_days
|
|
|
|
# Parse options
|
|
while :; do
|
|
# Reset per pass flags
|
|
unset -v opt val \
|
|
is_empty empty_ok number_only zero_allowed
|
|
|
|
# Separate option from value:
|
|
opt="${1%%=*}"
|
|
val="${1#*=}"
|
|
|
|
# Empty values are not allowed unless expected
|
|
# eg: '--batch'
|
|
[ "$opt" = "$val" ] && is_empty=1
|
|
# eg: '--pki-dir='
|
|
[ "$val" ] || is_empty=1
|
|
|
|
case "$opt" in
|
|
--days)
|
|
number_only=1
|
|
# Set the appropriate date variable
|
|
# when called by command later
|
|
alias_days="$val"
|
|
;;
|
|
--fix-offset)
|
|
number_only=1
|
|
export EASYRSA_FIX_OFFSET="$val"
|
|
;;
|
|
--pki-dir)
|
|
export EASYRSA_PKI="$val"
|
|
;;
|
|
--tmp-dir)
|
|
export EASYRSA_TEMP_DIR="$val"
|
|
;;
|
|
--ssl-conf)
|
|
export EASYRSA_SSL_CONF="$val"
|
|
;;
|
|
--keep-tmp)
|
|
export EASYRSA_KEEP_TEMP="$val"
|
|
;;
|
|
--use-algo)
|
|
export EASYRSA_ALGO="$val"
|
|
;;
|
|
--keysize)
|
|
number_only=1
|
|
export EASYRSA_KEY_SIZE="$val"
|
|
;;
|
|
--curve)
|
|
export EASYRSA_CURVE="$val"
|
|
;;
|
|
--dn-mode)
|
|
export EASYRSA_DN="$val"
|
|
;;
|
|
--req-cn)
|
|
export EASYRSA_REQ_CN="$val"
|
|
;;
|
|
--digest)
|
|
export EASYRSA_DIGEST="$val"
|
|
;;
|
|
--req-c)
|
|
empty_ok=1
|
|
export EASYRSA_REQ_COUNTRY="$val"
|
|
;;
|
|
--req-st)
|
|
empty_ok=1
|
|
export EASYRSA_REQ_PROVINCE="$val"
|
|
;;
|
|
--req-city)
|
|
empty_ok=1
|
|
export EASYRSA_REQ_CITY="$val"
|
|
;;
|
|
--req-org)
|
|
empty_ok=1
|
|
export EASYRSA_REQ_ORG="$val"
|
|
;;
|
|
--req-email)
|
|
empty_ok=1
|
|
export EASYRSA_REQ_EMAIL="$val"
|
|
;;
|
|
--req-ou)
|
|
empty_ok=1
|
|
export EASYRSA_REQ_OU="$val"
|
|
;;
|
|
--req-serial)
|
|
empty_ok=1
|
|
export EASYRSA_REQ_SERIAL="$val"
|
|
;;
|
|
--ns-cert)
|
|
empty_ok=1
|
|
[ "$is_empty" ] && unset -v val
|
|
export EASYRSA_NS_SUPPORT="${val:-yes}"
|
|
;;
|
|
--ns-comment)
|
|
empty_ok=1
|
|
export EASYRSA_NS_COMMENT="$val"
|
|
;;
|
|
--batch)
|
|
empty_ok=1
|
|
export EASYRSA_BATCH=1
|
|
;;
|
|
-s|--silent)
|
|
empty_ok=1
|
|
export EASYRSA_SILENT=1
|
|
;;
|
|
--sbatch|--silent-batch)
|
|
empty_ok=1
|
|
export EASYRSA_SILENT=1
|
|
export EASYRSA_BATCH=1
|
|
;;
|
|
--no-safe-ssl)
|
|
empty_ok=1
|
|
export EASYRSA_NO_SAFE_SSL=1
|
|
;;
|
|
--nopass|--no-pass)
|
|
empty_ok=1
|
|
export EASYRSA_NO_PASS=1
|
|
;;
|
|
--passin)
|
|
export EASYRSA_PASSIN="$val"
|
|
;;
|
|
--passout)
|
|
export EASYRSA_PASSOUT="$val"
|
|
;;
|
|
--notext|--no-text)
|
|
empty_ok=1
|
|
export EASYRSA_NO_TEXT=1
|
|
;;
|
|
--subca-len)
|
|
number_only=1
|
|
zero_allowed=1
|
|
export EASYRSA_SUBCA_LEN="$val"
|
|
;;
|
|
--vars)
|
|
user_vars_true=1
|
|
export EASYRSA_VARS_FILE="$val"
|
|
;;
|
|
--copy-ext)
|
|
empty_ok=1
|
|
export EASYRSA_CP_EXT=1
|
|
;;
|
|
--subject-alt-name|--san)
|
|
user_san_true=1
|
|
export EASYRSA_EXTRA_EXTS="\
|
|
$EASYRSA_EXTRA_EXTS
|
|
subjectAltName = $val"
|
|
;;
|
|
--version)
|
|
shift "$#"
|
|
set -- "$@" "version"
|
|
break
|
|
;;
|
|
*)
|
|
break
|
|
esac
|
|
|
|
# fatal error when no value was provided
|
|
if [ "$is_empty" ]; then
|
|
[ "$empty_ok" ] || \
|
|
die "Missing value to option: $opt"
|
|
fi
|
|
|
|
# fatal error when a number is expected but not provided
|
|
if [ "$number_only" ]; then
|
|
case "$val" in
|
|
(0)
|
|
# Allow zero only
|
|
[ "$zero_allowed" ] || \
|
|
die "$opt - Number expected: '$val'"
|
|
;;
|
|
(*[!1234567890]*|0*)
|
|
die "$opt - Number expected: '$val'"
|
|
esac
|
|
fi
|
|
|
|
shift
|
|
done
|
|
|
|
# Set cmd now
|
|
# vars_setup needs to know if this is init-pki
|
|
cmd="$1"
|
|
[ "$1" ] && shift # scrape off command
|
|
|
|
# This avoids unnecessary warnings and notices
|
|
case "$cmd" in
|
|
init-pki|clean-all|""|help|-h|--help|--usage|version)
|
|
no_pki_required=1
|
|
unset -v pki_is_required
|
|
;;
|
|
*)
|
|
pki_is_required=1
|
|
unset -v no_pki_required
|
|
esac
|
|
|
|
# Intelligent env-var detection and auto-loading:
|
|
vars_setup
|
|
|
|
# Mutual exclusions:
|
|
# --nopass cannot be used with --passout
|
|
if [ "$EASYRSA_PASSOUT" ]; then
|
|
# --passout MUST take priority over --nopass
|
|
unset -v EASYRSA_NO_PASS
|
|
prohibit_no_pass=1
|
|
fi
|
|
|
|
# Hand off to the function responsible
|
|
case "$cmd" in
|
|
init-pki|clean-all)
|
|
init_pki "$@"
|
|
;;
|
|
build-ca)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CA_EXPIRE="$alias_days"
|
|
build_ca "$@"
|
|
;;
|
|
gen-dh)
|
|
gen_dh
|
|
;;
|
|
gen-req)
|
|
gen_req "$@"
|
|
;;
|
|
sign|sign-req)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CERT_EXPIRE="$alias_days"
|
|
sign_req "$@"
|
|
;;
|
|
build-client-full)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CERT_EXPIRE="$alias_days"
|
|
build_full client "$@"
|
|
;;
|
|
build-server-full)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CERT_EXPIRE="$alias_days"
|
|
build_full server "$@"
|
|
;;
|
|
build-serverClient-full)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CERT_EXPIRE="$alias_days"
|
|
build_full serverClient "$@"
|
|
;;
|
|
gen-crl)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CRL_DAYS="$alias_days"
|
|
gen_crl
|
|
;;
|
|
revoke)
|
|
revoke "$@"
|
|
;;
|
|
revoke-renewed)
|
|
revoke_renewed "$@"
|
|
;;
|
|
renew)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CERT_EXPIRE="$alias_days"
|
|
renew "$@"
|
|
;;
|
|
rewind-renew)
|
|
rewind_renew "$@"
|
|
;;
|
|
rebuild)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CERT_EXPIRE="$alias_days"
|
|
rebuild "$@"
|
|
;;
|
|
import-req)
|
|
import_req "$@"
|
|
;;
|
|
export-p12)
|
|
export_pkcs p12 "$@"
|
|
;;
|
|
export-p7)
|
|
export_pkcs p7 "$@"
|
|
;;
|
|
export-p8)
|
|
export_pkcs p8 "$@"
|
|
;;
|
|
export-p1)
|
|
export_pkcs p1 "$@"
|
|
;;
|
|
set-rsa-pass)
|
|
set_pass_legacy rsa "$@"
|
|
;;
|
|
set-ec-pass)
|
|
set_pass_legacy ec "$@"
|
|
;;
|
|
set-pass|set-ed-pass)
|
|
set_pass "$@"
|
|
;;
|
|
update-db)
|
|
update_db
|
|
;;
|
|
show-req)
|
|
show req "$@"
|
|
;;
|
|
show-cert)
|
|
show cert "$@"
|
|
;;
|
|
show-crl)
|
|
show crl crl
|
|
;;
|
|
show-ca)
|
|
show_ca "$@"
|
|
;;
|
|
verify|verify-cert)
|
|
verify_cert "$@"
|
|
;;
|
|
show-expire)
|
|
[ -z "$alias_days" ] || \
|
|
export EASYRSA_CERT_RENEW="$alias_days"
|
|
status expire "$@"
|
|
;;
|
|
show-revoke)
|
|
status revoke "$@"
|
|
;;
|
|
show-renew)
|
|
status renew "$@"
|
|
;;
|
|
show-host)
|
|
show_host "$@"
|
|
;;
|
|
make-safe-ssl)
|
|
make_safe_ssl "$@"
|
|
;;
|
|
upgrade)
|
|
up23_manage_upgrade_23 "$@"
|
|
;;
|
|
""|help|-h|--help|--usage)
|
|
cmd_help "$1"
|
|
;;
|
|
version)
|
|
print_version
|
|
;;
|
|
*)
|
|
die "\
|
|
Unknown command '$cmd'. Run without commands for usage help."
|
|
esac
|
|
|
|
# Check for untrapped errors
|
|
# shellcheck disable=SC2181
|
|
[ $? = 0 ] || warn "Untrapped error detected!"
|
|
|
|
# Do 'cleanup ok' on successful completion
|
|
cleanup ok
|
|
|
|
# vim: ft=sh nu ai sw=8 ts=8 noet
|