From b21a2f2a6d73d764fc819f5682994aa9d6243676 Mon Sep 17 00:00:00 2001 From: paige Date: Wed, 6 Nov 2024 20:10:25 +0000 Subject: [PATCH] add easyrsa --- Dockerfile | 13 +- custom/.gitignore | 3 +- docker-compose.yml | 5 - easyrsa3/.gitignore | 1 + easyrsa3/easyrsa | 6131 ++++++++++++++++++++++++++++++ easyrsa3/openssl-easyrsa.cnf | 149 + easyrsa3/vars | 15 + easyrsa3/x509-types/COMMON | 12 + easyrsa3/x509-types/ca | 12 + easyrsa3/x509-types/client | 7 + easyrsa3/x509-types/code-signing | 7 + easyrsa3/x509-types/email | 7 + easyrsa3/x509-types/kdc | 21 + easyrsa3/x509-types/server | 7 + easyrsa3/x509-types/serverClient | 7 + 15 files changed, 6379 insertions(+), 18 deletions(-) create mode 100644 easyrsa3/.gitignore create mode 100755 easyrsa3/easyrsa create mode 100644 easyrsa3/openssl-easyrsa.cnf create mode 100644 easyrsa3/vars create mode 100644 easyrsa3/x509-types/COMMON create mode 100644 easyrsa3/x509-types/ca create mode 100644 easyrsa3/x509-types/client create mode 100644 easyrsa3/x509-types/code-signing create mode 100644 easyrsa3/x509-types/email create mode 100644 easyrsa3/x509-types/kdc create mode 100644 easyrsa3/x509-types/server create mode 100644 easyrsa3/x509-types/serverClient diff --git a/Dockerfile b/Dockerfile index 1dadfd9..3731bb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,3 @@ -ARG BUILD_SERVER_NAME="irc.lame-network.local" - ARG UBUNTU_VERSION="noble" FROM ubuntu:${UBUNTU_VERSION} @@ -480,19 +478,10 @@ RUN touch /etc/inspircd/oper.motd.txt ADD GeoLite2-Country.mmdb /etc/inspircd -RUN openssl genrsa -out /etc/ssl/inspircd/server.key - -RUN openssl req -new -key /etc/ssl/inspircd/server.key -out /etc/ssl/inspircd/server.csr \ - -subj "/C=US/ST=Washington/L=Seattle/O=LameNetwork/OU=IT Department/CN=$BUILD_SERVER_NAME" - -RUN openssl x509 -req -days 365 -in /etc/ssl/inspircd/server.csr -signkey /etc/ssl/inspircd/server.key -out /etc/ssl/inspircd/server.crt - -RUN chown -R inspircd:inspircd /etc/inspircd /etc/ssl/inspircd /var/lib/inspircd /var/log/inspircd +RUN chown -R inspircd:inspircd /etc/inspircd /var/lib/inspircd /var/log/inspircd VOLUME /etc/inspircd/custom -VOLUME /etc/ssl/inspircd - VOLUME /var/lib/inspircd VOLUME /var/log/inspircd diff --git a/custom/.gitignore b/custom/.gitignore index d5da261..d34eb6a 100644 --- a/custom/.gitignore +++ b/custom/.gitignore @@ -3,5 +3,6 @@ oper.motd.txt include.conf *.crt *.key -dh.pem +*.pem !ca.crt +!crl.pem diff --git a/docker-compose.yml b/docker-compose.yml index f18f6d7..8bf6698 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,20 +2,15 @@ services: ircd: build: context: . - args: - BUILD_SERVER_NAME: ${SERVER_NAME} image: inspi4 network_mode: "host" env_file: "config.env" volumes: - ./custom:/etc/inspircd/custom:ro - - ssl:/etc/ssl/inspircd - data:/var/lib/inspircd - log:/var/log/inspircd volumes: data: name: inspi4_data - ssl: - name: inspi4_ssl log: name: inspi4_log diff --git a/easyrsa3/.gitignore b/easyrsa3/.gitignore new file mode 100644 index 0000000..993b775 --- /dev/null +++ b/easyrsa3/.gitignore @@ -0,0 +1 @@ +pki/ diff --git a/easyrsa3/easyrsa b/easyrsa3/easyrsa new file mode 100755 index 0000000..7ee87fa --- /dev/null +++ b/easyrsa3/easyrsa @@ -0,0 +1,6131 @@ +#!/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 diff --git a/easyrsa3/openssl-easyrsa.cnf b/easyrsa3/openssl-easyrsa.cnf new file mode 100644 index 0000000..391bcff --- /dev/null +++ b/easyrsa3/openssl-easyrsa.cnf @@ -0,0 +1,149 @@ +# For use with Easy-RSA 3.0+ and OpenSSL or LibreSSL + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = $ENV::EASYRSA_PKI # Where everything is kept +certs = $dir # Where the issued certs are kept +crl_dir = $dir # Where the issued crl are kept +database = $dir/index.txt # database index file. +new_certs_dir = $dir/certs_by_serial # default place for new certs. + +certificate = $dir/ca.crt # The CA certificate +serial = $dir/serial # The current serial number +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/ca.key # The private key +RANDFILE = $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 + +default_days = $ENV::EASYRSA_CERT_EXPIRE # how long to certify for +default_crl_days = $ENV::EASYRSA_CRL_DAYS # how long before next CRL +default_md = $ENV::EASYRSA_DIGEST # use public key default MD + +# Note: preserve=no|yes, does nothing for EasyRSA. +# Use sign-req command option 'preserve' instead. +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 = $ENV::EASYRSA_KEY_SIZE +default_keyfile = privkey.pem +default_md = $ENV::EASYRSA_DIGEST +distinguished_name = $ENV::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 = $ENV::EASYRSA_REQ_CN + +# Easy-RSA DN for org support: +[ org ] +countryName = Country Name (2 letter code) +countryName_default = $ENV::EASYRSA_REQ_COUNTRY +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = $ENV::EASYRSA_REQ_PROVINCE + +localityName = Locality Name (eg, city) +localityName_default = $ENV::EASYRSA_REQ_CITY + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = $ENV::EASYRSA_REQ_ORG + +organizationalUnitName = Organizational Unit Name (eg, section) +organizationalUnitName_default = $ENV::EASYRSA_REQ_OU + +commonName = Common Name (eg: your user, host, or server name) +commonName_max = 64 +commonName_default = $ENV::EASYRSA_REQ_CN + +emailAddress = Email Address +emailAddress_default = $ENV::EASYRSA_REQ_EMAIL +emailAddress_max = 64 + +serialNumber = Serial-number (eg, device serial-number) +serialNumber_default = $ENV::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 diff --git a/easyrsa3/vars b/easyrsa3/vars new file mode 100644 index 0000000..87df66b --- /dev/null +++ b/easyrsa3/vars @@ -0,0 +1,15 @@ +set_var EASYRSA_DN "org" +set_var EASYRSA_REQ_COUNTRY "US" +set_var EASYRSA_REQ_PROVINCE "California" +set_var EASYRSA_REQ_CITY "San Francisco" +set_var EASYRSA_REQ_ORG "SuperNETs" +set_var EASYRSA_REQ_EMAIL "no-reply@supernets.org" +set_var EASYRSA_REQ_OU "IRC" +set_var EASYRSA_PRESERVE_DN 1 +set_var EASYRSA_NO_PASS 1 +set_var EASYRSA_KEY_SIZE 4096 +set_var EASYRSA_ALGO rsa +set_var EASYRSA_CA_EXPIRE 3650 +set_var EASYRSA_CERT_EXPIRE 3650 +set_var EASYRSA_CRL_DAYS 180 +set_var EASYRSA_RAND_SN "yes" diff --git a/easyrsa3/x509-types/COMMON b/easyrsa3/x509-types/COMMON new file mode 100644 index 0000000..a9867db --- /dev/null +++ b/easyrsa3/x509-types/COMMON @@ -0,0 +1,12 @@ +# X509 extensions added to every signed cert + +# This file is included for every cert signed, and by default does nothing. +# It could be used to add values every cert should have, such as a CDP as +# demonstrated in the following example: + +#crlDistributionPoints = URI:http://example.net/pki/my_ca.crl + +# The authority information access extension gives details about how to access +# certain information relating to the CA. + +#authorityInfoAccess = caIssuers;URI:http://example.net/pki/my_ca.crt diff --git a/easyrsa3/x509-types/ca b/easyrsa3/x509-types/ca new file mode 100644 index 0000000..0eeeb20 --- /dev/null +++ b/easyrsa3/x509-types/ca @@ -0,0 +1,12 @@ +# X509 extensions for a ca + +# Note that basicConstraints will be overridden by Easy-RSA when defining a +# CA_PATH_LEN for CA path length limits. You could also do this here +# manually as in the following example in place of the existing line: +# +# basicConstraints = CA:TRUE, pathlen:1 + +basicConstraints = CA:TRUE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +keyUsage = cRLSign, keyCertSign diff --git a/easyrsa3/x509-types/client b/easyrsa3/x509-types/client new file mode 100644 index 0000000..aed0065 --- /dev/null +++ b/easyrsa3/x509-types/client @@ -0,0 +1,7 @@ +# X509 extensions for a client + +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +extendedKeyUsage = clientAuth +keyUsage = digitalSignature diff --git a/easyrsa3/x509-types/code-signing b/easyrsa3/x509-types/code-signing new file mode 100644 index 0000000..9a2115b --- /dev/null +++ b/easyrsa3/x509-types/code-signing @@ -0,0 +1,7 @@ +# X509 extensions for a client + +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +extendedKeyUsage = codeSigning +keyUsage = digitalSignature diff --git a/easyrsa3/x509-types/email b/easyrsa3/x509-types/email new file mode 100644 index 0000000..bc8f39e --- /dev/null +++ b/easyrsa3/x509-types/email @@ -0,0 +1,7 @@ +# X509 extensions for email + +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +extendedKeyUsage = emailProtection +keyUsage = digitalSignature,keyEncipherment,nonRepudiation diff --git a/easyrsa3/x509-types/kdc b/easyrsa3/x509-types/kdc new file mode 100644 index 0000000..702ec0a --- /dev/null +++ b/easyrsa3/x509-types/kdc @@ -0,0 +1,21 @@ +# X509 extensions for a KDC server certificate + +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} diff --git a/easyrsa3/x509-types/server b/easyrsa3/x509-types/server new file mode 100644 index 0000000..8c4d537 --- /dev/null +++ b/easyrsa3/x509-types/server @@ -0,0 +1,7 @@ +# X509 extensions for a server + +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +extendedKeyUsage = serverAuth +keyUsage = digitalSignature,keyEncipherment diff --git a/easyrsa3/x509-types/serverClient b/easyrsa3/x509-types/serverClient new file mode 100644 index 0000000..8a8a58e --- /dev/null +++ b/easyrsa3/x509-types/serverClient @@ -0,0 +1,7 @@ +# X509 extensions for a client/server + +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +extendedKeyUsage = serverAuth,clientAuth +keyUsage = digitalSignature,keyEncipherment