#!/bin/sh # Easy-RSA 3 -- A Shell-based CA Utility # # Copyright (C) 2024 - 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: information " Easy-RSA 3 usage and overview $easyrsa_help_title To get detailed usage and help for a command, use: ./easyrsa help COMMAND For a list of global-options, use: ./easyrsa help options For a list of utility commands, use: ./easyrsa help util A list of commands is shown below: init-pki [ cmd-opts ] self-sign-server [ cmd-opts ] self-sign-client [ cmd-opts ] build-ca [ cmd-opts ] gen-dh gen-req [ cmd-opts ] sign-req [ cmd-opts ] build-client-full [ cmd-opts ] build-server-full [ cmd-opts ] build-serverClient-full [ cmd-opts ] inline renew revoke [ cmd-opts ] expire revoke-expired [ cmd-opts ] revoke-renewed [ cmd-opts ] gen-crl update-db show-req [ cmd-opts ] show-cert [ cmd-opts ] show-ca [ cmd-opts ] show-crl verify-cert import-req export-p1 [ cmd-opts ] export-p7 [ cmd-opts ] export-p8 [ cmd-opts ] export-p12 [ cmd-opts ] set-pass [ cmd-opts ] gen-tls-auth-key / gen-tls-crypt-key write [ cmd-opts ]" # collect/show dir status: text_only=1 work_dir="${EASYRSA:-undefined}" pki_dir="${EASYRSA_PKI:-undefined}" # check for vars changing PKI unexpectedly! if [ "$invalid_vars" ]; then ivmsg=" *WARNING*: \ Invalid vars setting for EASYRSA and/or EASYRSA_PKI${NL}" else unset -v ivmsg fi # Print details information " DIRECTORY STATUS (commands would take effect on these locations) EASYRSA: $work_dir PKI: $pki_dir vars-file: ${EASYRSA_VARS_FILE:-Missing or undefined}${ivmsg}" # CA Status if verify_ca_init test; then if [ -z "$EASYRSA_SILENT" ]; then # Show SSL output directly, with easyrsa header printf '%s' " CA status: OK${NL}${NL} " "$EASYRSA_OPENSSL" x509 -in "$EASYRSA_PKI/ca.crt" \ -noout -subject -nameopt utf8,multiline print "" # for a clean line fi else information " CA status: CA has not been built${NL}" fi # verbose info verbose "ssl-cnf: ${EASYRSA_SSL_CONF:-built-in}" verbose "x509-types: ${EASYRSA_EXT_DIR:-built-in}" if [ -d "$EASYRSA_TEMP_DIR" ]; then verbose "temp-dir: Found: $EASYRSA_TEMP_DIR" else verbose "temp-dir: Missing: ${EASYRSA_TEMP_DIR:-undefined}" fi } # => 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() { easyrsa_help_title="\ Usage: easyrsa [ OPTIONS.. ] [ cmd-opts.. ]" 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 ENTIRE PKI directory (default). * soft - Keep the named PKI directory and PKI 'vars' file intact. Also keep the current Request files, to be signed by a new CA (Partial CA renewal)." ;; self-sign*) text=" * self-sign-server|self-sign-client [ cmd-opts ] Creates a new self-signed server|client key pair" opts=" * nopass - Do not encrypt the private key (Default: encrypted) (Equivalent to global option '--nopass|--no-pass')" ;; build-ca) text=" * build-ca [ cmd-opts ] Creates a new CA" opts=" * raw-ca - ONLY use SSL binary to input CA password raw (Equivalent to global option '--raw-ca') * nopass - Do not encrypt the private key (Default: encrypted) (Equivalent to global option '--nopass|--no-pass') * subca - Create an intermediate CA keypair and request intca (default is a root CA)" ;; gen-dh) text=" * gen-dh Generates DH (Diffie-Hellman) parameters file" ;; gen-req) text=" * gen-req [ cmd-opts ] Generate a standalone-private-key and certificate-signing-request This request is suitable for sending to a remote CA for signing." opts=" * nopass - Do not encrypt the private key (Default: encrypted) (Equivalent to global option '--nopass|--no-pass') * text - Include certificate text in request" ;; sign|sign-req) text=" * sign-req [ cmd-opts ] Sign a certificate request of the defined type. must be a known type. eg: 'client', 'server', 'serverClient', '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' for importing from other sources." opts=" * newsubj - Replace subject. See 'help subject'. * preserve - Use the DN-field order of the CSR not the CA." ;; build|build-client-full|build-server-full|build-serverClient-full) text=" * build-client-full [ cmd-opts ] * build-server-full [ cmd-opts ] * build-serverClient-full [ cmd-opts ] Generate a keypair and sign locally. This mode uses the as the X509 commonName." opts=" * nopass - Do not encrypt the private key (Default: encrypted) (Equivalent to global option '--nopass|--no-pass')" ;; inline) text=" * inline Create inline file for ." ;; revoke*) text=" * revoke [ reason ] * revoke-expired [ reason ] * revoke-renewed [ reason ] Revoke a certificate specified by the , with an optional revocation [ reason ]. Commands 'revoke-expired' and 'revoke-renewed' are functionally equivalent to 'revoke', however, they are used to revoke certificates which have been either 'expired' or 'renewed' by EasyRSA commands." opts=" * [ reason ]${NL} Values accepted for option [ reason ]:${NL} us | uns* | unspecified kc | key* | keyCompromise cc | ca* | CACompromise ac | aff* | affiliationChanged ss | sup* | superseded co | ces* | cessationOfOperation ch | cer* | certificateHold" ;; expire) text=" * expire Move a certificate specified by to the 'pki/expired' directory. Allows an existing request to be signed again." ;; renew) text=" * renew Renew a certificate specified by " ;; 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." ;; show-req|show-cert) text=" * show-req [ cmd-opts ] * show-cert [ cmd-opts ] Shows details of the req or cert referenced by 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 CA info, including pubkey/sig data" ;; show-crl) text=" * show-crl Shows details of the current certificate revocation list (CRL) Human-readable output is shown." ;; verify|verify-cert) text=" * verify-cert [ 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 Import a certificate request from a file This will copy the specified file into the reqs/ dir in preparation for signing. The is the to create. Example usage: import-req /some/where/bob_request.req bob" ;; export-p12) text=" * export-p12 [ cmd-opts ] Export a PKCS#12 file with the keypair, specified by " opts=" * nopass - Do not encrypt the private key (Default: 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 * nofn - Do not set 'friendlyName' For more, see: 'easyrsa help friendly' * legacy - Use legacy algorithm: RC2_CBC or 3DES_CBC + MAC: SHA1 (Default algorithm: AES-256-CBC + MAC: SHA256)" ;; friendly) text_only=1 text=" * export-p12: Internal file label 'friendlyName' The 'friendlyname' is always set to the file-name-base. An alternate friendlyName can be configured by using: * Global option '--usefn=' Fallback to previous behavior can be configured by using: * Command option 'nofn' ('friendlyname' will not be set)" ;; export-p7) text=" * export-p7 [ cmd-opts ] Export a PKCS#7 file with the pubkey, specified by " opts=" * noca - Do not include the ca.crt file in the PKCS7 output" ;; export-p8) text=" * export-p8 [ cmd-opts ] Export a PKCS#8 file with the private key, specified by " opts=" * nopass - Do not encrypt the private key (Default: encrypted) (Equivalent to global option '--nopass|--no-pass')" ;; export-p1) text=" * export-p1 [ cmd-opts ] Export a PKCS#1 (RSA format) file with the pubkey, specified by " opts=" * nopass - Do not encrypt the private key (Default: encrypted) (Equivalent to global option '--nopass|--no-pass')" ;; set-pass|set-ed-pass|set-rsa-pass|set-ec-pass) text=" * set-pass [ cmd-opts ] Set a new passphrase for the private key specified by DEPRECATED: 'set-rsa-pass' and 'set-ec-pass'" opts=" * nopass - Do not encrypt the private key (Default: encrypted) (Equivalent to global option '--nopass|--no-pass') * file - (Advanced) Treat the file as a raw path, not a short-name" ;; write) text=" * write [] ['overwrite'] Write data to stdout or Types: * ssl-cnf - Write EasyRSA SSL config file. * safe-cnf - Write expanded EasyRSA SSL config file for LibreSSL. * COMMON|ca|server|serverClient|client|codeSigning|email|kdc - Write x509-type file. * legacy - Write ALL support files (above) to the PKI directory. Will create '\$EASYRSA_PKI/x509-types' directory. * legacy-hard - Same as 'legacy' plus OVER-WRITE files. * vars - Write vars.example file." opts=" * filename - If is specified then it is the output is directed to the named file. Otherwise, the data is sent to stdout * overwrite - Overwrite the . is always preserved without 'overwrite'." ;; --san|--subject-alt-name|altname|subjectaltname|san) text_only=1 text=" * Global 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: --san can be specified more than once on the command line. The following two command line examples are equivalent: 1. --san=DNS:server1,DNS:serverA,IP:10.0.0.1 2. --san=DNS:server1 --san=DNS:serverA --san=IP:10.0.0.1 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" ;; --copy-ext|copy-ext|copyext) text_only=1 text=" * Global Option: How to use --copy-ext and --san= These are the only commands that support --copy-ext and/or --san. Command 'gen-req': --san: Add SAN extension to the request file. Command 'sign-req': --copy-ext: Copy all request extensions to the signed certificate. --san: Over write the request SAN with option SAN. Command 'build-*-full': --copy-ext: Always enabled. --san: Add SAN extension to the request and signed certificate. See 'help san' for option --san full syntax." ;; --days|days) text_only=1 text=" * Global 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 revocation list. eg: '--days=180 gen-crl' * Cutoff days for command: show-expire. eg: '--days=90 show-expire'" ;; --new-subj*|new-subj*|newsubj*|subject) text_only=1 text=" * Global Option: --new-subject= This global option is used to set the new certificate subject, when signing a new certificate * REQUIRES Command option: 'newsubj', for command 'sign-req' Using command 'sign-req', add command option 'newsubj', to FORCE the --new-subject to be used. Example: --new-subject='/CN=foo' sign-req client bar newsubj See OpenSSL command 'ca', option -subj, for full details." ;; tool*|util*|more) # Test features text_only=1 text=" NOTE: These commands are safe to test and will NOT effect your PKI. Check number is unique: serial|check-serial Display DN of request or certificate:
= req|x509 display-dn Display EKU of certificate: show-eku | Generate random hex: rand These commands require easyrsa-tools.lib to be installed: show-expire (Optional) show-revoke (Optional) show-renew (Optional)" ;; gen-tls*) text_only=1 text=" Generate TLS keys for use with OpenVPN: gen-tls-auth-key : Generate OpenVPN TLS-AUTH key gen-tls-crypt-key : Generate OpenVPN TLS-CRYPT-V1 key (Preferred) Only ONE TLS key is allowed to exist. (pki/private/easyrsa-tls.key) This TLS key will be automatically added to inline files." ;; opts|options) opt_usage cleanup ok ;; "") usage cleanup ok ;; *) err_text=" Unknown command: '$1' \ (try without commands for a list of commands)" easyrsa_exit_with_error=1 esac if [ "$err_text" ]; then print "$easyrsa_help_title" print "${err_text}" else # display the help text print "$easyrsa_help_title" [ "$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 print } # => cmd_help() # Options usage opt_usage() { text_only=1 information " Easy-RSA Global Option Flags The following global-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 --silent-ssl|-S : Silence SSL output (Requires batch mode) --nopass|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) --raw-ca : Build CA with password via RAW SSL input --vars=FILE : Define a specific 'vars' file to use for Easy-RSA config (Default vars file is in the current working directory) --pki=DIR : Declare the PKI directory (Default PKI directory is sub-directory 'pki') See Advanced.md for in depth usage. --ssl-conf=FILE : Define a specific OpenSSL config file for Easy-RSA to use (Default config file is in the EasyRSA PKI directory) --force-safe-ssl: Always generate a safe SSL config file (Default: Generate Safe SSL config once per instance) --tools=FILE : Declare the full easyrsa-tools.lib file-name --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) --notext|no-text: Create certificates without human readable text --days=# : Sets the signing validity to the specified number of days Applies to other commands. For details, see: 'help days' --startdate=DATE: Sets the SSL option '-startdate' (Format 'YYYYMMDDhhmmssZ') --enddate=DATE : Sets the SSL option '-enddate' (Format 'YYYYMMDDhhmmssZ') --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: algo ec: secp384r1, algo ed: ed25519) --subca-len=# : Path length of signed intermediate CA certificates --copy-ext : Copy included request X509 extensions (namely subjAltName) For more info, see: 'easyrsa help copyext' --san|--subject-alt-name=SUBJECT_ALT_NAME : Add a subjectAltName. Can be used multiple times. For more info and syntax, see: 'easyrsa help altname' --auto-san : Use commonName as subjectAltName: 'DNS:commonName' If commonName is 'n.n.n.n' then set 'IP:commonName' --san-crit : Mark X509v3 subjectAltName as critical --bc-crit : Add X509 'basicContraints = critical' attribute. --ku-crit : Add X509 'keyUsage = critical' attribute. --eku-crit : Add X509 'extendedKeyUsage = critical' attribute. --new-subject='SUBJECT' : Specify a new subject field to sign a request with. For more info and syntax, see: 'easyrsa help subject' --usefn=NAME : export-p12, set 'friendlyName' to NAME For more, see: 'easyrsa help friendly' Distinguished Name mode: --dn-mode=MODE : Distinguished Name mode to use 'cn_only' (Default) or 'org' --req-cn=NAME : Request commonName 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 # printf format - print() print() { printf '%s\n' "$*" } # => print() # Exit fatally with a message to stderr # present even with EASYRSA_BATCH as these are fatal problems die() { print " Easy-RSA error: $*${NL}" # error_info is for hard-to-spot errors! if [ "$error_info" ]; then print " * $cmd: ${error_info}${NL}" fi # show host info show_host # exit to cleanup() exit "${2:-1}" } # => die() # User errors, less noise than die() user_error() { print " EasyRSA version $EASYRSA_version Error ----- $*${NL}" easyrsa_exit_with_error=1 cleanup } # => user_error() # verbose information verbose() { [ "$EASYRSA_VERBOSE" ] || return 0 print " # $*" } # => verbose() # non-fatal warning output warn() { [ "$EASYRSA_SILENT" ] && return print " WARNING ======= $*${NL}" } # => warn() # informational notices to stdout notice() { [ "$EASYRSA_SILENT" ] && return print " Notice ------ $*${NL}" } # => notice() # Helpful information information() { [ "$EASYRSA_SILENT" ] && return print "$*" } # => 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 - confirm() read input printf '\n' [ "$input" = "$value" ] && return easyrsa_exit_with_error=1 unset -v EASYRSA_SILENT notice "Aborting without confirmation." cleanup } # => confirm() # Generate random hex easyrsa_random() { case "$1" in *[!1234567890]*|0*|"") die "easyrsa_random - input" esac if rand_hex="$( "$EASYRSA_OPENSSL" rand -hex "$1" 2>/dev/null )" then if [ "$2" ]; then force_set_var "$2" "$rand_hex" else print "$rand_hex" fi unset -v rand_hex return 0 fi die "easyrsa_random failed" } # => easyrsa_random() # Create session directory atomically or fail secure_session() { # Session must not be defined [ -z "$secured_session" ] || die "session overload" # Temporary directory must exist [ -d "$EASYRSA_TEMP_DIR" ] || die "\ secure_session - Missing temporary directory: * $EASYRSA_TEMP_DIR" for i in 1 2 3; do session= easyrsa_random 4 session secured_session="${EASYRSA_TEMP_DIR}/${session}" # atomic: # ONLY effects Windows 11 "broken" mkdir.exe # The procedure now is a "poor man's" version # of an atomic directory creation call. # The "race condition" still exists but is minimized. # What remains is equivalent to 32bit hash collision. [ -d "$secured_session" ] && continue if mkdir "$secured_session"; then # Check mkdir.exe has created the directory [ -d "$secured_session" ] || \ die "secure_session - mkdir FAILED" [ -f "$secured_session"/temp.0.1 ] && \ die "secure_session - temp-file EXISTS" # New session requires safe-ssl conf unset -v session OPENSSL_CONF \ EASYRSA_SSL_CONF safe_ssl_cnf_tmp \ working_safe_ssl_conf working_safe_org_conf easyrsa_err_log="$secured_session/error.log" verbose "\ secure_session: CREATED: $secured_session" return fi done die "secure_session failed" } # => secure_session() # Remove secure session remove_secure_session() { [ -d "$secured_session" ] || return 0 if rm -rf "$secured_session"; then verbose "\ remove_secure_session: DELETED: $secured_session" unset -v secured_session OPENSSL_CONF \ EASYRSA_SSL_CONF safe_ssl_cnf_tmp \ working_safe_ssl_conf working_safe_org_conf return fi die "remove_secure_session Failed: $secured_session" } # => remove_secure_session() # 'mkdir' wrapper, broken by win11, which fails without error easyrsa_mkdir() { [ "$2" ] && die "easyrsa_mkdir - excess input" [ "$1" ] || die "easyrsa_mkdir - input" [ -d "$1" ] && return mkdir "$1" 2>/dev/null [ -d "$1" ] && return die "easyrsa_mkdir - FAIL: $1" } # => easyrsa_mkdir() # Create temp-file atomically or fail # WARNING: Running easyrsa_openssl in a subshell # will hide error message and verbose messages # from easyrsa_mktemp() easyrsa_mktemp() { if [ -z "$1" ] || [ "$2" ]; then die "easyrsa_mktemp - input error" fi # session directory must exist [ -d "$secured_session" ] || die "\ easyrsa_mktemp - Temporary session undefined (--tmp-dir)" # Force noclobber if [ "$easyrsa_host_os" = win ]; then set -o noclobber else set -C fi # Assign internal temp-file name tmp_fname="${secured_session}/temp.${mktemp_counter}" # Create shotfile for shot_try in x y z; do shotfile="${tmp_fname}.${shot_try}" if [ -f "$shotfile" ]; then verbose "\ easyrsa_mktemp: shotfile EXISTS: $shotfile" continue else printf "" > "$shotfile" || die "\ easyrsa_mktemp: create shotfile failed (1) $1" # Create temp-file or die # subshells do not update mktemp_counter, # which is why this extension is required. # Current max required is 1 attempt for ext_try in 1 2 3 4 5 6 7 8 9; do want_tmp_file="${tmp_fname}.${ext_try}" # Warn to error log file for max reached if [ "$EASYRSA_MAX_TEMP" -lt "$ext_try" ]; then print "\ Max temp-file limit $ext_try, hit for: $1" > "$easyrsa_err_log" die "EASYRSA_MAX_TEMP exceeded" fi if [ -f "$want_tmp_file" ]; then verbose "\ easyrsa_mktemp: temp-file EXISTS: $want_tmp_file" continue else # atomic: if mv "$shotfile" "$want_tmp_file"; then # Assign external temp-file name if force_set_var "$1" "$want_tmp_file" then verbose "\ : easyrsa_mktemp: $1 OK: $want_tmp_file" # unset noclobber if [ "$easyrsa_host_os" = win ]; then set +o noclobber else set +C fi # Update counter mktemp_counter="$((mktemp_counter+1))" unset -v tmp_fname \ shotfile shot_try \ want_tmp_file ext_try return else die "\ easyrsa_mktemp - force_set_var $1 failed" fi fi fi done fi done # unset noclobber if [ "$easyrsa_host_os" = win ]; then set +o noclobber else set +C fi # In case of subshell abuse, report to error log err_msg="\ easyrsa_mktemp - failed for: $1 @ attempt=$ext_try want_tmp_file: $want_tmp_file" print "$err_msg" > "$easyrsa_err_log" die "$err_msg" } # => easyrsa_mktemp() # remove temp files and do terminal cleanups cleanup() { # In case of subshell abuse, display error log file if [ -f "$easyrsa_err_log" ]; then print; cat "$easyrsa_err_log"; print fi # Remove redundant file: index.txt.attr if [ -f "$EASYRSA_PKI"/index.txt.attr ]; then rm -f "$EASYRSA_PKI"/index.txt.attr verbose "cleanup: DELETED $EASYRSA_PKI/index.txt.attr" fi # undo changes BEFORE delete temp-dir # 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 # Remove temp-session or create temp-snapshot if [ -d "$secured_session" ]; then if [ "$EASYRSA_KEEP_TEMP" ]; then # skip on black-listed directory names, with a warning # Use '-e' for directory or file name 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" easyrsa_mkdir "$EASYRSA_TEMP_DIR"/tmp easyrsa_mkdir "$keep_tmp" rm -rf "$keep_tmp" mv -f "$secured_session" "$keep_tmp" information "Temp session preserved: $keep_tmp" unset -v secured_session fi fi # remove temp-session remove_secure_session verbose "mktemp_counter: $mktemp_counter uses" fi # When prompt is disabled then restore prompt case "$prompt_restore" in 0) : ;; # Not required 1) [ -t 1 ] && stty echo [ "$EASYRSA_SILENT" ] || print ;; 2) # shellcheck disable=SC3040 # POSIX set -o set -o echo [ "$EASYRSA_SILENT" ] || print ;; *) warn "Unknown prompt_restore: '$prompt_restore'" esac # Clear traps trap - 0 1 2 3 6 15 # Exit: Known errors # -> confirm(): aborted # -> verify_cert(): verify failed --batch mode # -> check_serial_unique(): not unique --batch mode # -> user_error(): User errors but not die() if [ "$easyrsa_exit_with_error" ]; then verbose "Exit: Known errors = true" exit 1 elif [ "$1" = 2 ]; then verbose "exit SIGINT = true" kill -2 "$$" # Exit: SIGINT elif [ "$1" = ok ]; then verbose "Exit: Final Success = true" exit 0 # Exit: Final Success fi # if 'cleanup' is called without 'ok' then an error occurred verbose "Exit: Final Fail = true" exit 1 # Exit: Final Fail, unknown error } # => cleanup() # Escape hazardous characters # 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() { if [ "$EASYRSA_FORCE_SAFE_SSL" ]; then # Always run verbose "escape_hazard: FORCED" elif [ "$working_safe_org_conf" ]; then # Has run once verbose "escape_hazard: BYPASSED" return else # Run once verbose "escape_hazard: RUN-ONCE" working_safe_org_conf=1 # Set run once fi # Assign temp-file escape_hazard_tmp="" easyrsa_mktemp escape_hazard_tmp || die \ "escape_hazard - easyrsa_mktemp escape_hazard_tmp" # 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\" export EASYRSA_REQ_SERIAL=\"$EASYRSA_REQ_SERIAL\"\ " | sed -e s\`'\&'\`'\\\&'\`g \ -e s\`'\$'\`'\\\$'\`g > "$escape_hazard_tmp" || \ die "escape_hazard - Failed to write temp-file" # Reload fields from fully escaped temp-file # shellcheck disable=1090 # Non-constant source . "$escape_hazard_tmp" verbose "escape_hazard: COMPLETED" } # => escape_hazard() # Replace environment variable names with current value # and write to temp-file or return error from sed expand_ssl_config() { if [ "$EASYRSA_FORCE_SAFE_SSL" ]; then # Always run verbose "expand_ssl_config: FORCED" elif [ "$working_safe_ssl_conf" ]; then # Has run once verbose "expand_ssl_config: BYPASSED" return elif [ "$ssl_lib" = libressl ]; then # LibreSSL Always run verbose "expand_ssl_config: REQUIRED" elif [ "$ssl_lib" = openssl ]; then # OpenSSL not required verbose "expand_ssl_config: IGNORED" return else die "expand_ssl_config: EXCEPTION" # do NOT Run fi # Set run once working_safe_ssl_conf=1 verbose "expand_ssl_config: RUN-ONCE" # Assign temp-file safe_ssl_cnf_tmp="" easyrsa_mktemp safe_ssl_cnf_tmp || die \ "expand_ssl_config - easyrsa_mktemp safe_ssl_cnf_tmp" # Rewrite # shellcheck disable=SC2016 # No expand '' if 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" > "$safe_ssl_cnf_tmp" then verbose "expand_ssl_config: via 'sed' COMPLETED" else return 1 fi export EASYRSA_SSL_CONF="$safe_ssl_cnf_tmp" verbose \ "expand_ssl_config: EASYRSA_SSL_CONF = $EASYRSA_SSL_CONF" } # => expand_ssl_config() # Easy-RSA meta-wrapper for SSL # WARNING: Running easyrsa_openssl in a subshell # will hide error message and verbose messages easyrsa_openssl() { openssl_command="$1"; shift if [ "$EASYRSA_DEBUG" ]; then verbose "= easyrsa_openssl - BEGIN $openssl_command $*" else verbose "= easyrsa_openssl - BEGIN $openssl_command" fi # Do not allow 'rand' here, see easyrsa_random() case "$openssl_command" in rand) die "easyrsa_openssl: Illegal SSL command: rand" esac # Use $EASYRSA_SSL_CONF (local) or $OPENSSL_CONF (global) if [ -f "$EASYRSA_SSL_CONF" ]; then export OPENSSL_CONF="$EASYRSA_SSL_CONF" else [ -f "$OPENSSL_CONF" ] || \ die "easyrsa_openssl - OPENSSL_CONF undefined" fi verbose "= easyrsa_openssl: OPENSSL_CONF = $OPENSSL_CONF" # Exec SSL if [ "$EASYRSA_SILENT_SSL" ] && [ "$EASYRSA_BATCH" ] then if "$EASYRSA_OPENSSL" "$openssl_command" "$@" \ 2>/dev/null then verbose "= easyrsa_openssl - END $openssl_command" return fi else if "$EASYRSA_OPENSSL" "$openssl_command" "$@" then verbose "= easyrsa_openssl - END $openssl_command" return fi fi # Always fail here die "\ easyrsa_openssl - Command has failed: * $EASYRSA_OPENSSL $openssl_command $*" } # => easyrsa_openssl() # Verify the SSL library is functional # and establish version dependencies verify_ssl_lib() { # Run once only [ "$verify_ssl_lib_ok" ] && return verify_ssl_lib_ok=1 unset -v openssl_v3 # redirect std-err, ignore missing ssl/openssl.cnf val="$( "$EASYRSA_OPENSSL" version 2>/dev/null )" ssl_version="$val" # SSL lib name case "${val%% *}" in OpenSSL) ssl_lib=openssl # Honor EASYRSA_FORCE_SAFE_SSL if [ "$EASYRSA_FORCE_SAFE_SSL" ]; then ssl_cnf_type=safe-cnf else ssl_cnf_type=ssl-cnf fi ;; LibreSSL) ssl_lib=libressl ssl_cnf_type=safe-cnf ;; *) error_msg="$("$EASYRSA_OPENSSL" version 2>&1)" user_error "\ * OpenSSL must either exist in your PATH or be defined in your vars file. 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|4) case "$ssl_lib" in openssl) openssl_v3=1 no_password='-noenc' ;; libressl) no_password='-nodes' ;; *) die "Unexpected SSL library: $ssl_lib" esac ;; *) die "Unexpected SSL version: $osslv_major" esac # Message verbose "verify_ssl_lib(): $ssl_version ($EASYRSA_OPENSSL)" } # => 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" ] || user_error "\ 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" ] || user_error "\ 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() { verify_ca_help_note="\ Run easyrsa 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 serial; do if [ ! -f "$EASYRSA_PKI/$i" ] && \ [ ! -p "$EASYRSA_PKI/$i" ] then # Used by usage() and export-p12/p7 [ "$1" = "test" ] && return 1 user_error "\ Missing expected CA file: $i (perhaps you need to run build-ca?) $verify_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" ] || user_error "\ Missing expected CA dir: $i (perhaps you need to run build-ca?) $verify_ca_help_note" done } # => verify_ca_init() # init-pki backend: init_pki() { # Process command options reset="hard" while [ "$1" ]; do case "$1" in hard-reset|hard) reset="hard" confirm_msg= ;; soft-reset|soft) reset="soft" confirm_msg='PARTIALLY ' ;; *) warn "Ignoring unknown command option: '$1'" esac shift done # EasyRSA will NOT do 'rm -rf /' case "$EASYRSA_PKI" in .|..|./|../|.//*|..//*|/|//*|\\|?:|'') user_error "Invalid PKI: $EASYRSA_PKI" esac # If EASYRSA_PKI exists, confirm before deletion if [ -d "$EASYRSA_PKI" ]; then confirm "Confirm removal: " "yes" " WARNING!!! You are about to ${confirm_msg}remove the EASYRSA_PKI at: * $EASYRSA_PKI and initialize a fresh PKI here." # now remove it: case "$reset" in hard) # Promote use of 'init-pki soft': confirm " WARNING: COMPLETELY DESTROY current PKI (NOT recommended) ? [yes/NO]: " yes "\ ****************************************** * SECOND WARNING - STOP - SECOND WARNING * ****************************************** To keep your current 'pki/vars' settings use 'init-pki soft'. To keep your current Request files use 'init-pki soft' The Requests can then be signed by a new CA (Partial CA renewal) To keep your current Easy-RSA TLS Key use 'init-pki soft' This private key file is in use by your current VPN. ** USE OF 'init-pki soft' IS RECOMMENDED **${NL}" # # # shellcheck disable=SC2115 # Use "${var:?}" rm -rf "$EASYRSA_PKI" || \ die "init-pki hard reset failed." ;; soft) # There is no unit test for a soft reset # Save existing TLS key tls_key_file="$EASYRSA_PKI"/private/easyrsa-tls.key old_tls_key_file="$EASYRSA_PKI"/easyrsa-keepsafe-tls.key # If both keys exist then they must be the same if [ -f "$old_tls_key_file" ]; then if [ -f "$tls_key_file" ]; then # Match by hash tls_key_hash="$( "$EASYRSA_OPENSSL" dgst -sha256 \ "$tls_key_file")" tls_key_hash="${tls_key_hash##* }" old_tls_key_hash="$( "$EASYRSA_OPENSSL" dgst -sha256 \ "$old_tls_key_file")" old_tls_key_hash="${old_tls_key_hash##* }" [ "$tls_key_hash" = "$old_tls_key_hash" ] || \ user_error "\ Easy-RSA TLS Keys do not match, only ONE of these files is valid: * $tls_key_file * $old_tls_key_file Please delete the key above that is no longer in use." fi fi # Save existing TLS key if [ -f "$tls_key_file" ]; then tls_key_data="$(cat "$tls_key_file")" else tls_key_data= fi # Do NOT remove pki/reqs sub-dir, for "renew ca" for i in ca.crt crl.pem \ issued private inline revoked renewed expired \ serial serial.old index.txt index.txt.old \ index.txt.attr index.txt.attr.old certs_by_serial do # # # shellcheck disable=SC2115 # Use "${var:?}" target="$EASYRSA_PKI/$i" if [ "${target%/*}" ]; then rm -rf "$target" || \ die "init-pki soft reset(1) failed!" else die "init-pki soft reset(2) failed!" fi done ;; *) user_error "Unknown reset type: $reset" esac fi # new dirs: easyrsa_mkdir "$EASYRSA_PKI" for i in issued private reqs; do easyrsa_mkdir "${EASYRSA_PKI}/$i" done # If one existed then recreate old TLS key backup file if [ "$tls_key_data" ]; then header="# Easy-RSA TLS Key: $(date)${NL}# DO NOT DELETE" printf '%s\n\n%s\n' "$header" "$tls_key_data" \ > "$old_tls_key_file" tls_msg="${NL} Previous Easy-RSA TLS key saved to: * $old_tls_key_file${NL}" else # if an OLD TLS key still exists then notify user if [ -f "$old_tls_key_file" ]; then tls_msg="${NL} Existing Easy-RSA TLS key preserved: * $old_tls_key_file${NL}" else tls_msg= fi fi # write pki/vars.example - no temp-file because no session write_legacy_file_v2 \ vars "$EASYRSA_PKI"/vars.example overwrite || \ warn "init-pki - Failed to create vars.example" # User notice notice "\ 'init-pki' complete; you may now create a CA or requests. Your newly created PKI dir is: * $EASYRSA_PKI" # Select and show vars file unset -v EASYRSA_VARS_FILE select_vars information "\ Using Easy-RSA configuration: * ${EASYRSA_VARS_FILE:-undefined}${tls_msg}" } # => init_pki() # Find support files from various sources # Declare in preferred order, first wins # beaten by command line. # If these files are not found here then they # will be built on-demand by the selected command. locate_support_files() { # Set required sources ssl_cnf_file='openssl-easyrsa.cnf' x509_types_dir='x509-types' easyrsa_tools='easyrsa-tools.lib' # Find data-files for area in \ "$EASYRSA_PKI" \ "$EASYRSA" \ "$PWD" \ "${0%/*}" \ '/usr/local/share/easy-rsa' \ '/usr/share/easy-rsa' \ '/etc/easy-rsa' \ # EOL do # Find x509-types if [ -d "${area}/${x509_types_dir}" ]; then set_var EASYRSA_EXT_DIR "${area}/${x509_types_dir}" fi # Find openssl-easyrsa.cnf if [ -f "${area}/${ssl_cnf_file}" ]; then set_var EASYRSA_SSL_CONF "${area}/${ssl_cnf_file}" fi # Find easyrsa-tools.lib if [ -f "${area}/${easyrsa_tools}" ]; then set_var EASYRSA_TOOLS_LIB "${area}/${easyrsa_tools}" fi done verbose ": EASYRSA_EXT_DIR: ${EASYRSA_EXT_DIR:-built-in}" verbose ": EASYRSA_SSL_CONF: ${EASYRSA_SSL_CONF:-built-in}" verbose ": EASYRSA_TOOLS_LIB: ${EASYRSA_TOOLS_LIB:-undefined}" verbose "locate_support_files: COMPLETED" } # => locate_support_files() # 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 # 3061 - In POSIX sh, read without a variable is undefined. # shellcheck disable=SC3040,SC3045,SC3061 if stty -echo 2>/dev/null; then prompt_restore=1 read -r "$@" stty echo elif (set +o echo 2>/dev/null); then prompt_restore=2 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 prompt_restore=0 } # => hide_read_pass() # Get passphrase get_passphrase() { t="$1"; shift || die "password malfunction" while :; do r="" printf '\n%s' "$*" hide_read_pass r if [ "${#r}" -lt 4 ]; then printf '\n%s\n' \ "Passphrase must be at least 4 characters!" else # shellcheck disable=SC2229 # does not read 't' read -r "$t" <<- SECRET $r SECRET unset -v r t print return 0 fi done } # => get_passphrase() # build-ca backend: build_ca() { cipher="-aes256" unset -v sub_ca date_stamp x509 error_info \ ca_password_via_cmdline while [ "$1" ]; do case "$1" in intca|subca) sub_ca=1 ;; nopass) [ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1 ;; raw*) EASYRSA_RAW_CA=1 ;; *) user_error "Unknown command option: '$1'" esac shift done out_key="$EASYRSA_PKI/private/ca.key" # setup for an intermediate CA if [ "$sub_ca" ]; then # Generate a CSR out_file="$EASYRSA_PKI/reqs/ca.req" else # Generate a certificate out_file="$EASYRSA_PKI/ca.crt" date_stamp=1 x509=1 fi # RAW mode must take priority if [ "$EASYRSA_RAW_CA" ]; then unset -v EASYRSA_NO_PASS EASYRSA_PASSOUT EASYRSA_PASSIN verbose "build-ca: CA password RAW method" else # If encrypted then create the CA key with AES256 cipher if [ "$EASYRSA_NO_PASS" ]; then unset -v cipher else unset -v no_password fi fi # Test for existing CA, and complain if already present if verify_ca_init test; then user_error "\ 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 user_error "\ 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 # create necessary dirs: err_msg="\ Unable to create necessary PKI files (permissions?)" easyrsa_mkdir "${EASYRSA_PKI}"/certs_by_serial easyrsa_mkdir "${EASYRSA_PKI}"/revoked easyrsa_mkdir "${EASYRSA_PKI}"/revoked/certs_by_serial easyrsa_mkdir "${EASYRSA_PKI}"/revoked/private_by_serial # create necessary files: printf "" > \ "$EASYRSA_PKI/index.txt" || die "$err_msg" printf '%s\n' "01" \ > "$EASYRSA_PKI/serial" || die "$err_msg" unset -v err_msg # If one exists then recreate TLS Key tls_key_file="$EASYRSA_PKI"/private/easyrsa-tls.key old_tls_key_file="$EASYRSA_PKI"/easyrsa-keepsafe-tls.key if [ -f "$old_tls_key_file" ]; then confirm "Re-install existing Easy-RSA TLS Key ? " yes " An Easy-RSA TLS Key, saved by 'init-pki soft', has been found. This TLS Key is in use by your VPN, it is recommended that you re-install this TLS Key. Note: This is a private key and will NOT be added to new inline files. To create a new Easy-RSA TLS Key, delete this old TLS Key above." cp "$old_tls_key_file" "$tls_key_file" || \ warn "Failed to install TLS Key!" tls_key_msg="${NL} NOTICE: The previous Easy-RSA TLS Key has been installed: * $tls_key_file This TLS Key will NOT be added to new inline files. These new inline files can then be easily distributed to your servers and clients. The TLS Key that your servers and clients have previously received, can be added to the inline file manually. To re-enable automatically adding this TLS Key to inline files, simply delete the backup TLS Key at: * $old_tls_key_file To create a new Easy-RSA TLS Key, delete both TLS Keys above." else tls_key_msg="${NL} Create an OpenVPN TLS-AUTH|TLS-CRYPT-V1 key now: See 'help gen-tls'" fi # Set ssl batch mode, as required [ "$EASYRSA_BATCH" ] && ssl_batch=1 # 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 # create local SSL cnf write_easyrsa_ssl_cnf_tmp # Assign cert and key temp files out_key_tmp="" easyrsa_mktemp out_key_tmp || \ die "build_ca - easyrsa_mktemp out_key_tmp" out_file_tmp="" easyrsa_mktemp out_file_tmp || \ die "build_ca - easyrsa_mktemp out_file_tmp" # Get passphrase from user if necessary if [ "$EASYRSA_RAW_CA" ] then # Passphrase will be provided confirm " Accept ? " yes "\ Raw CA mode =========== CA password must be input THREE times: 1. Set the password. 2. Confirm the password. 3. Use the password. (Create the Root CA)" elif [ "$EASYRSA_NO_PASS" ] then : # No passphrase required elif [ "$EASYRSA_PASSOUT" ] && [ "$EASYRSA_PASSIN" ] then # passphrase defined on command line # Both --passout and --passin # must be defined for a CA with a password ca_password_via_cmdline=1 else # Assign passphrase vars # Heed shellcheck SC2154 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 # CA password via temp-files in_key_pass_tmp="" easyrsa_mktemp in_key_pass_tmp || \ die "build_ca - in_key_pass_tmp" out_key_pass_tmp="" easyrsa_mktemp out_key_pass_tmp || \ die "build_ca - out_key_pass_tmp" 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 unset -v p q user_error "Passphrases do not match!" fi fi # Find or create x509 CA file if [ -f "$EASYRSA_EXT_DIR/ca" ]; then # Use the x509-types/ca file x509_type_file="$EASYRSA_EXT_DIR/ca" else # Use a temp file write_x509_type_tmp ca x509_type_file="$write_x509_file_tmp" fi # keyUsage critical if [ "$EASYRSA_KU_CRIT" ]; then crit_tmp= easyrsa_mktemp crit_tmp || \ die "build-ca - easyrsa_mktemp KU crit_tmp" add_critical_attrib keyUsage "$x509_type_file" \ "$crit_tmp" || die "build-ca - KU add_critical_attrib" # Use the new tmp-file with critical attribute x509_type_file="$crit_tmp" verbose "build_ca: keyUsage critical OK" fi # basicConstraints critical if [ "$EASYRSA_BC_CRIT" ]; then crit_tmp= easyrsa_mktemp crit_tmp || \ die "build-ca - easyrsa_mktemp BC crit_tmp" add_critical_attrib basicConstraints "$x509_type_file" \ "$crit_tmp" || die "build-ca - BC add_critical_attrib" # Use the new tmp-file with critical attribute x509_type_file="$crit_tmp" verbose "build_ca: basicConstraints critical OK" fi # Find or create x509 COMMON file if [ -f "$EASYRSA_EXT_DIR/COMMON" ]; then # Use the x509-types/COMMON file x509_COMMON_file="$EASYRSA_EXT_DIR/COMMON" else # Use a temp file write_x509_type_tmp COMMON x509_COMMON_file="$write_x509_file_tmp" fi # Check for insert-marker in ssl config file if ! grep -q '^#%CA_X509_TYPES_EXTRA_EXTS%' \ "$EASYRSA_SSL_CONF" then die "\ This openssl config file does not support X509-type 'ca'. * $EASYRSA_SSL_CONF Please update 'openssl-easyrsa.cnf' to the latest Easy-RSA release." fi # Assign awkscript to insert EASYRSA_EXTRA_EXTS # shellcheck disable=SC2016 # No expand '' - build_ca() awkscript='\ {if ( match($0, "^#%CA_X509_TYPES_EXTRA_EXTS%") ) { while ( getline<"/dev/stdin" ) {print} next } {print} }' # Assign tmp-file for config adjusted_ssl_cnf_tmp="" easyrsa_mktemp adjusted_ssl_cnf_tmp || \ die "build_ca - easyrsa_mktemp adjusted_ssl_cnf_tmp" # Insert x509-types COMMON and 'ca' and EASYRSA_EXTRA_EXTS { # X509 files cat "$x509_type_file" "$x509_COMMON_file" # User extensions [ "$EASYRSA_EXTRA_EXTS" ] && \ print "$EASYRSA_EXTRA_EXTS" } | awk "$awkscript" "$EASYRSA_SSL_CONF" \ > "$adjusted_ssl_cnf_tmp" || \ die "Copying X509_TYPES to config file failed" verbose "build-ca: insert x509 and extensions OK" # Use this new SSL config for the rest of this function EASYRSA_SSL_CONF="$adjusted_ssl_cnf_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 # verbose notice if [ "$EASYRSA_RAW_CA" ]; then verbose "\ build_ca: CA key password created via RAW" else if [ "$ca_password_via_cmdline" ]; then verbose "\ build_ca: CA key password created via command options" else if [ "$EASYRSA_NO_PASS" ]; then verbose "\ build_ca: CA key has no password" else verbose "\ build_ca: CA key password created via temp-files" fi fi fi # Generate the CA keypair: easyrsa_openssl req -utf8 -new \ -key "$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 keypair" # Move temp-files to target-files mv "$out_key_tmp" "$out_key" || mv_temp_error=1 mv "$out_file_tmp" "$out_file" || mv_temp_error=1 if [ "$mv_temp_error" ]; then rm -f "$out_key" "$out_file" die "Failed to move new CA files." fi # 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. Prior to signing operations, place your resulting Sub-CA cert at: * $EASYRSA_PKI/ca.crt" else notice "\ CA creation complete. Your new CA certificate is at: * $out_file${tls_key_msg} Build-ca completed successfully." fi } # => build_ca() # Build self signed key pair self_sign() { # Define x509 type case "$1" in server) selfsign_eku=serverAuth crt_type=self-signed-server ;; client) selfsign_eku=clientAuth crt_type=self-signed-client ;; *) die "self_sign: Unknown EKU '$1'" esac shift # pull $file_name_base [ "$1" ] || user_error "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and command help." file_name_base="$1" shift # Prohibit --req-cn [ "$EASYRSA_REQ_CN" = ChangeMe ] || user_error "\ Option conflict --req-cn: * '$cmd' does not support setting an external commonName" # Enforce commonName export EASYRSA_REQ_CN="$file_name_base" # create local SSL cnf write_easyrsa_ssl_cnf_tmp # Refuse option as name case "$file_name_base" in nopass) user_error "Refusing '$file_name_base' as name." esac # function opts support while [ "$1" ]; do case "$1" in nopass) [ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1 ;; *) user_error "Unknown command option: '$1'" esac shift done # Assign output files key_out="$EASYRSA_PKI/private/${file_name_base}.key" crt_out="$EASYRSA_PKI/issued/${file_name_base}.crt" inline_out="$EASYRSA_PKI/inline/${file_name_base}.inline" # key file must NOT exist [ -f "$key_out" ] && user_error "\ Cannot self-sign this request for '$file_name_base'. Conflicting key exists at: * $key_out" # Certificate file must NOT exist [ -f "$crt_out" ] && user_error "\ Cannot self-sign this request for '$file_name_base'. Conflicting certificate exists at: * $crt_out" # Check algo and curve case "$EASYRSA_ALGO" in rsa|ec) # Silently use ec instead of rsa export EASYRSA_ALGO=ec # Selectively set --curve=secp384r1 set_var EASYRSA_CURVE secp384r1 # temp-file for params-file selfsign_params_file="" easyrsa_mktemp selfsign_params_file || \ die "self_sign - easyrsa_mktemp selfsign_params_file" # params-file "$EASYRSA_OPENSSL" ecparam \ -name "$EASYRSA_CURVE" \ -out "$selfsign_params_file" || \ die "self_sign - params-file failed" newkey_params="$EASYRSA_ALGO":"$selfsign_params_file" ;; ed) # Selectively set --curve=ed25519 set_var EASYRSA_CURVE ed25519 newkey_params="$EASYRSA_CURVE" ;; *) user_error "Unrecognised algorithm: '$EASYRSA_ALGO'" esac verbose "\ self-sign: Use ALGO:'$EASYRSA_ALGO' / CURVE:'$EASYRSA_CURVE'" # Assign tmp-file for config adjusted_ssl_cnf_tmp="" easyrsa_mktemp adjusted_ssl_cnf_tmp || \ die "self_sign - easyrsa_mktemp adjusted_ssl_cnf_tmp" # Assign awkscript to insert EASYRSA_EXTRA_EXTS # shellcheck disable=SC2016 # No expand '' - build_ca() awkscript='\ {if ( match($0, "^#%CA_X509_TYPES_EXTRA_EXTS%") ) { while ( getline<"/dev/stdin" ) {print} next } {print} }' # Find or create x509 selfsign file if [ -f "$EASYRSA_EXT_DIR/selfsign" ]; then # Use the x509-types/selfsign file x509_selfsign_file="$EASYRSA_EXT_DIR/selfsign" else # Use a temp file write_x509_type_tmp selfsign x509_selfsign_file="$write_x509_file_tmp" fi # Find or create x509 COMMON file if [ -f "$EASYRSA_EXT_DIR/COMMON" ]; then # Use the x509-types/COMMON file x509_COMMON_file="$EASYRSA_EXT_DIR/COMMON" else # Use a temp file write_x509_type_tmp COMMON x509_COMMON_file="$write_x509_file_tmp" fi # Insert x509-types COMMON and 'selfsign' and EASYRSA_EXTRA_EXTS { # X509 files cat "$x509_selfsign_file" "$x509_COMMON_file" # User extensions [ "$EASYRSA_EXTRA_EXTS" ] && \ print "$EASYRSA_EXTRA_EXTS" } | awk "$awkscript" "$EASYRSA_SSL_CONF" \ > "$adjusted_ssl_cnf_tmp" || \ die "Copying X509_TYPES to config file failed" verbose "self_sign: insert x509 and extensions OK" # Use this new SSL config for the rest of this function EASYRSA_SSL_CONF="$adjusted_ssl_cnf_tmp" # Create temp-files for output tmp_key_out="" easyrsa_mktemp tmp_key_out || \ die "self_sign - easyrsa_mktemp tmp_key_out" tmp_crt_out="" easyrsa_mktemp tmp_crt_out || \ die "self_sign - easyrsa_mktemp tmp_crt_out" # create self-signed key pair easyrsa_openssl req -x509 -utf8 -sha256 -text \ -newkey "$newkey_params" \ -keyout "$tmp_key_out" \ -out "$tmp_crt_out" \ -subj "/CN=$file_name_base" \ ${EASYRSA_NO_PASS:+ "$no_password"} \ ${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \ ${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} \ ${EASYRSA_CERT_EXPIRE:+ -days "$EASYRSA_CERT_EXPIRE"} \ ${EASYRSA_START_DATE:+ -startdate "$EASYRSA_START_DATE"} \ ${EASYRSA_END_DATE:+ -enddate "$EASYRSA_END_DATE"} # Move temp-files to target-files mv "$tmp_key_out" "$key_out" || mv_temp_error=1 mv "$tmp_crt_out" "$crt_out" || mv_temp_error=1 if [ "$mv_temp_error" ]; then rm -f "$key_out" "$crt_out" die "Failed to move new key/cert files." fi # inline key/cert/fingerprint inline_file "$file_name_base" # User info notice "\ Self-signed '$EASYRSA_ALGO/$EASYRSA_CURVE' \ key and certificate created: * $key_out * $crt_out" } # => self_sign() # gen-dh backend: gen_dh() { out_file="$EASYRSA_PKI/dh.pem" # check to see if we already have a dh parameters file if [ -f "$out_file" ]; then if [ "$EASYRSA_BATCH" ]; then # if batch is enabled, die user_error "\ 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 tmp_dh_file || \ die "gen_dh - easyrsa_mktemp tmp_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" # Move temp-files to target-files mv "$tmp_dh_file" "$out_file" || mv_temp_error=1 if [ "$mv_temp_error" ]; then rm -f "$out_file" die "Failed to move temp DH file." fi notice " DH parameters of size $EASYRSA_KEY_SIZE created at: * $out_file" } # => gen_dh() # gen-req and key backend: gen_req() { # pull filename, use as default interactive CommonName [ "$1" ] || user_error "\ Error: gen-req must have a file-name-base as the first argument. Run easyrsa without commands for usage and commands." file_name_base="$1" shift # scrape off file-name-base # Set ssl batch mode as required [ "$EASYRSA_BATCH" ] && ssl_batch=1 # Set commonName if [ "$EASYRSA_REQ_CN" = ChangeMe ]; then export EASYRSA_REQ_CN="$file_name_base" fi # create local SSL cnf write_easyrsa_ssl_cnf_tmp # Output files key_out="$EASYRSA_PKI/private/${file_name_base}.key" req_out="$EASYRSA_PKI/reqs/${file_name_base}.req" # 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 ;; *) user_error "Unknown command option: '$1'" esac shift done # don't wipe out an existing request without confirmation [ -f "$req_out" ] && confirm "Confirm request overwrite: " "yes" "\ WARNING!!! An existing request file was found at * $req_out Continuing with key generation will replace this request." # 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 "\ This openssl config file does \ does not support EASYRSA_EXTRA_EXTS. * $EASYRSA_SSL_CONF Please update 'openssl-easyrsa.cnf' \ to the latest Easy-RSA release." fi # Setup & insert the extra ext data keyed by magic line extra_exts=" req_extensions = req_extra [ req_extra ] $EASYRSA_EXTRA_EXTS" # shellcheck disable=SC2016 # No expand '' - gen_req() awkscript=' {if ( match($0, "^#%EXTRA_EXTS%") ) { while ( getline<"/dev/stdin" ) {print} next } {print} }' # Assign temp-file for config adjusted_ssl_cnf_tmp="" easyrsa_mktemp adjusted_ssl_cnf_tmp || \ die "gen_req - easyrsa_mktemp adjusted_ssl_cnf_tmp" # Insert $extra_exts @ %EXTRA_EXTS% in SSL Config print "$extra_exts" | \ awk "$awkscript" "$EASYRSA_SSL_CONF" \ > "$adjusted_ssl_cnf_tmp" || \ die "Writing SSL config to temp file failed" [ "${EASYRSA_SAN_CRIT}" ] && \ verbose "gen-req: SAN critical OK" # Use this SSL config for the rest of this function EASYRSA_SSL_CONF="$adjusted_ssl_cnf_tmp" fi # Name temp files key_out_tmp="" easyrsa_mktemp key_out_tmp || \ die "gen_req - easyrsa_mktemp key_out_tmp" req_out_tmp="" easyrsa_mktemp req_out_tmp || \ die "gen_req - easyrsa_mktemp req_out_tmp" # Set algorithm options algo_opts="" case "$EASYRSA_ALGO" in rsa|ec) # Set elliptic curve parameters-file # or RSA bit-length algo_opts="$EASYRSA_ALGO:$EASYRSA_ALGO_PARAMS" ;; ed) # Set Edwards curve name algo_opts="$EASYRSA_CURVE" ;; *) die "gen_req - Unknown algorithm: $EASYRSA_ALGO" esac # Generate request if 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"} then : # ok else die "Failed to generate request" fi # Move temp-files to target-files mv "$key_out_tmp" "$key_out" || mv_temp_error=1 mv "$req_out_tmp" "$req_out" || mv_temp_error=1 if [ "$mv_temp_error" ]; then rm -f "$key_out" "$req_out" die "Failed to move temp key/req file." fi # Success messages notice "\ Private-Key and Public-Certificate-Request files created. Your files are: * req: $req_out * key: $key_out${do_build_full:+ $NL}" } # => gen_req() # common signing backend sign_req() { crt_type="$1" file_name_base="$2" # Verify $crt_type is valid case "$crt_type" in ca|server|serverClient|client|codeSigning|email|kdc) : # All known types plus CA for sub-ca ;; *) warn "\ Unrecognised x509-type: '$crt_type' In order to sign a custom X509 Type certificate, there must be a corresponding SSL configuration file in the 'x509-type' folder." esac # Check argument sanity: [ "$file_name_base" ] || user_error "\ Incorrect number of arguments provided to sign-req: expected 2, got $# (see command help for usage)" req_in="$EASYRSA_PKI/reqs/$file_name_base.req" crt_out="$EASYRSA_PKI/issued/$file_name_base.crt" shift 2 # create local SSL cnf write_easyrsa_ssl_cnf_tmp # Check optional subject force_subj= while [ "$1" ]; do case "$1" in nopass) warn "Ignoring option '$1'" ;; newsubj*) # verify force_subj opts are used correctly [ "$EASYRSA_NEW_SUBJECT" ] || user_error "\ To force a new certificate subject, global option --new-subject must also be specified." force_subj="$EASYRSA_NEW_SUBJECT" ;; preserve*) export EASYRSA_PRESERVE_DN=1 ;; *) user_error "Unknown option '$1'" esac shift done # verify force_subj opts are used correctly if [ "$EASYRSA_NEW_SUBJECT" ]; then [ "$force_subj" ] || user_error "\ To force a new certificate subject, command option 'newsubj' must also be specified." fi # Cert type must NOT be COMMON [ "$crt_type" = COMMON ] && user_error "\ Invalid certificate type: '$crt_type'" # Request file must exist [ -f "$req_in" ] || user_error "\ No request found for the input: '$file_name_base' Expected to find the request at: * $req_in" # Certificate file must NOT exist [ -f "$crt_out" ] && user_error "\ Cannot sign this request for '$file_name_base'. Conflicting certificate exists at: * $crt_out" # Confirm input is a cert req verify_file req "$req_in" || user_error "\ The certificate request file is not in a valid X509 format: * $req_in" # Randomize Serial number if [ "$EASYRSA_RAND_SN" != no ]; then serial="" check_serial="" unset -v serial_is_unique for i in 1 2 3 4 5; do easyrsa_random 16 serial # Require 128bit serial number [ "$serial" = "${serial#00}" ] || continue # Check for duplicate serial in CA db if check_serial_unique "$serial" batch; then serial_is_unique=1 break fi done # Check for unique_serial [ "$serial_is_unique" ] || die "\ sign_req - Randomize Serial number failed: $check_serial" # Print random $serial to pki/serial file # for use by SSL config print "$serial" > "$EASYRSA_PKI/serial" || \ die "sign_req - write serial to file" unset -v serial check_serial serial_is_unique 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 "\ This openssl config file does \ not support option '--copy-ext'. * $EASYRSA_SSL_CONF Please update 'openssl-easyrsa.cnf' \ to the latest Easy-RSA release." fi # Setup & insert the copy_extensions data # keyed by a magic line copy_exts="copy_extensions = copy" # shellcheck disable=SC2016 # No expand '' - sign_req() awkscript=' {if ( match($0, "^#%COPY_EXTS%") ) { while ( getline<"/dev/stdin" ) {print} next } {print} }' # Assign temp-file for config adjusted_ssl_cnf_tmp="" easyrsa_mktemp adjusted_ssl_cnf_tmp || \ die "sign_req - easyrsa_mktemp adjusted_ssl_cnf_tmp" print "$copy_exts" | \ awk "$awkscript" "$EASYRSA_SSL_CONF" \ > "$adjusted_ssl_cnf_tmp" || die "\ Writing 'copy_exts' to SSL config temp-file failed" # Use this SSL config for the rest of this function EASYRSA_SSL_CONF="$adjusted_ssl_cnf_tmp" verbose "sign_req: Using '$copy_exts'" verbose "sign_req: EASYRSA_SSL_CONF = $EASYRSA_SSL_CONF" fi # Find or create x509-type file if [ -f "$EASYRSA_EXT_DIR/$crt_type" ]; then # Use the x509-types/$crt_type file x509_type_file="$EASYRSA_EXT_DIR/$crt_type" else # Use a temp file write_x509_type_tmp "$crt_type" x509_type_file="$write_x509_file_tmp" fi # keyUsage critical confirm_ku_crit= if [ "$EASYRSA_KU_CRIT" ]; then crit_tmp= easyrsa_mktemp crit_tmp || \ die "sign-req - easyrsa_mktemp KU crit_tmp" add_critical_attrib keyUsage "$x509_type_file" \ "$crit_tmp" || die "sign-req - KU add_critical_attrib" # Use the new tmp-file with critical attribute x509_type_file="$crit_tmp" confirm_ku_crit=" keyUsage: 'critical'${NL}" verbose "sign_req: keyUsage critical OK" fi # basicConstraints critical confirm_bc_crit= if [ "$EASYRSA_BC_CRIT" ]; then crit_tmp= easyrsa_mktemp crit_tmp || \ die "sign-req - easyrsa_mktemp BC crit_tmp" add_critical_attrib basicConstraints "$x509_type_file" \ "$crit_tmp" || die "sign-req - BC add_critical_attrib" # Use the new tmp-file with critical attribute x509_type_file="$crit_tmp" confirm_bc_crit=" basicConstraints: 'critical'${NL}" verbose "sign_req: basicConstraints critical OK" fi # extendedKeyUsage critical confirm_eku_crit= if [ "$EASYRSA_EKU_CRIT" ]; then crit_tmp= easyrsa_mktemp crit_tmp || \ die "sign-req - easyrsa_mktemp EKU crit_tmp" add_critical_attrib extendedKeyUsage "$x509_type_file" \ "$crit_tmp" || die "sign-req - EKU add_critical_attrib" # Use the new tmp-file with critical attribute x509_type_file="$crit_tmp" confirm_eku_crit=" extendedKeyUsage: 'critical'${NL}" verbose "sign_req: extendedKeyUsage critical OK" fi # Find or create x509 COMMON file if [ -f "$EASYRSA_EXT_DIR/COMMON" ]; then # Use the x509-types/COMMON file x509_COMMON_file="$EASYRSA_EXT_DIR/COMMON" else # Use a temp file write_x509_type_tmp COMMON x509_COMMON_file="$write_x509_file_tmp" fi # Support a dynamic CA path length when present: unset -v basicConstraints confirm_bc_len if [ "$crt_type" = "ca" ] && [ "$EASYRSA_SUBCA_LEN" ] then # Print the last occurrence of basicConstraints in # x509-types/ca # If basicConstraints is not defined then bail # shellcheck disable=SC2016 # No expand '' - sign_req() awkscript='\ /^[[:blank:]]*basicConstraints[[:blank:]]*=/ { bC=$0 } END { if (length(bC) == 0 ) exit 1; print bC }' basicConstraints="$( awk "$awkscript" "$x509_type_file" )" || die "\ basicConstraints is not defined, cannot use 'pathlen'" confirm_pathlen=" Path length: '$EASYRSA_SUBCA_LEN'${NL}" verbose "sign_req: Using basicConstraints pathlen" fi # Deprecated Netscape extension support case "$EASYRSA_NS_SUPPORT" in [yY][eE][sS]) confirm "Confirm use of Netscape extensions: " yes \ "WARNING: Netscape extensions are DEPRECATED!" # Netscape extension case "$crt_type" in serverClient) ns_cert_type="nsCertType = serverClient" ;; server) ns_cert_type="nsCertType = server" ;; client) ns_cert_type="nsCertType = client" ;; ca) ns_cert_type="nsCertType = sslCA" ;; *) ns_cert_type="nsCertType = $crt_type" esac verbose "sign_req: Using $ns_cert_type" ;; *) # ok No NS support required unset -v ns_cert_type esac # Get request CN # EASYRSA_REQ_CN MUST always be set to the CSR CN EASYRSA_REQ_CN="$( "$EASYRSA_OPENSSL" req -utf8 -in "$req_in" -noout \ -subject -nameopt multiline | grep 'commonName' )" || warn "sign-req - EASYRSA_REQ_CN FAILED" EASYRSA_REQ_CN="${EASYRSA_REQ_CN##*= }" # Add auto SAN, if EASYRSA_AUTO_SAN is enabled if [ -z "$EASYRSA_SAN" ] && [ "$EASYRSA_AUTO_SAN" ]; then # Choose DNS:san or IP:san if print "$EASYRSA_REQ_CN" | grep -q \ '^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$' then EASYRSA_SAN="IP:${EASYRSA_REQ_CN}" else EASYRSA_SAN="DNS:${EASYRSA_REQ_CN}" fi # Add auto SAN to EASYRSA_EXTRA_EXTS EASYRSA_EXTRA_EXTS="\ $EASYRSA_EXTRA_EXTS subjectAltName = ${EASYRSA_SAN_CRIT}${EASYRSA_SAN}" verbose "sign-req: Auto SAN: ${EASYRSA_SAN}" fi # confirm SAN critical confirm_san_crit= if [ "${EASYRSA_SAN_CRIT}" ]; then confirm_san_crit=" subjectAltName: 'critical'${NL}" verbose "sign-req: SAN critical OK" fi # Generate the extensions file for this cert: ext_tmp="" easyrsa_mktemp ext_tmp || \ die "sign_req - easyrsa_mktemp ext_tmp" # Begin output redirect { # Append $cert-type extensions cat "$x509_COMMON_file" "$x509_type_file" # Support a dynamic CA path length when present: if [ "$basicConstraints" ]; then print "$basicConstraints, pathlen:$EASYRSA_SUBCA_LEN" fi # Deprecated Netscape extension support if [ "$ns_cert_type" ]; then print "$ns_cert_type" print "nsComment = \"$EASYRSA_NS_COMMENT\"" fi # Add user supplied extra extensions # and/or SAN extension if [ "$EASYRSA_EXTRA_EXTS" ]; then print "$EASYRSA_EXTRA_EXTS" fi } > "$ext_tmp" || die "\ Failed to create temp extension file (bad permissions?) at: * $ext_tmp" verbose "sign_req: Generated extensions file OK" # Set confirm CN confirm_CN=" Requested CN: '$EASYRSA_REQ_CN'" # Set confirm type confirm_type=" Requested type: '$crt_type'" # Set confirm valid_period message if [ "$EASYRSA_END_DATE" ]; then confirm_period=" Valid until: '$EASYRSA_END_DATE'" else confirm_period=" Valid for: '$EASYRSA_CERT_EXPIRE' days" fi # Set confirm DN if [ "$force_subj" ]; then confirm_dn="${NL}* Forced Subject: '$force_subj'${NL}" else confirm_dn="${NL}$(display_dn req "$req_in")" || \ die "sign-req: display_dn" fi # Set confirm SAN # SAN from .req if [ "$EASYRSA_CP_EXT" ]; then # capture complete CSR req_text="$( "$EASYRSA_OPENSSL" req -utf8 -in "$req_in" -noout -text )" || die "sign-req: openssl: req_text" # Check CSR for any requested SAN if echo "$req_text" | \ grep -q 'X509v3 Subject Alternative Name' then # extract requested SAN # 'grep -A' may not be strictly POSIX, die on error req_x509_san="$( echo "$req_text" | \ grep -A 1 'X509v3 Subject Alternative Name' )" || die "sign-req: req_x509_san: grep -A 1 (POSIX)" else # No requested SAN req_x509_san= fi fi # Set confirm details confirm_critical_attribs=" ${confirm_bc_crit}${confirm_ku_crit}\ ${confirm_eku_crit}${confirm_san_crit}" confirm_details="\ ${confirm_CN} ${confirm_type}${confirm_pathlen} ${confirm_period} ${confirm_critical_attribs}${confirm_dn}" # --san takes priority over req SAN and --copy-ext if [ "$EASYRSA_SAN" ]; then confirm_san="\ X509v3 Subject Alternative Name: ${EASYRSA_SAN_CRIT}${EASYRSA_SAN}" else confirm_san="$req_x509_san" fi # Set confirm SAN if [ "$EASYRSA_SAN" ] || [ "$req_x509_san" ]; then confirm_details="$confirm_details${NL}${NL}$confirm_san" fi # Display the request subject in an easy-to-read format # Confirm the user wishes to sign this request # The foreign_request confirmation is not required # for build_full: if [ "$local_request" ]; then unset -v foreign_request else foreign_request="\ 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.$NL" fi confirm "Confirm requested details: " "yes" "\ ${foreign_request}You are about to sign the following certificate: $confirm_details" # => confirm end # Assign temp cert file crt_out_tmp="" easyrsa_mktemp crt_out_tmp || \ die "sign_req - easyrsa_mktemp crt_out_tmp" # sign request easyrsa_openssl ca -utf8 -batch \ -in "$req_in" -out "$crt_out_tmp" \ -extfile "$ext_tmp" \ ${EASYRSA_PRESERVE_DN:+ -preserveDN} \ ${force_subj:+ -subj "$force_subj"} \ ${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \ ${EASYRSA_NO_TEXT:+ -notext} \ ${EASYRSA_CERT_EXPIRE:+ -days "$EASYRSA_CERT_EXPIRE"} \ ${EASYRSA_START_DATE:+ -startdate "$EASYRSA_START_DATE"} \ ${EASYRSA_END_DATE:+ -enddate "$EASYRSA_END_DATE"} \ || die "\ Signing failed (openssl output above may have more detail)" verbose "sign_req: signed cert '$file_name_base' OK" # Move temp-files to target-files mv "$crt_out_tmp" "$crt_out" || mv_temp_error=1 if [ "$mv_temp_error" ]; then rm -f "$crt_out" die "Failed to move temp certificate file." fi # inline file inline_file "$file_name_base" # Success messages notice "\ Certificate created at: * $crt_out" } # => sign_req() # Add 'critical' attribute to X509-type file add_critical_attrib() { case "$1" in basicConstraints|keyUsage|extendedKeyUsage) : ;; # ok *) die "add_critical_attrib - usage: '$1'" esac [ -f "$2" ] || die "add_critical_attrib - file-2: '$2'" [ -f "$3" ] || die "add_critical_attrib - file-3: '$3'" sed s/"$1 = "/"$1 = critical,"/g "$2" > "$3" } # => add_critical_attrib() # Check serial in db check_serial_unique() { [ "$1" ] || user_error "Serial number required!" case "$1" in (*[!1234567890abcdef]*) user_error "Invalid serial number: '$1'" esac unset -v unique_serial_true # Check for openssl -status of serial number # Always errors out - Do not capture error # unset EASYRSA_SILENT_SSL to capture all output # Do NOT unset check_serial for sign-req error msg check_serial="$( "$EASYRSA_OPENSSL" ca -status "$1" 2>&1 )" || : # Check for duplicate serial in CA db case "$check_serial" in (*"not present in db"*) unique_serial_true=1 verbose "check_serial_unique: unique_serial=true" ;; *) : # Some other response verbose "check_serial_unique: unique_serial=false" esac # In batch mode return result only if [ "$2" = batch ] || [ "$EASYRSA_BATCH" ]; then if [ "$unique_serial_true" ]; then unset -v unique_serial_true return 0 else unset -v unique_serial_true return 1 fi fi # Otherwise, show result to user # and do not return any error code print " check_serial_status RESULT: ======================================== $check_serial ======================================== COMPLETE" } # => check_serial_unique() # common build backend # used to generate+sign in 1 step build_full() { # pull filename base: [ "$2" ] || user_error "\ 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 ;; *) user_error "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. Conflicting file found at: *" [ -f "$req_out" ] && \ user_error "Request $err_exists $req_out" [ -f "$key_out" ] && \ user_error "Key $err_exists $key_out" [ -f "$crt_out" ] && \ user_error "Certificate $err_exists $crt_out" unset -v err_exists # create request verbose "build_full: BEGIN gen_req" gen_req "$name" batch verbose "build_full: END gen_req" # Set to modify sign-req confirmation message local_request=1 # Recreate temp-session and # drop edits to SSL Conf file remove_secure_session locate_support_files secure_session write_global_safe_ssl_cnf_tmp # Require --copy-ext export EASYRSA_CP_EXT=1 # Sign it verbose "build_full: BEGIN sign_req" error_build_full_cleanup=1 if sign_req "$crt_type" "$name"; then unset -v error_build_full_cleanup do_build_full else die "\ Failed to sign '$name' - \ See error messages above for details." fi verbose "build_full: END sign_req" } # => build_full() # Generate inline file V2 inline_file() { # Allow complete disable [ "$EASYRSA_DISABLE_INLINE" ] && return # definitive source [ "$1" ] || die "inline_file - Missing file_name_base" # make inline dirs easyrsa_mkdir "$EASYRSA_PKI"/inline easyrsa_mkdir "$EASYRSA_PKI"/inline/private # Source files crt_source="${EASYRSA_PKI}/issued/${1}.crt" key_source="${EASYRSA_PKI}/private/${1}.key" ca_source="$EASYRSA_PKI"/ca.crt tls_source="$EASYRSA_PKI"/private/easyrsa-tls.key old_tls_key_file="$EASYRSA_PKI"/easyrsa-keepsafe-tls.key # output inline_out="${EASYRSA_PKI}/inline/${1}.inline" print "\ # Inline files in the 'private' directory contain security keys which # MUST only be transmitted over a secure connection, such as 'scp'." \ > "$EASYRSA_PKI"/inline/private/README.inline.private inline_incomplete= inline_private= # Generate Inline data # Certificate if [ -f "$crt_source" ]; then crt_data="\ $(cat "$crt_source") " # Calculate decimal value for serial number # because openvpn uses decimal serial ?!? # for '--crl-verify /path/to/dir dir' # For reasons unknown.. if which bc >/dev/null; then crt_serial="$( "$EASYRSA_OPENSSL" x509 -in "$crt_source" \ -noout -serial )" || die "inline_file - SSL -serial failed" crt_serial="${crt_serial#*=}" crt_serial_dec="$( echo "ibase=16; $crt_serial" | bc )" || die "inline_file - HEX to DEC failed" else crt_serial_dec="Unavailable" fi # Generate fingerprint crt_fingerprint="$( "$EASYRSA_OPENSSL" x509 -in "$crt_source" \ -noout -sha256 -fingerprint )" || die "inline_file - Failed -fingerprint" # strip prefix crt_fingerprint="${crt_fingerprint#*=}" # Certificate type if [ -z "$crt_type" ]; then ssl_cert_x509v3_eku "$crt_source" crt_type || \ die "inline_file: Failed to set crt_type" fi # commonName crt_CN="$( display_dn x509 "$crt_source" | grep 'commonName' )" || die "inline_file: Failed to set crt_CN" # strip prefix crt_CN="${crt_CN#*= }" else inline_incomplete=1 crt_data="\ # When you recieve your signed certificate place it in the # 'pki/issued' sub-dir of your PKI and use command 'inline' # to rebuild this inline file with your certificate. # # * Paste your user certificate here * # " crt_fingerprint=unknown crt_type=unknown crt_CN=unknown fi # Private key if [ -f "$key_source" ]; then inline_private=1 key_data="\ $(cat "$key_source") " else inline_incomplete=1 key_data="\ # When you recieve your key place it in the # 'pki/private' sub-dir of your PKI and use command 'inline' # to rebuild this inline file with your key. # # * Paste your private key here * # " fi # CA certificate if [ -f "$ca_source" ]; then ca_data="\ $(cat "$ca_source") " else inline_incomplete=1 ca_data="\ # When you recieve your CA certificate place it in the # 'pki' sub-dir of your PKI and use command 'inline' # to rebuild this inline file with your CA certificate. # # * Paste your CA certificate here * # " fi # TLS KEY - Set TLS auth|crypt key inline label if [ -f "$tls_source" ]; then tls_key_data="$(cat "$tls_source")" case "$tls_key_data" in *'TLS-AUTH'*) tls_key_label=tls-auth ;; *'TLS-CRYPT'*) tls_key_label=tls-crypt ;; *) tls_key_label= esac fi # Do NOT add TLS key if OLD TLS key exists # because this PSK has already been shared. if [ -f "$old_tls_key_file" ]; then tls_data="\ # Add the existing TLS AUTH/CRYPT-V1 Key here: # <${tls_key_label}> # * Paste The existing pre-shared TLS key here * # " # Add --key-direction for TLS-AUTH [ "$tls_key_label" = tls-auth ] && \ tls_data="$tls_data # # Add the required 'key-direction 0|1' here: # key-direction 1" unset -v tls_key_data tls_key_label else # Add standard TLS key details if [ -f "$tls_source" ]; then inline_private=1 if [ "$tls_key_label" ]; then tls_data="\ <${tls_key_label}> ${tls_key_data} " else inline_incomplete=1 tls_data="# Easy-RSA TLS Key not recognised!" fi else #inline_incomplete=1 tls_data="# Easy-RSA TLS Key not found!" fi fi # Only support inline TLS keys for OpenVPN server/client use case "$crt_type" in server) key_direction="key-direction 0" ;; client) key_direction="key-direction 1" ;; *) verbose "inline: Unsupported certificate type: $crt_type" tls_key_label= key_direction= tls_data="# No TLS Key support for cert-type: $crt_type" esac # Add --key-direction for TLS-AUTH if [ "$tls_key_label" = tls-auth ]; then tls_data="${tls_data}${NL}${NL}${key_direction}" fi # If inline file has keys then redirect to 'private' dir [ "$inline_private" ] && \ inline_out="${EASYRSA_PKI}/inline/private/${1}.inline" # Print data print "\ # Easy-RSA Inline file # Certificate type: $crt_type # commonName: $crt_CN # SHA256 fingerprint: # $crt_fingerprint # Decimal serial number: $crt_serial_dec $crt_data $key_data $ca_data $tls_data " > "$inline_out" # user info if [ "$inline_incomplete" ]; then warn "\ INCOMPLETE Inline file created: * $inline_out" else notice "\ Inline file created: * $inline_out" fi } # => inline_file() # revoke backend revoke() { # pull filename base: [ "$1" ] || user_error "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and command help." # Assign file_name_base and dust off! file_name_base="$1" shift # create local SSL cnf write_easyrsa_ssl_cnf_tmp in_dir="$EASYRSA_PKI" key_in="$in_dir/private/${file_name_base}.key" req_in="$in_dir/reqs/${file_name_base}.req" inline_pub="$in_dir/inline/${file_name_base}.inline" inline_pri="$in_dir/inline/private/${file_name_base}.inline" # input cert for revocation: issued, expired or renewed crt_in="${in_dir}/${cert_dir}/${file_name_base}.crt" # Assign possible "crl_reason" if [ "$1" ]; then crl_reason="$1" shift case "$crl_reason" in us|uns*) crl_reason=unspecified ;; kc|key*) crl_reason=keyCompromise ;; cc|[Cc][Aa]*) crl_reason=CACompromise ;; ac|aff*) crl_reason=affiliationChanged ;; ss|sup*) crl_reason=superseded ;; co|ces*) crl_reason=cessationOfOperation ;; ch|cer*) crl_reason=certificateHold ;; *) user_error "\ Unexpected reason: '$crl_reason'. See 'help revoke' for valid reasons." esac else unset -v crl_reason fi # Enforce syntax if [ "$1" ]; then user_error "Syntax error: $1" fi # referenced cert must exist: [ -f "$crt_in" ] || user_error "\ Unable to revoke as no certificate was found. Certificate was expected at: * $crt_in" # Verify certificate verify_file x509 "$crt_in" || user_error "\ Unable to revoke as the input-file is not a valid certificate. Certificate was expected at: * $crt_in" # Check for misuse of revoke when revoke-* is intended case "$cert_dir" in issued) # expired cert exp_exist="${in_dir}/expired/${file_name_base}.crt" if [ -f "$exp_exist" ]; then exp_endd="$( "$EASYRSA_OPENSSL" x509 -in "$exp_exist" -noout \ -enddate -serial)" || die "revoke - expire -enddate" # shellcheck disable=SC2295 # Expansions inside ${..} exp_confirm=" Expired certificate: * $exp_exist Expiry: ${exp_endd%%${NL}serial=*} Serial: ${exp_endd##*serial=} Use command 'revoke-expired' to revoke this certificate." else unset -v exp_exist exp_endd exp_confirm fi # renewed cert ren_exist="${in_dir}/renewed/${file_name_base}.crt" if [ -f "$ren_exist" ]; then ren_endd="$( "$EASYRSA_OPENSSL" x509 -in "$ren_exist" -noout \ -enddate -serial)" || die "revoke - renew -enddate" # shellcheck disable=SC2295 # Expansions inside ${..} ren_confirm=" Renewed certificate: * $ren_exist Expiry: ${ren_endd%%${NL}serial=*} Serial: ${ren_endd##*serial=} Use command 'revoke-renewed' to revoke this certificate." else unset -v ren_exist ren_endd ren_confirm fi # issued cert crt_endd="$( "$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout \ -enddate -serial)" || die "revoke - expire -enddate" # Confirm intended use of 'revoke' if [ "$exp_exist" ] || [ "$ren_exist" ]; then warn "The following certificate(s) exist: ${exp_exist:+${exp_confirm}${NL}}${ren_exist:+${ren_confirm}${NL}}" # shellcheck disable=SC2295 # Expansions inside ${..} confirm " Confirm intended use of 'revoke' ? " yes "\ Please confirm your intended use of 'revoke' for the following issued certificate:${NL} * $crt_in Expiry: ${crt_endd%%${NL}serial=*} Serial: ${crt_endd##*serial=}" fi # Revoking an issued cert forces req/key to be moved move_req_and_key=1 ;; expired|renewed/issued) # Revoke-expired/renewed cert means req/key can remain move_req_and_key= ;; *) die "Invalid cert_dir: '$cert_dir'" esac # Verify request if [ -f "$req_in" ]; then verify_file req "$req_in" || user_error "\ Unable to verify request. The file is not a valid request. Request was expected at: * $req_in" fi # get the serial number of the certificate cert_serial= ssl_cert_serial "$crt_in" cert_serial || \ die "$cmd: Failed to get cert serial number!" # 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 must be unique deny_msg="\ Cannot revoke this certificate, a conflicting file exists. *" [ -f "$crt_out" ] && \ user_error "$deny_msg certificate: $crt_out" [ -f "$key_out" ] && \ user_error "$deny_msg private key: $key_out" [ -f "$req_out" ] && \ user_error "$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 if [ "$move_req_and_key" ] && [ -f "$key_in" ]; then if_exist_key_in=" * $key_in" fi if [ "$move_req_and_key" ] && [ -f "$req_in" ]; then if_exist_req_in=" * $req_in" fi # Set confirm DN and serial confirm_dn="$(display_dn x509 "$crt_in")" || \ die "revoke: display_dn" confirm_sn=" serial-number = $cert_serial" # confirm operation by displaying DN: warn "\ This process is destructive! These files will be MOVED to the 'revoked' sub-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: * $inline_pub * $inline_pri" confirm " Continue with revocation: " "yes" " Please confirm that you wish to revoke the certificate with the following subject: $confirm_dn $confirm_sn 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." } # => revoke() # revoke_move # moves revoked certificates to the 'revoked' folder # allows reissuing certificates with the same name revoke_move() { easyrsa_mkdir "$EASYRSA_PKI"/revoked easyrsa_mkdir "$EASYRSA_PKI"/revoked/reqs_by_serial easyrsa_mkdir "$EASYRSA_PKI"/revoked/certs_by_serial easyrsa_mkdir "$EASYRSA_PKI"/revoked/private_by_serial # only move the req when revoking an issued cert # and if we have the req if [ "$move_req_and_key" ] && [ -f "$req_in" ]; then mv "$req_in" "$req_out" || warn "Failed to move: $req_in" fi # move crt to revoked folder mv "$crt_in" "$crt_out" || die "Failed to move: $crt_in" # only move the key when revoking an issued cert # and if we have the key if [ "$move_req_and_key" ] && [ -f "$key_in" ]; then mv "$key_in" "$key_out" || warn "Failed to move: $key_in" fi # remove any pkcs files for pkcs in p12 p7b p8 p1; do if [ -f "$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" fi if [ -f "$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" fi done # remove inline files rm -f "$inline_pub" "$inline_pri" || warn \ "revoke_move - Error trying to remove inline files." } # => revoke_move() # Move expired cert out of pki/issued to pki/expired # to allow renewal expire_cert() { # pull filename base: [ "$1" ] || user_error "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and command help." # Assign file_name_base and dust off! file_name_base="$1" shift # input in_dir="$EASYRSA_PKI/issued" crt_in="$in_dir/$file_name_base.crt" #key_in="$in_dir/private/$file_name_base.key" #req_in="$in_dir/reqs/$file_name_base.req" # output out_dir="$EASYRSA_PKI/expired" crt_out="$out_dir/$file_name_base.crt" # make output folder easyrsa_mkdir "$EASYRSA_PKI"/expired # Do not over write existing cert if [ -f "$crt_out" ]; then user_error "\ Existing file must be revoked: * $crt_out" fi # deprecate ALL options while [ "$1" ]; do case "$1" in nopass) warn "\ Option 'nopass' is not supported by command '$cmd'." ;; *) user_error "Unknown option: $1" esac shift done # Verify certificate if [ -f "$crt_in" ]; then verify_file x509 "$crt_in" || user_error "\ Input file is not a valid certificate: * $crt_in" else user_error "\ Missing certificate file: * $crt_in" fi # get the serial number of the certificate cert_serial= ssl_cert_serial "$crt_in" cert_serial || \ die "$cmd: Failed to get cert serial number!" # Set confirm DN and serial confirm_dn="$(display_dn x509 "$crt_in")" || \ die "expire: display_dn" confirm_sn=" serial-number = $cert_serial" # date of expiry # Equal to: easyrsa-tools.lib - ssl_cert_not_after_date() # This is left as a reminder that easyrsa does not handle # dates well and they should be avoided, at all cost. # This is for confirmation purposes ONLY. crt_expire="$( "$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -enddate )" || die "expire: enddate" confirm_ex=" notAfter date = ${crt_expire#*=}" # confirm confirm " Continue with expiry: " yes " Please confirm you wish to expire the certificate with the following subject: $confirm_dn $confirm_sn $confirm_ex" # => End confirm # move cert to expired dir mv "$crt_in" "$crt_out" || die "failed to move expired: $crt_in" # User message notice "\ Certificate has been successfully moved to the expired directory. * $crt_out This certificate is still valid, until it expires. It can be revoked with command 'revoke-expired'. It is now possible to sign a new certificate for '$file_name_base'" } # => expire_cert() # gen-crl backend gen_crl() { out_file="$EASYRSA_PKI/crl.pem" out_der="$EASYRSA_PKI/crl.der" out_file_tmp="" easyrsa_mktemp out_file_tmp || \ die "gen_crl - easyrsa_mktemp out_file_tmp" if [ -r "$out_file" ]; then cp -p "$out_file" "$out_file_tmp" || \ warn "Failed to preserve CRL file permissions." fi easyrsa_openssl ca -utf8 -gencrl -out "$out_file_tmp" \ ${EASYRSA_CRL_DAYS:+ -crldays "$EASYRSA_CRL_DAYS"} \ ${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} || \ die "CRL Generation failed." # Move temp-files to target-files mv "$out_file_tmp" "$out_file" || mv_temp_error=1 if [ "$mv_temp_error" ]; then #rm -f "$out_file" die "Failed to move temp CRL file." fi # Copy to DER - As published by OpenSSL if "$EASYRSA_OPENSSL" crl -in "$out_file" -out "$out_der" \ -outform DER then crl_der_note="An updated CRL DER copy has been created: * $out_der" else crl_der_note="Failed to create CRL DER copy!" fi notice "\ $crl_der_note An updated CRL has been created: * $out_file" } # => gen_crl() # import-req backend import_req() { # pull passed paths in_req="$1" short_name="$2" out_req="$EASYRSA_PKI/reqs/$2.req" [ "$short_name" ] || user_error "\ Unable to import: incorrect command syntax. Run easyrsa without commands for usage and command help." # Request file must exist [ -f "$in_req" ] || user_error "\ No request found for the input: '$2' Expected to find the request at: * $in_req" verify_file req "$in_req" || user_error "\ The certificate request file is not in a valid X509 format: * $in_req" # destination must not exist [ -f "$out_req" ] && user_error "\ Please choose a different name for your imported request file. Conflicting file already exists at: * $out_req" # now import it cp "$in_req" "$out_req" notice "\ Request successfully imported with short-name: $short_name This request is now ready to be signed." } # => import_req() # export pkcs#12, pkcs#7, pkcs#8 or pkcs#1 export_pkcs() { pkcs_type="$1" shift [ "$1" ] || user_error "\ Unable to export '$pkcs_type': incorrect command syntax. Run easyrsa without commands for usage and command help." file_name_base="$1" shift crt_in="$EASYRSA_PKI/issued/$file_name_base.crt" key_in="$EASYRSA_PKI/private/$file_name_base.key" crt_ca="$EASYRSA_PKI/ca.crt" # Always set a friendly_name set_var EASYRSA_P12_FR_NAME "$file_name_base" friendly_name="$EASYRSA_P12_FR_NAME" # opts support cipher=-aes256 want_ca=1 want_key=1 unset -v nokeys legacy # Under OpenSSL 1.1, use the PBE/MAC algorithms OpenSSL 3.0 uses, # unless "legacy" is set. This makes the .p12 files readable by # OpenSSL 3.0 without needing '-legacy'. if [ "$openssl_v3" ]; then # No cipher opts required p12_cipher_opts="" else # Upgrade PBE & MAC opts - Reset by option 'legacy' p12_cipher_opts="-keypbe AES-256-CBC -certpbe AES-256-CBC" p12_cipher_opts="${p12_cipher_opts} -macalg sha256" fi while [ "$1" ]; do case "$1" in noca) want_ca="" ;; nokey) want_key="" # Undocumented OpenSSL feature: option # -nokeys will ignore missing -inkey file # No doubt, the reason for the extra -inkey nokeys=-nokeys ;; nopass) [ "$prohibit_no_pass" ] || EASYRSA_NO_PASS=1 ;; nofn) friendly_name="" ;; legacy) if [ "$openssl_v3" ]; then legacy=-legacy else # Downgrade PBE & MAC opts p12_cipher_opts="" fi ;; *) user_error "Unknown command option: '$1'" esac shift done # Required options - PKCS, rhymes with mess case "$pkcs_type" in p12|p7) : # ok ;; p8|p1) want_key=1 ;; *) die "Unknown PKCS type: $pkcs_type" esac # Check for CA, if required if [ "$want_ca" ]; then case "$pkcs_type" in p12|p7) # verify_ca_init() here, otherwise not required if verify_ca_init test; then : # ok else warn "\ Missing CA Certificate, expected at: * $crt_ca" confirm " Continue without CA Certificate (EG: option 'noca') ? " yes " Your PKI does not include a CA Certificate. You can export your User Certificate to a $pkcs_type file but the CA Certificate will not be included." # --batch mode does not allow # on-the-fly command changes if [ "$EASYRSA_BATCH" ]; then die "export-$pkcs_type: Missing CA" fi want_ca="" fi ;; p8|p1) : # Not required ;; *) die "Unknown PKCS type: $pkcs_type" esac fi # Check for key, if required if [ "$want_key" ]; then if [ -f "$key_in" ]; then : #ok else case "$pkcs_type" in p12) warn "\ Missing Private Key, expected at: * $key_in" confirm " Continue without Private Key (EG: option 'nokey') ? " yes " Your PKI does not include a Private Key for '$file_name_base'. You can export your User Certificate to a '$pkcs_type' file but the Private Key will not be included." # --batch mode does not allow # on-the-fly command changes if [ "$EASYRSA_BATCH" ]; then die "export-$pkcs_type: Missing key" fi nokeys=-nokeys ;; p8|p1) user_error "\ Missing Private Key, expected at: * $key_in" ;; p7) : # Not required ;; *) die "Unknown PKCS type: $pkcs_type" esac fi fi # Check for certificate, if required if [ -f "$crt_in" ]; then : # ok else case "$pkcs_type" in p12|p7) user_error "\ Missing User Certificate, expected at: * $crt_in" ;; p8|p1) : # Not required ;; *) die "Unknown PKCS type: $pkcs_type" esac fi # For 'nopass' PKCS requires an explicit empty password if [ "$EASYRSA_NO_PASS" ]; then EASYRSA_PASSIN=pass: EASYRSA_PASSOUT=pass: unset -v cipher # pkcs#1 only fi # Complete export inline_out= inline_msg= case "$pkcs_type" in p12) pkcs_out="$EASYRSA_PKI/private/$file_name_base.p12" inline_out="$EASYRSA_PKI/inline/$file_name_base-p12.inline" [ "$legacy" ] && \ error_info="SSL library may not support -legacy mode" # export the p12: # shellcheck disable=2086 # Double quote p12_cipher_opts easyrsa_openssl pkcs12 -export \ -in "$crt_in" \ -out "$pkcs_out" \ -inkey "$key_in" \ ${nokeys} \ ${legacy} \ ${p12_cipher_opts} \ ${friendly_name:+ -name "$friendly_name"} \ ${want_ca:+ -certfile "$crt_ca"} \ ${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \ ${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} \ || die "Failed to export PKCS#12" # Inline .p12 only # Get cert CN inline_CN="$( "$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -subject \ -nameopt multiline,-esc_msb | grep 'commonName' )" || die "export_pkcs - inline_CN FAILED" inline_CN="${inline_CN##*= }" # BASE64 encode pkcs12 inline_tmp= easyrsa_mktemp inline_tmp || die "export_pkcs - inline_tmp" if "$EASYRSA_OPENSSL" enc -a -in "$pkcs_out" > "$inline_tmp" then # make inline file { print "\ # Easy-RSA inline file: pkcs12 # commonName: ${inline_CN}${NL}" print "" cat "$inline_tmp" print "" } > "$inline_out" || die "export_pkcs - make inline" inline_msg="\ A BASE64 encoded inline file has also been created at: * ${inline_out}${NL}" else inline_msg="\ Failed to create a BASE64 encoded inline file${NL}" fi ;; p7) pkcs_out="$EASYRSA_PKI/issued/$file_name_base.p7b" # export the p7: easyrsa_openssl crl2pkcs7 -nocrl \ -certfile "$crt_in" \ -out "$pkcs_out" \ ${want_ca:+ -certfile "$crt_ca"} \ || die "Failed to export PKCS#7" ;; p8) pkcs_out="$EASYRSA_PKI/private/$file_name_base.p8" # export the p8: easyrsa_openssl pkcs8 -topk8 \ -in "$key_in" \ -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/$file_name_base.p1" # OpenSSLv3 requires -traditional for PKCS#1 # Otherwise, OpenSSLv3 outputs PKCS#8 [ "$verify_ssl_lib_ok" ] || \ die "export_pkcs.p1: verify_ssl_lib_ok FAIL" if [ "$openssl_v3" ]; then traditional=-traditional else unset -v traditional fi # export the p1: easyrsa_openssl rsa \ -in "$key_in" \ -out "$pkcs_out" \ ${traditional} \ ${cipher} \ ${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \ ${EASYRSA_PASSOUT:+ -passout "$EASYRSA_PASSOUT"} \ || die "Failed to export PKCS#1" ;; *) die "Unknown PKCS type: $pkcs_type" esac # User messages notice "\ Successful export of $pkcs_type file. Your exported file is at: * $pkcs_out" [ "$inline_msg" ] && print "$inline_msg" return 0 } # => export_pkcs() # set-pass backend set_pass() { # values supplied by the user: raw_file="$1" file="$EASYRSA_PKI/private/$raw_file.key" if [ "$raw_file" ]; then shift else user_error "\ 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" ;; *) user_error "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 [ -f "$file" ] || user_error "\ Missing private key: expected to find the private key file at: * $file" notice "\ If the key is encrypted then you must supply the current password. ${cipher:+You will then enter a new password for this key.$NL}" # Set password out_key_tmp="" easyrsa_mktemp out_key_tmp || \ die "set_pass - easyrsa_mktemp out_key_tmp" 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." # Move old key-file out of the way mv "$file" "${file}.tmp" || \ die "Failed to move the old-key file." # Move new key-file into place if mv "$out_key_tmp" "$file"; then rm -f "${file}.tmp" else mv -f "${file}.tmp" "$file" die "Failed to update the private key file." fi key_update=changed [ "$EASYRSA_NO_PASS" ] && key_update=removed notice "Key passphrase successfully $key_update" } # => set_pass() # update-db backend update_db() { easyrsa_openssl ca -utf8 -updatedb \ ${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} || \ die "Failed to perform update-db." } # => update_db() # display cert DN info on a req/X509, passed by full pathname display_dn() { [ "$#" = 2 ] || die "\ display_dn - input error" format="$1" path="$2" shift 2 # Display DN "$EASYRSA_OPENSSL" "$format" -in "$path" -noout -subject \ -nameopt utf8,sep_multiline,space_eq,lname,align } # => display_dn() # 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" ] || user_error "\ Missing expected 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) in_file="$EASYRSA_PKI/issued/$name.crt" format="x509" ;; req) in_file="$EASYRSA_PKI/reqs/$name.req" format="req" ;; crl) 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 [ -f "$in_file" ] || user_error "\ No such '$type' type file with a of '$name'. Expected to find this file at: * $in_file" verify_file "$format" "$in_file" || user_error "\ This file is not a valid $type file: * $in_file" notice "\ Showing '$type' details for: '$name' This file is stored at: * $in_file${NL}" 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" } # => 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 in_file="$EASYRSA_PKI/ca.crt" format="x509" # Verify file exists and is of the correct type [ -f "$in_file" ] || user_error "\ No such $type file with a basename of '$name' is present. Expected to find this file at: $in_file" verify_file "$format" "$in_file" || user_error "\ This file is not a valid $type file: $in_file" notice "\ Showing details for CA certificate, at: * $in_file${NL}" easyrsa_openssl "$format" -in "$in_file" -noout -text \ -nameopt "$name_opts" -certopt "$out_opts" || \ die "OpenSSL failure to process the input" } # => show_ca() # Certificate X509v3 Extended Key Usage ssl_cert_x509v3_eku() { [ "$1" ] || die "ssl_cert_x509v3_eku - Missing input" # check input file name if [ -f "$1" ]; then __crt="$1" else __crt="${EASYRSA_PKI}/issued/${1}.crt" [ -f "$__crt" ] || \ die "ssl_cert_x509v3_eku - Missing cert '$__crt'" fi # Set output variable __var="$2" shift "$#" # required variables __pattern="X509v3 Extended Key Usage:" __cli="TLS Web Client Authentication" __srv="TLS Web Server Authentication" __srv_cli="${__srv}, ${__cli}" __codeSign="Code Signing" unset -v __known # Extract certificate Extended Key Usage if [ "$ssl_lib" = libressl ]; then __eku="$( "$EASYRSA_OPENSSL" x509 -in "${__crt}" -noout -text | \ sed -n "/${__pattern}/{n;s/^ *//g;p;}" )" else __eku="$( "$EASYRSA_OPENSSL" x509 -in "${__crt}" -noout \ -ext extendedKeyUsage | \ sed -e /"${__pattern}"/d -e s/^\ *// )" fi # Match EKU with supported usage case "$__eku" in "$__srv_cli") __known=1 __type=serverClient ;; "$__cli") __known=1 __type=client ;; "$__srv") __known=1 __type=server ;; "$__codeSign") __known=1 __type=codeSign ;; '') __type=undefined ;; *) __type="'$__eku'" esac # Check for self-sign if "$EASYRSA_OPENSSL" x509 -in "$__crt" -noout -text | \ grep -q 'CA:TRUE' then __type="self-signed-$__type" fi # Set variable to return if [ "$__var" ]; then verbose "ssl_cert_x509v3_eku - EKU: $__type" force_set_var "$__var" "$__type" elif [ "$__known" ]; then information " * Known X509v3 Extended Key Usage: $__type" else information " * Unknown X509v3 Extended Key Usage: $__type" fi unset -v __crt __var __pattern __srv_cli __cli __srv \ __codeSign __eku __type if [ "$__known" ]; then unset -v __known return fi # Also, catch errors from SSL x509 command # for '__eku' subshell+pipe return 1 } # => ssl_cert_x509v3_eku() # get the serial number of the certificate -> serial=XXXX ssl_cert_serial() { [ "$#" = 2 ] || die "ssl_cert_serial - input error" [ -f "$1" ] || die "ssl_cert_serial - missing cert" fn_ssl_out="$( "$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() # 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 # No expand '' - detect_host() 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() { case "$EASYRSA_ALGO" in rsa) # Set RSA key size EASYRSA_ALGO_PARAMS="$EASYRSA_KEY_SIZE" ;; ec) # Verify Elliptic curve EASYRSA_ALGO_PARAMS="" easyrsa_mktemp EASYRSA_ALGO_PARAMS || \ die "\ verify_algo_params - easyrsa_mktemp EASYRSA_ALGO_PARAMS" # Create the required ecparams file, temp-file # call openssl directly because error is expected "$EASYRSA_OPENSSL" ecparam \ -name "$EASYRSA_CURVE" \ -out "$EASYRSA_ALGO_PARAMS" \ >/dev/null 2>&1 || user_error "\ Failed to generate ecparam file for curve '$EASYRSA_CURVE'" ;; ed) # Verify Edwards curve # call openssl directly because error is expected "$EASYRSA_OPENSSL" genpkey \ -algorithm "$EASYRSA_CURVE" \ >/dev/null 2>&1 || user_error "\ Edwards Curve '$EASYRSA_CURVE' not found." ;; *) user_error "\ Unknown algorithm '$EASYRSA_ALGO': Must be 'rsa', 'ec' or 'ed'" esac verbose "\ verify_algo_params: Params verified for algo '$EASYRSA_ALGO' OK" } # => verify_algo_params() # Check for conflicting input options mutual_exclusions() { # --nopass cannot be used with --passout if [ "$EASYRSA_PASSOUT" ]; then # --passout MUST take priority over --nopass [ "$EASYRSA_NO_PASS" ] && warn "\ Option --passout cannot be used with --nopass|nopass." unset -v EASYRSA_NO_PASS prohibit_no_pass=1 fi # --silent-ssl requires --batch if [ "$EASYRSA_SILENT_SSL" ]; then [ "$EASYRSA_BATCH" ] || warn "\ Option --silent-ssl requires batch mode --batch." fi # --startdate requires --enddate # otherwise, --days counts from now if [ "$EASYRSA_START_DATE" ]; then [ "$EASYRSA_END_DATE" ] || user_error "\ Use of --startdate requires use of --enddate." fi # --enddate may over-rule EASYRSA_CERT_EXPIRE if [ "$EASYRSA_END_DATE" ]; then case "$cmd" in sign-req|build-*-full|renew) # User specified alias_days IS over-ruled if [ "$alias_days" ]; then warn "\ Option --days is over-ruled by option --enddate." fi unset -v EASYRSA_CERT_EXPIRE alias_days ;; *) warn "\ EasyRSA '$cmd' does not support --startdate or --enddate" unset -v EASYRSA_START_DATE EASYRSA_END_DATE esac fi # Insecure Windows directory if [ "$easyrsa_host_os" = win ]; then if echo "$PWD" | grep -q '/Prog.*/OpenVPN/easy-rsa' then verbose "\ Using Windows-System-Folders for your PKI is NOT SECURE! Your Easy-RSA PKI CA Private Key is WORLD readable. To correct this problem, it is recommended that you either: * Copy Easy-RSA to your User folders and run it from there, OR * Define your PKI to be in your User folders. EG: 'easyrsa --pki-dir=\"C:/Users//easy-rsa/pki\"\ '" fi fi verbose "mutual_exclusions: COMPLETED" } # => mutual_exclusions() # Select vars in order preference: # Here sourcing of 'vars' if present occurs. # If not present, defaults are used to support # running without a sourced config format. select_vars() { # User specified vars file will be used ONLY if [ "$EASYRSA_VARS_FILE" ]; then # Takes priority, nothing to do verbose "select_vars: EASYRSA_VARS_FILE" # This is where auto-load goes bananas else # User specified PKI; if vars exists, use it ONLY if [ "$EASYRSA_PKI" ]; then if [ -f "$EASYRSA_PKI/vars" ]; then verbose "select_vars: source EASYRSA_PKI/vars" set_var EASYRSA_VARS_FILE "$EASYRSA_PKI/vars" fi fi # User specified EASYRSA; if vars exists, use it ONLY if [ "$EASYRSA" ]; then if [ -f "$EASYRSA/vars" ]; then verbose "select_vars: EASYRSA/vars" set_var EASYRSA_VARS_FILE "$EASYRSA/vars" fi fi # Default PKI; if vars exists, use it ONLY if [ -f "$PWD/pki/vars" ] && \ [ -z "$EASYRSA_PKI" ] && \ [ -z "$EASYRSA" ] then # Prevent vars from changing expected PKI. # A vars in the PKI MUST always imply EASYRSA_PKI # This is NOT backward compatible # Use expected value comparison for v3.1.7 if [ -z "$EASYRSA_VARS_FILE" ]; then expected_EASYRSA="$PWD" expected_EASYRSA_PKI="$PWD/pki" fi # Use this for v3.2.0 # If the pki/vars sets a different PKI then # there will be no PKI in the default /pki #set_var EASYRSA "$PWD" #set_var EASYRSA_PKI "$EASYRSA/pki" verbose "select_vars: PWD/pki/vars" set_var EASYRSA_VARS_FILE "$PWD/pki/vars" fi # Default working dir; if vars exists, use it ONLY if [ -f "$PWD/vars" ]; then verbose "select_vars: PWD/vars" set_var EASYRSA_VARS_FILE "$PWD/vars" fi fi # if select_vars failed to find a vars file if [ -z "$EASYRSA_VARS_FILE" ]; then verbose "select_vars: No vars" return 1 fi } # => select_vars() # Source a vars file source_vars() { # File to be sourced target_file="$1" # 'vars' MUST not be a directory [ -d "$target_file" ] && user_error "\ Missing vars file: * $target_file" # 'vars' now MUST exist [ -f "$target_file" ] || user_error "\ Missing vars file: * $target_file" # Sanitize vars if grep -q \ -e 'EASYRSA_PASSIN' -e 'EASYRSA_PASSOUT' \ -e '[^(]`[^)]' \ -e 'export ' \ -e 'unset ' \ "$target_file" then # here we go .. err_msg="\ These problems have been found in your 'vars' settings:${NL}" # No passwords! if grep -q \ -e 'EASYRSA_PASSIN' -e 'EASYRSA_PASSOUT' \ "$target_file" then err_msg="${err_msg} Use of 'EASYRSA_PASSIN' or 'EASYRSA_PASSOUT': Storing password information in the 'vars' file is not permitted." fi # No backticks if grep -q \ -e '[^(]`[^)]' \ "$target_file" then err_msg="${err_msg} Use of unsupported characters: These characters are not supported: \` backtick" fi # No export if grep -q \ -e 'export ' \ "$target_file" then err_msg="${err_msg} Use of 'export': Remove 'export' or replace it with 'set_var'." fi # No unset if grep -q \ -e 'unset ' \ "$target_file" then err_msg="${err_msg} Use of 'unset': Remove 'unset' ('force_set_var' may also work)." fi # Fatal error user_error "${err_msg}${NL} Please, correct these errors and try again." else verbose "source_vars: CLEAN '$target_file'" fi # Enable sourcing 'vars' # shellcheck disable=SC2034 # appears unused - source_vars() EASYRSA_CALLER=1 easyrsa_path="$PATH" # shellcheck disable=SC2123 # PATH is - source_vars() PATH=./ # Test sourcing 'vars' in a subshell # shellcheck disable=1090 # can't follow - source_vars() if ( . "$target_file" ); then # Source 'vars' now # shellcheck disable=1090 # can't follow - source_vars() . "$target_file" || \ die "Failed to source the '$target_file' file." else PATH="$easyrsa_path" die "Failed to dry-run the '$target_file' file." fi PATH="$easyrsa_path" verbose "source_vars: sourced OK '$target_file'" unset -v EASYRSA_CALLER easyrsa_path target_file } # => source_vars() # Set defaults default_vars() { # 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_REQ_SERIAL "" set_var EASYRSA_ALGO rsa set_var EASYRSA_KEY_SIZE 2048 case "$EASYRSA_ALGO" in rsa) : # ok # default EASYRSA_KEY_SIZE must always be set # it must NOT be set selectively because it is # present in the SSL config file ;; ec) set_var EASYRSA_CURVE secp384r1 ;; ed) set_var EASYRSA_CURVE ed25519 ;; *) user_error "\ Algorithm '$EASYRSA_ALGO' is invalid: Must be 'rsa', 'ec' or 'ed'" esac set_var EASYRSA_CA_EXPIRE 3650 set_var EASYRSA_CERT_EXPIRE 825 set_var \ EASYRSA_PRE_EXPIRY_WINDOW 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_KDC_REALM "CHANGEME.EXAMPLE.COM" set_var EASYRSA_MAX_TEMP 1 } # => default_vars() # Validate expected values for EASYRSA and EASYRSA_PKI validate_default_vars() { unset -v unexpected_error # Keep checks separate # EASYRSA if [ "$expected_EASYRSA" ]; then [ "$expected_EASYRSA" = "$EASYRSA" ] || \ unexpected_error="\ EASYRSA: $EASYRSA Expected: $expected_EASYRSA" fi # EASYRSA_PKI if [ "$expected_EASYRSA_PKI" ]; then if [ "$expected_EASYRSA_PKI" = "$EASYRSA_PKI" ]; then : # ok else if [ "$unexpected_error" ]; then # Add a new-line Extra separator, for clarity unexpected_error="${unexpected_error}${NL}${NL}" fi unexpected_error="${unexpected_error}\ EASYRSA_PKI: $EASYRSA_PKI Expected: $expected_EASYRSA_PKI" fi fi # Return no error [ -z "$unexpected_error" ] && return # This is an almost unacceptable error invalid_vars=1 [ "$quiet_vars" ] || user_error "\ The values in the vars file have unexpectedly changed the values for EASYRSA and/or EASYRSA_PKI. The default pki/vars file is forbidden to change these values. vars-file: $EASYRSA_VARS_FILE ${unexpected_error}" } # => validate_default_vars() # Verify working environment verify_working_env() { verbose "verify_working_env: BEGIN" # For commands which 'require a PKI' and PKI exists if [ "$require_pki" ]; then # Verify PKI is initialised verify_pki_init # Temp dir session secure_session # global safe ssl cnf temp write_global_safe_ssl_cnf_tmp # Verify selected algorithm and parameters verify_algo_params # Verify CA is initialised if [ "$require_ca" ]; then verify_ca_init fi else # For commands that do not require a PKI # but do require a temp-dir, eg. 'write' # If there is a valid temp-dir: # Create temp-session and openssl-easyrsa.cnf (Temp) now if [ -d "$EASYRSA_TEMP_DIR" ]; then # Temp dir session secure_session # global safe ssl cnf temp write_global_safe_ssl_cnf_tmp fi fi verbose "verify_working_env: COMPLETED Handover-to: $cmd" } # => verify_working_env() # variable assignment by indirection. # Sets '$1' as the value contained in '$2' # and exports (may be blank) set_var() { [ -z "$*" ] && return [ -z "$3" ] || \ user_error "set_var - excess input '$*'" case "$1" in *=*) user_error "set_var - var '$1'" esac eval "export \"$1\"=\"\${$1-$2}\"" && return die "set_var - eval '$*'" } # => set_var() # sanitize and set var # nix.sh/win.sh/busybox.sh never return error from unset # when an invalid variable name 'a=b' is used with a value # to set, eg. 'c'; This causes EasyRSA to execute: # eval "export a=b=c". 'set_var EASYRSA_PKI=pki' results in # $EASYRSA_PKI being set to 'pki=pki-', without error! # Guard against this possible user error with 'case'. force_set_var() { [ -z "$3" ] || \ user_error "force_set_var - excess input '$*'" case "$1" in *=*) user_error "force_set_var - var '$1'" esac # force unsetting $1; Guard unset with '|| die', just in case unset -v "$1" || die "force_set_var - unset '$1'" # Allow empty value to unset variable by returning [ "$2" ] || return 0 set_var "$1" "$2" && return die "force_set_var - set_var '$*'" } # => force_set_var() # global Safe SSL conf file, for use by any SSL lib write_global_safe_ssl_cnf_tmp() { global_safe_ssl_cnf_tmp= easyrsa_mktemp global_safe_ssl_cnf_tmp || die "\ verify_working_env - easyrsa_mktemp global_safe_ssl_cnf_tmp" write_legacy_file_v2 safe-cnf "$global_safe_ssl_cnf_tmp" || \ die "verify_working_env - write safe-cnf" export OPENSSL_CONF="$global_safe_ssl_cnf_tmp" verbose "GLOBAL - OPENSSL_CONF = $OPENSSL_CONF" } # => write_global_safe_ssl_cnf_tmp() # Create as needed: $EASYRSA_SSL_CONF pki/openssl-easyrsa.cnf # If the existing file has a known hash then use temp-file. # Otherwise, use the file in place. write_easyrsa_ssl_cnf_tmp() { if [ -f "$EASYRSA_SSL_CONF" ]; then verbose "write_easyrsa_ssl_cnf_tmp: SSL config EXISTS" # Set known hashes # 3.1.7 -> Current known_file_317="\ 13ca05f031d58c5e2912652b33099ce9\ ac05f49595e5d5fe96367229e3ce070c" # 3.1.5 -> 3.1.6 known_file_315="\ 87d51ca0db1cc0ac3cc2634792fc5576\ e0034ebf9d546de11674b897514f3afb" # 3.1.0 -> 3.1.4 known_file_310="\ 5455947df40f01f845bf79c1e89f102c\ 628faaa65d71a6512d0e17bdd183feb0" # 3.0.8 -> 3.0.9 known_file_308="\ 1cc6a1de93ca357b5c364aa0fa2c4bea\ f97425686fa1976d436fa31f550641aa" # Built-in here-doc 3.2.0 known_heredoc_320="\ 82439f1860838e28f6270d5d06b17717\ 56db777861e19bf9edc21222f86a310d" # Get file hash file_hash="$( "$EASYRSA_OPENSSL" dgst -sha256 -r \ "$EASYRSA_SSL_CONF" 2>/dev/null )" || die "write_easyrsa_ssl_cnf_tmp - hash malfunction!" # Strip excess SSL info file_hash="${file_hash%% *}" # Compare SSL output case "$file_hash" in *[!1234567890abcdef]*|'') die "write_easyrsa_ssl_cnf_tmp - hash failure!" esac # Check file hash against known hash hash_is_unknown="" case "$file_hash" in "$known_file_317") ;; "$known_file_315") ;; "$known_file_310") ;; "$known_file_308") ;; "$known_heredoc_320") ;; *) # File is unknown or has been changed # leave in place hash_is_unknown=1 esac # Cleanup unset -v file_hash known_heredoc_320 \ known_file_317 \ known_file_315 \ known_file_310 \ known_file_308 # Use the existing file ONLY if [ "$hash_is_unknown" ] || [ "$EASYRSA_FORCE_SAFE_SSL" ] then unset -v hash_is_unknown verbose "write_easyrsa_ssl_cnf_tmp: SSL config UNKNOWN!" # Auto-escape hazardous characters escape_hazard || \ die "easyrsa_openssl - escape_hazard failed" # Rewrite SSL config expand_ssl_config || \ die "easyrsa_openssl - expand_ssl_config failed" return 0 fi # Ignore existing file, prefer to use a temp-file verbose "write_easyrsa_ssl_cnf_tmp: SSL config KNOWN" fi # SET and USE temp-file from here-doc Now # Create temp-file ssl_cnf_tmp= easyrsa_mktemp ssl_cnf_tmp || die "\ write_easyrsa_ssl_cnf_tmp - easyrsa_mktemp" # Write SSL cnf to temp-file write_legacy_file_v2 "$ssl_cnf_type" "$ssl_cnf_tmp" || die "\ write_easyrsa_ssl_cnf_tmp - write $ssl_cnf_type: $ssl_cnf_tmp" # export SSL cnf tmp export EASYRSA_SSL_CONF="$ssl_cnf_tmp" verbose "\ write_easyrsa_ssl_cnf_tmp: $ssl_cnf_type \ - EASYRSA_SSL_CONF = $EASYRSA_SSL_CONF" export OPENSSL_CONF="$EASYRSA_SSL_CONF" verbose "LOCAL - OPENSSL_CONF = $OPENSSL_CONF" } # => write_easyrsa_ssl_cnf_tmp() # Write x509 type file to a temp file write_x509_type_tmp() { # Verify x509-type before redirect case "$1" in COMMON|ca|server|serverClient|client|email| \ codeSigning|kdc|selfsign) : # ok ;; *) die "write_x509_type_tmp - unknown type '$1'" esac write_x509_file_tmp="" easyrsa_mktemp write_x509_file_tmp || \ die "write_x509_type_tmp - easyrsa_mktemp" write_legacy_file_v2 "$1" "$write_x509_file_tmp" || \ die "write_x509_type_tmp - write $1" verbose ": write_x509_type_tmp: $1 COMPLETE" } # => write_x509_type_tmp() ############################################################################ # # Create legacy files # # Write ALL legacy files to $1 or default all_legacy_files_v2() { # Confirm over write if [ "$legacy_file_over_write" ]; then confirm "${NL} Confirm OVER-WRITE files ? " yes " Warning: 'legacy-hard' will OVER-WRITE all legacy files to default settings. Legacy files: * File: ${EASYRSA_PKI}/openssl-easyrsa.cnf * File: ${EASYRSA_PKI}/vars.example * Dir: ${EASYRSA_PKI}/x509-types/*" verbose "all_legacy_files_v2 - over-write ENABLED" fi # Output directories legacy_out_d="$EASYRSA_PKI" easyrsa_mkdir "$legacy_out_d" x509_types_d="$legacy_out_d"/x509-types easyrsa_mkdir "$x509_types_d" # Create x509-types for legacy_type in COMMON ca server serverClient client \ email codeSigning kdc do legacy_target="${x509_types_d}/${legacy_type}" write_legacy_file_v2 "$legacy_type" "$legacy_target" \ "$legacy_file_over_write" done # vars.example legacy_type=vars legacy_target="$legacy_out_d"/vars.example write_legacy_file_v2 "$legacy_type" "$legacy_target" \ "$legacy_file_over_write" # openssl-easyrsa.cnf legacy_type=ssl-cnf legacy_target="$legacy_out_d"/openssl-easyrsa.cnf write_legacy_file_v2 "$legacy_type" "$legacy_target" \ "$legacy_file_over_write" # User notice if [ "$legacy_file_over_write" ]; then notice "legacy-hard has updated all files." else notice "legacy has updated missing files." fi } # => all_legacy_files_v2() # write legacy files to stdout or user defined file write_legacy_file_v2() { # recursion check write_recursion="$(( write_recursion + 1 ))" if [ "$write_recursion" -gt 1 ]; then print "write recursion" > "$easyrsa_err_log" die "write recursion" fi write_type="$1" write_file="$2" write_over= [ "$3" = overwrite ] && write_over="$3" # Select by type case "$write_type" in ssl-cnf) set_openssl_easyrsa_cnf_vars unexpanded ;; safe-cnf) set_openssl_easyrsa_cnf_vars expanded ;; vars) ;; # This correctly renames 'code-signing' to 'codeSigning' COMMON|ca|server|serverClient|client|codeSigning|email|kdc) ;; selfsign) ;; *) user_error "write - unknown type '$write_type'" esac # If $write_file is given then establish overwrite rules if [ "$write_file" ]; then # $write_file must not be a directory [ -d "$write_file" ] && user_error \ "write: Target is a directory: '$write_file'" # If $write_file exists then check for temp-file if [ -f "$write_file" ]; then # if this is a temp file then enable auto-overwrite path="${write_file%%/temp.*}" if [ "${secured_session}" = "$path" ]; then verbose ": write_legacy_file_v2 - temp-file ACCEPTED" write_over=overwrite else # target is not a temp-file, overwrite not changed verbose ": Target is not a temp-file: $write_file" fi else # enable overwrite, "there is no file" to over write verbose ": Missing input file: $write_file" write_over=overwrite fi fi # write legacy data stream to stdout or file if [ "$write_file" ]; then if [ "$write_over" ]; then verbose ": write_legacy_file_v2 - over-write ENABLED" create_legacy_stream "$write_type" > "$write_file" || \ die "write failed" else user_error "write: Over-write refused for existing file!" fi else # write stream to stdout ONLY create_legacy_stream "$write_type" fi write_recursion="$(( write_recursion - 1 ))" } # => write_legacy_file_v2() # set heredoc variables for openssl-easyrsa.cnf # shellcheck disable=SC2016 # (info): $ don't expand in '' set_openssl_easyrsa_cnf_vars(){ case "$1" in expanded) # fully expand ssl-cnf for safe-cnf conf_EASYRSA_dir="$EASYRSA_PKI" conf_EASYRSA_PKI="$EASYRSA_PKI" conf_EASYRSA_DIGEST="$EASYRSA_DIGEST" conf_EASYRSA_KEY_SIZE="$EASYRSA_KEY_SIZE" conf_EASYRSA_DN="$EASYRSA_DN" conf_EASYRSA_REQ_CN="$EASYRSA_REQ_CN" conf_EASYRSA_REQ_COUNTRY="$EASYRSA_REQ_COUNTRY" conf_EASYRSA_REQ_PROVINCE="$EASYRSA_REQ_PROVINCE" conf_EASYRSA_REQ_CITY="$EASYRSA_REQ_CITY" conf_EASYRSA_REQ_ORG="$EASYRSA_REQ_ORG" conf_EASYRSA_REQ_OU="$EASYRSA_REQ_OU" conf_EASYRSA_REQ_EMAIL="$EASYRSA_REQ_EMAIL" conf_EASYRSA_REQ_SERIAL="$EASYRSA_REQ_SERIAL" ;; unexpanded) # write standard ssl-cnf conf_EASYRSA_dir='$dir' conf_EASYRSA_PKI='$ENV::EASYRSA_PKI' conf_EASYRSA_DIGEST='$ENV::EASYRSA_DIGEST' conf_EASYRSA_KEY_SIZE='$ENV::EASYRSA_KEY_SIZE' conf_EASYRSA_DN='$ENV::EASYRSA_DN' conf_EASYRSA_REQ_CN='$ENV::EASYRSA_REQ_CN' conf_EASYRSA_REQ_COUNTRY='$ENV::EASYRSA_REQ_COUNTRY' conf_EASYRSA_REQ_PROVINCE='$ENV::EASYRSA_REQ_PROVINCE' conf_EASYRSA_REQ_CITY='$ENV::EASYRSA_REQ_CITY' conf_EASYRSA_REQ_ORG='$ENV::EASYRSA_REQ_ORG' conf_EASYRSA_REQ_OU='$ENV::EASYRSA_REQ_OU' conf_EASYRSA_REQ_EMAIL='$ENV::EASYRSA_REQ_EMAIL' conf_EASYRSA_REQ_SERIAL='$ENV::EASYRSA_REQ_SERIAL' ;; *) die "set_openssl_easyrsa_cnf_vars - input" esac } # => set_openssl_easyrsa_cnf_vars() # Create x509 type create_legacy_stream() { case "$1" in COMMON) # COMMON is not very useful cat <<- "CREATE_X509_TYPE_COMMON" CREATE_X509_TYPE_COMMON ;; easyrsa) # This could be COMMON but not is not suitable for a CA cat <<- "CREATE_X509_TYPE_EASYRSA" basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always keyUsage = digitalSignature,keyEncipherment CREATE_X509_TYPE_EASYRSA ;; serverClient) # serverClient create_legacy_stream easyrsa cat <<- "CREATE_X509_TYPE_SERV_CLI" extendedKeyUsage = serverAuth,clientAuth CREATE_X509_TYPE_SERV_CLI ;; server) # server create_legacy_stream easyrsa cat <<- "CREATE_X509_TYPE_SERV" extendedKeyUsage = serverAuth CREATE_X509_TYPE_SERV ;; client) # client create_legacy_stream easyrsa cat <<- "CREATE_X509_TYPE_CLI" extendedKeyUsage = clientAuth CREATE_X509_TYPE_CLI ;; ca) # ca cat <<- "CREATE_X509_TYPE_CA" basicConstraints = CA:TRUE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always keyUsage = cRLSign, keyCertSign CREATE_X509_TYPE_CA ;; selfsign) # selfsign cat <<- "CREATE_X509_TYPE_SELFSIGN" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always basicConstraints = CA:TRUE keyUsage = digitalSignature,keyEncipherment CREATE_X509_TYPE_SELFSIGN print "extendedKeyUsage = $selfsign_eku" ;; codeSigning) # codeSigning cat <<- "CREATE_X509_CODE_SIGNING" basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always extendedKeyUsage = codeSigning keyUsage = digitalSignature CREATE_X509_CODE_SIGNING ;; email) # email cat <<- "CREATE_X509_TYPE_EMAIL" basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always extendedKeyUsage = emailProtection keyUsage = digitalSignature,keyEncipherment,nonRepudiation CREATE_X509_TYPE_EMAIL ;; kdc) # kdc cat <<- "CREATE_X509_TYPE_KDC" basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always extendedKeyUsage = 1.3.6.1.5.2.3.5 keyUsage = nonRepudiation,digitalSignature,keyEncipherment,keyAgreement issuerAltName = issuer:copy subjectAltName = otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name [kdc_princ_name] realm = EXP:0,GeneralString:${ENV::EASYRSA_KDC_REALM} principal_name = EXP:1,SEQUENCE:kdc_principal_seq [kdc_principal_seq] name_type = EXP:0,INTEGER:1 name_string = EXP:1,SEQUENCE:kdc_principals [kdc_principals] princ1 = GeneralString:krbtgt princ2 = GeneralString:${ENV::EASYRSA_KDC_REALM} CREATE_X509_TYPE_KDC ;; vars) # vars cat << "CREATE_VARS_EXAMPLE" # Easy-RSA 3 parameter settings # NOTE: If you installed Easy-RSA from your package manager, do not edit # this file in place -- instead, you should copy the entire easy-rsa directory # to another location so future upgrades do not wipe out your changes. # HOW TO USE THIS FILE # # vars.example contains built-in examples to Easy-RSA settings. You MUST name # this file "vars" if you want it to be used as a configuration file. If you # do not, it WILL NOT be automatically read when you call easyrsa commands. # # It is not necessary to use this config file unless you wish to change # operational defaults. These defaults should be fine for many uses without # the need to copy and edit the "vars" file. # # All of the editable settings are shown commented and start with the command # "set_var" -- this means any set_var command that is uncommented has been # modified by the user. If you are happy with a default, there is no need to # define the value to its default. # NOTES FOR WINDOWS USERS # # Paths for Windows *MUST* use forward slashes, or optionally double-escaped # backslashes (single forward slashes are recommended.) This means your path # to the openssl binary might look like this: # "C:/Program Files/OpenSSL-Win32/bin/openssl.exe" # A little housekeeping: DO NOT EDIT THIS SECTION # # Easy-RSA 3.x does not source into the environment directly. # Complain if a user tries to do this: if [ -z "$EASYRSA_CALLER" ]; then echo "You appear to be sourcing an Easy-RSA *vars* file. This is" >&2 echo "no longer necessary and is disallowed. See the section called" >&2 echo "*How to use this file* near the top comments for more details." >&2 return 1 fi # DO YOUR EDITS BELOW THIS POINT # If your OpenSSL command is not in the system PATH, you will need to define # the path here. Normally this means a full path to the executable, otherwise # you could have left it undefined here and the shown default would be used. # # Windows users, remember to use paths with forward-slashes (or escaped # back-slashes.) Windows users should declare the full path to the openssl # binary here if it is not in their system PATH. # #set_var EASYRSA_OPENSSL "openssl" # # 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" # Windows users, to generate OpenVPN TLS Keys the Openvpn binary must be # defined here. # #set_var EASYRSA_OPENVPN "C:\\Program Files\\Openvpn\\bin\\openvpn.exe" # Define X509 DN mode. # # This is used to adjust which elements are included in the Subject field # as the DN ("Distinguished Name"). Note that in 'cn_only' mode the # Organizational fields, listed further below, are not used. # # Choices are: # cn_only - Use just a commonName value. # org - Use the "traditional" format: # Country/Province/City/Org/Org.Unit/email/commonName # #set_var EASYRSA_DN "cn_only" # Organizational fields (used with "org" mode and ignored in "cn_only" mode). # These are the default values for fields which will be placed in the # certificate. Do not leave any of these fields blank, although interactively # you may omit any specific field by typing the "." symbol (not valid for # email). # # NOTE: The following characters are not supported # in these "Organizational fields" by Easy-RSA: # back-tick (`) # #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" # Preserve the Distinguished Name field order # of the certificate signing request # *Only* effective in --dn-mode=org # #set_var EASYRSA_PRESERVE_DN 1 # Set no password mode - This will create the entire PKI without passwords. # This can be better managed by choosing which entity private keys should be # encrypted with the following command line options: # Global option '--no-pass' or command option 'nopass'. # #set_var EASYRSA_NO_PASS 1 # Choose a size in bits for your keypairs. The recommended value is 2048. # Using 2048-bit keys is considered more than sufficient for many years into # the future. Larger keysizes will slow down TLS negotiation and make key/DH # param generation take much longer. Values up to 4096 should be accepted by # most software. Only used when the crypto alg is rsa, see below. # #set_var EASYRSA_KEY_SIZE 2048 # The default crypto mode is rsa; ec can enable elliptic curve support. # Note that not all software supports ECC, so use care when enabling it. # Choices for crypto alg are: (each in lower-case) # * rsa # * ec # * ed # #set_var EASYRSA_ALGO rsa # Define the named curve, used in ec & ed modes: # #set_var EASYRSA_CURVE secp384r1 # In how many days should the root CA key expire? # #set_var EASYRSA_CA_EXPIRE 3650 # In how many days should certificates expire? # #set_var EASYRSA_CERT_EXPIRE 825 # How many days until the next CRL publish date? Note that the CRL can still # be parsed after this timeframe passes. It is only used for an expected next # publication date. # #set_var EASYRSA_CRL_DAYS 180 # Random serial numbers by default. # Set to 'no' for the old incremental serial numbers. # #set_var EASYRSA_RAND_SN "yes" # Cut-off window for checking expiring certificates. # #set_var EASYRSA_PRE_EXPIRY_WINDOW 90 # Generate automatic subjectAltName for certificates # #set_var EASYRSA_AUTO_SAN 1 # Add critical attribute to X509 fields: basicConstraints (BC), # keyUsage (KU), extendedKeyUsage (EKU) or SAN # #set_var EASYRSA_BC_CRIT 1 #set_var EASYRSA_KU_CRIT 1 #set_var EASYRSA_EKU_CRIT 1 #set_var EASYRSA_SAN_CRIT 1 # Disable automatic inline files # #set_var EASYRSA_DISABLE_INLINE 1 CREATE_VARS_EXAMPLE ;; ssl-cnf|safe-cnf) # SSL config v3.2.0-1 cat << CREATE_SSL_CONFIG # For use with Easy-RSA 3.0+ and OpenSSL or LibreSSL #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] dir = $conf_EASYRSA_PKI # Where everything is kept certs = $conf_EASYRSA_dir # Where the issued certs are kept crl_dir = $conf_EASYRSA_dir # Where the issued crl are kept database = $conf_EASYRSA_dir/index.txt # database index file. new_certs_dir = $conf_EASYRSA_dir/certs_by_serial # default place for new certs. certificate = $conf_EASYRSA_dir/ca.crt # The CA certificate serial = $conf_EASYRSA_dir/serial # The current serial number crl = $conf_EASYRSA_dir/crl.pem # The current CRL private_key = $conf_EASYRSA_dir/private/ca.key # The private key RANDFILE = $conf_EASYRSA_dir/.rand # private random number file x509_extensions = basic_exts # The extensions to add to the cert # A placeholder to handle the --copy-ext feature: #%COPY_EXTS% # Do NOT remove or change this line as --copy-ext support requires it # This allows a V2 CRL. Ancient browsers don't like it, but anything Easy-RSA # is designed for will. In return, we get the Issuer attached to CRLs. crl_extensions = crl_ext # These fields are always configured via the command line. # These fields are removed from this here-doc but retained # in 'openssl-easyrsa.cnf' file, in case something breaks. # default_days is no longer required by Easy-RSA #default_days = \$ENV::EASYRSA_CERT_EXPIRE # how long to certify for # default_crl_days is no longer required by Easy-RSA #default_crl_days = \$ENV::EASYRSA_CRL_DAYS # how long before next CRL default_md = $conf_EASYRSA_DIGEST # use public key default MD preserve = no # keep passed DN ordering # This allows to renew certificates which have not been revoked unique_subject = no # A few different ways of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_anything # For the 'anything' policy, which defines allowed DN fields [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional serialNumber = optional #################################################################### # Easy-RSA request handling # We key off \$DN_MODE to determine how to format the DN [ req ] default_bits = $conf_EASYRSA_KEY_SIZE default_keyfile = privkey.pem default_md = $conf_EASYRSA_DIGEST distinguished_name = $conf_EASYRSA_DN x509_extensions = easyrsa_ca # The extensions to add to the self signed cert # A placeholder to handle the \$EXTRA_EXTS feature: #%EXTRA_EXTS% # Do NOT remove or change this line as \$EXTRA_EXTS support requires it #################################################################### # Easy-RSA DN (Subject) handling # Easy-RSA DN for cn_only support: [ cn_only ] commonName = Common Name (eg: your user, host, or server name) commonName_max = 64 commonName_default = $conf_EASYRSA_REQ_CN # Easy-RSA DN for org support: [ org ] countryName = Country Name (2 letter code) countryName_default = $conf_EASYRSA_REQ_COUNTRY countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = $conf_EASYRSA_REQ_PROVINCE localityName = Locality Name (eg, city) localityName_default = $conf_EASYRSA_REQ_CITY 0.organizationName = Organization Name (eg, company) 0.organizationName_default = $conf_EASYRSA_REQ_ORG organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = $conf_EASYRSA_REQ_OU commonName = Common Name (eg: your user, host, or server name) commonName_max = 64 commonName_default = $conf_EASYRSA_REQ_CN emailAddress = Email Address emailAddress_default = $conf_EASYRSA_REQ_EMAIL emailAddress_max = 64 serialNumber = Serial-number (eg, device serial-number) serialNumber_default = $conf_EASYRSA_REQ_SERIAL #################################################################### # Easy-RSA cert extension handling # This section is effectively unused as the main script sets extensions # dynamically. This core section is left to support the odd usecase where # a user calls openssl directly. [ basic_exts ] basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always # The Easy-RSA CA extensions [ easyrsa_ca ] # PKIX recommendations: subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always # This could be marked critical, but it's nice to support reading by any # broken clients who attempt to do so. basicConstraints = CA:true # Limit key usage to CA tasks. If you really want to use the generated pair as # a self-signed cert, comment this out. keyUsage = cRLSign, keyCertSign # nsCertType omitted by default. Let's try to let the deprecated stuff die. # nsCertType = sslCA # A placeholder to handle the \$X509_TYPES and CA extra extensions \$EXTRA_EXTS: #%CA_X509_TYPES_EXTRA_EXTS% # Do NOT remove or change this line as \$X509_TYPES and EXTRA_EXTS demands it # CRL extensions. [ crl_ext ] # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. # issuerAltName=issuer:copy authorityKeyIdentifier=keyid:always,issuer:always CREATE_SSL_CONFIG ;; *) die "create_legacy_stream: unknown type '$1'" esac } # => create_legacy_stream() # Load easyrsa-tools.lib source_easyrsa_tools_lib() { if [ -f "$EASYRSA_TOOLS_LIB" ]; then export EASYRSA_TOOLS_CALLER=1 # shellcheck disable=SC1090 # can't follow non-constant.. . "$EASYRSA_TOOLS_LIB" || \ die "Source failed: $EASYRSA_TOOLS_LIB" unset -v EASYRSA_TOOLS_CALLER tools_error verbose "EASYRSA_TOOLS_LIB: $EASYRSA_TOOLS_LIB" verbose "EASYRSA_TOOLS_VERSION: $EASYRSA_TOOLS_VERSION" # Verify tools version if [ "$EASYRSA_TOOLS_VERSION" -lt 321 ]; then tools_error_txt="\ EasyRSA Tools version is out of date: * EASYRSA_TOOLS_VERSION: $EASYRSA_TOOLS_VERSION" return 1 fi else verbose "Missing: easyrsa-tools.lib" tools_error_txt="Missing: easyrsa-tools.lib Use of command '$cmd' requires Easy-RSA tools library. Source: https://github.com/OpenVPN/easy-rsa/dev/easyrsa-tools.lib Download: https://raw.githubusercontent.com/OpenVPN/easy-rsa/refs/heads/master/dev/easyrsa-tools.lib Place a copy of easyrsa-tools.lib in a standard system location." return 1 fi } # => source_easyrsa_tools_lib() # Version information 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:-undefined} Git Commit: ~GITHEAD~ Source Repo: https://github.com/OpenVPN/easy-rsa 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 15" 15 # Get host details - No configurable input allowed detect_host # Initialisation requirements unset -v \ OPENSSL_CONF \ verify_ssl_lib_ok ssl_batch \ secured_session \ working_safe_ssl_conf working_safe_org_conf \ alias_days text \ prohibit_no_pass \ invalid_vars \ local_request error_build_full_cleanup \ selfsign_eku \ internal_batch mv_temp_error \ easyrsa_exit_with_error error_info \ write_recursion tools_error tools_error_txt # Used by build-ca->cleanup to restore prompt # after user interrupt when using manual password prompt_restore=0 # Sequential temp-file counter mktemp_counter=0 # 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" ;; --startdate) export EASYRSA_START_DATE="$val" ;; --enddate) export EASYRSA_END_DATE="$val" ;; --pki-dir|--pki) 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 ;; --verbose) empty_ok=1 export EASYRSA_VERBOSE=1 ;; --days-margin) # ONLY ALLOWED use by status reports number_only=1 export EASYRSA_iso_8601_MARGIN="$val" ;; -S|--silent-ssl) empty_ok=1 export EASYRSA_SILENT_SSL=1 ;; --force-safe-ssl) empty_ok=1 export EASYRSA_FORCE_SAFE_SSL=1 ;; --nopass|--no-pass) empty_ok=1 export EASYRSA_NO_PASS=1 ;; --passin) export EASYRSA_PASSIN="$val" ;; --passout) export EASYRSA_PASSOUT="$val" ;; --raw-ca) empty_ok=1 export EASYRSA_RAW_CA=1 ;; --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) export EASYRSA_VARS_FILE="$val" ;; --copy-ext) empty_ok=1 export EASYRSA_CP_EXT=1 ;; --subject-alt-name|--san) # This allows --san to be used multiple times if [ "$EASYRSA_SAN" ]; then EASYRSA_SAN="$EASYRSA_SAN, $val" else EASYRSA_SAN="$val" fi ;; --auto-san) empty_ok=1 export EASYRSA_AUTO_SAN=1 ;; --san-crit*) empty_ok=1 export EASYRSA_SAN_CRIT='critical,' ;; --bc-crit*) empty_ok=1 export EASYRSA_BC_CRIT=1 ;; --ku-crit*) empty_ok=1 export EASYRSA_KU_CRIT=1 ;; --eku-crit*) empty_ok=1 export EASYRSA_EKU_CRIT=1 ;; --new-subj*) export EASYRSA_NEW_SUBJECT="$val" ;; --usefn) export EASYRSA_P12_FR_NAME="$val" ;; --tools) export EASYRSA_TOOLS_LIB="$val" ;; --version) shift "$#" set -- "$@" "version" break ;; -*) user_error "\ Unknown option '$opt'. Run 'easyrsa help options' for option help." ;; *) break esac # fatal error when no value was provided if [ "$is_empty" ]; then [ "$empty_ok" ] || \ user_error "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" ] || \ user_error "$opt - Number expected: '$val'" ;; (*[!1234567890]*|0*) user_error "$opt - Number expected: '$val'" esac fi shift done # option dependencies # Add full --san to extra extensions if [ "$EASYRSA_SAN" ]; then EASYRSA_EXTRA_EXTS="\ $EASYRSA_EXTRA_EXTS subjectAltName = ${EASYRSA_SAN_CRIT}${EASYRSA_SAN}" fi # Set cmd now # vars_setup needs to know if this is init-pki cmd="$1" [ "$1" ] && shift # scrape off command # Establish PKI and CA initialisation requirements unset -v require_pki require_ca quiet_vars case "$cmd" in ''|help|-h|--help|--usage| \ version|show-host|rand|random) quiet_vars=1 ;; init-pki|clean-all) : # ok ;; *) require_pki=1 case "$cmd" in gen-req|gen-dh|build-ca|show-req|export-p*| \ inline|self-sign-*|write) : ;; # ok *) require_ca=1 esac esac # Intelligent env-var detection and auto-loading: # Select vars file as EASYRSA_VARS_FILE # then source the vars file, if found # otherwise, ignore no vars file if select_vars; then [ "$quiet_vars" ] || information "\ Using Easy-RSA 'vars' configuration: * $EASYRSA_VARS_FILE" source_vars "$EASYRSA_VARS_FILE" else verbose "\ No Easy-RSA 'vars' configuration file exists!" fi # then set defaults default_vars # Check for unexpected changes to EASYRSA or EASYRSA_PKI # https://github.com/OpenVPN/easy-rsa/issues/1006 validate_default_vars # Check for conflicting input options mutual_exclusions # Find x509-types, openssl-easyrsa.cnf and easyrsa-tools.lib locate_support_files # Verify SSL Lib - One time ONLY verify_ssl_lib # Check $working_safe_ssl_conf, to build # a fully configured safe ssl conf, on the # next invocation of easyrsa_openssl() if [ "$working_safe_ssl_conf" ]; then die "working_safe_ssl_conf must not be set!" fi # Hand off to the function responsible # ONLY verify_working_env() for valid commands case "$cmd" in init-pki|clean-all) verify_working_env init_pki "$@" ;; build-ca) verify_working_env [ -z "$alias_days" ] || \ export EASYRSA_CA_EXPIRE="$alias_days" build_ca "$@" ;; self-sign-server) verify_working_env [ -z "$alias_days" ] || \ export EASYRSA_CERT_EXPIRE="$alias_days" self_sign server "$@" ;; self-sign-client) verify_working_env [ -z "$alias_days" ] || \ export EASYRSA_CERT_EXPIRE="$alias_days" self_sign client "$@" ;; self*) user_error "Self-sign syntax example: 'self-sign-server foo'" ;; gen-dh) verify_working_env gen_dh ;; gen-req) verify_working_env gen_req "$@" ;; sign|sign-req) verify_working_env [ -z "$alias_days" ] || \ export EASYRSA_CERT_EXPIRE="$alias_days" sign_req "$@" ;; build-client-full) verify_working_env [ -z "$alias_days" ] || \ export EASYRSA_CERT_EXPIRE="$alias_days" build_full client "$@" ;; build-server-full) verify_working_env [ -z "$alias_days" ] || \ export EASYRSA_CERT_EXPIRE="$alias_days" build_full server "$@" ;; build-serverClient-full) verify_working_env [ -z "$alias_days" ] || \ export EASYRSA_CERT_EXPIRE="$alias_days" build_full serverClient "$@" ;; gen-crl) verify_working_env [ -z "$alias_days" ] || \ export EASYRSA_CRL_DAYS="$alias_days" gen_crl ;; revoke) verify_working_env cert_dir=issued revoke "$@" ;; revoke-expired) verify_working_env cert_dir=expired revoke "$@" ;; revoke-renewed) verify_working_env cert_dir=renewed/issued revoke "$@" ;; import-req) verify_working_env import_req "$@" ;; expire) verify_working_env expire_cert "$@" ;; inline) verify_working_env inline_file "$@" ;; export-p12) verify_working_env export_pkcs p12 "$@" ;; export-p7) verify_working_env export_pkcs p7 "$@" ;; export-p8) verify_working_env export_pkcs p8 "$@" ;; export-p1) verify_working_env export_pkcs p1 "$@" ;; set-pass|set-rsa-pass|set-ec-pass|set-ed-pass) verify_working_env set_pass "$@" ;; update-db) verify_working_env update_db ;; show-req) verify_working_env show req "$@" ;; show-cert) verify_working_env show cert "$@" ;; show-crl) verify_working_env show crl crl ;; show-ca) verify_working_env show_ca "$@" ;; show-host) verify_working_env show_host "$@" ;; renew|show-expire|show-revoke|show-renew|verify-cert) verify_working_env # easyrsa-tools.lib is required source_easyrsa_tools_lib || tools_error=1 case "$cmd" in renew) [ "$tools_error" ] && user_error "$tools_error_txt A certificate can be renewed without EasyRSA Tools. Expire the certificate using command 'expire' and sign the original request with 'sign-req'." [ -z "$alias_days" ] || \ export EASYRSA_CERT_EXPIRE="$alias_days" renew "$@" ;; show-expire) [ "$tools_error" ] && user_error "$tools_error_txt" [ -z "$alias_days" ] || \ export EASYRSA_PRE_EXPIRY_WINDOW="$alias_days" status expire "$@" ;; show-revoke) [ "$tools_error" ] && user_error "$tools_error_txt" status revoke "$@" ;; show-renew) [ "$tools_error" ] && user_error "$tools_error_txt" status renew "$@" ;; verify-cert) [ "$tools_error" ] && user_error "$tools_error_txt" # Called with --batch, this will return error # when the certificate fails verification. # Therefore, on error, exit with error. verify_cert "$@" || easyrsa_exit_with_error=1 ;; *) die "Unknown command: '$cmd'" esac ;; gen-tls-*) verify_working_env # easyrsa-tools.lib is required source_easyrsa_tools_lib || tools_error=1 [ "$tools_error" ] && user_error "$tools_error_txt" case "$cmd" in gen-tls-auth|gen-tls-auth-*) tls_key_gen tls-auth "$@" ;; gen-tls-crypt|gen-tls-crypt-*) tls_key_gen tls-crypt "$@" ;; gen-tls-cryptv2|gen-tls-cryptv2-*) tls_key_gen tls-crypt-v2 "$@" ;; *) die "Command '$cmd' not currently implemented." esac ;; write) verify_working_env # Write legacy files to write_dir # or EASYRSA_PKI or EASYRSA case "$1" in legacy) # over-write NO shift legacy_file_over_write= all_legacy_files_v2 "$@" ;; legacy-hard) # over-write YES shift legacy_file_over_write=overwrite all_legacy_files_v2 "$@" ;; *) write_legacy_file_v2 "$@" esac ;; serial|check-serial) verify_working_env # Called with --batch, this will return error # when the serial number is not unique. # Therefore, on error, exit with error. check_serial_unique "$@" || \ easyrsa_exit_with_error=1 ;; display-dn) verify_working_env display_dn "$@" ;; x509-eku|show-eku) verify_working_env ssl_cert_x509v3_eku "$@" || \ easyrsa_exit_with_error=1 ;; rand|random) easyrsa_random "$1" ;; ""|help|-h|--help|--usage) verify_working_env cmd_help "$1" ;; version) print_version ;; *) user_error "\ Unknown command '$cmd'. Run without commands for usage help." esac # Check for untrapped errors # shellcheck disable=SC2181 # Quote expand - pre-cleanup $? if [ $? = 0 ]; then # Do 'cleanup ok' on successful completion cleanup ok fi # Otherwise, exit with error print "Untrapped error detected!" cleanup # vim: ft=sh nu ai sw=8 ts=8 noet