diff --git a/easyrsa3/easyrsa b/easyrsa3/easyrsa index be5272c..9fb7d04 100755 --- a/easyrsa3/easyrsa +++ b/easyrsa3/easyrsa @@ -34,6 +34,7 @@ Here is the list of commands available with a short syntax reminder. Use the build-client-full [ cmd-opts ] build-server-full [ cmd-opts ] revoke [cmd-opts] + revoke-renewed [cmd-opts] renew [cmd-opts] build-serverClient-full [ cmd-opts ] gen-crl @@ -113,6 +114,17 @@ cmd_help() { revoke) text=" revoke [reason] Revoke a certificate specified by the filename_base, with an optional + revocation reason that is one of: + unspecified + keyCompromise + CACompromise + affiliationChanged + superseded + cessationOfOperation + certificateHold";; + revoke-renewed) text=" + revoke-renewed [reason] + Revoke a renewed certificate specified by its old certificate_serial, with an optional revocation reason that is one of: unspecified keyCompromise @@ -827,7 +839,7 @@ install_data_to_pki () { # Note that A && B || C is not if-then-else. C may run when A is true # shellcheck disable=SC2015 [ -n "$EASYRSA_EXT_DIR" ] && [ -e "$EASYRSA_EXT_DIR" ] || \ - die "x509-types folder cannot be found" + die "x509-types folder cannot be found: $EASYRSA_EXT_DIR" # Complete or error [ -e "$EASYRSA_SAFE_CONF" ] || easyrsa_openssl makesafeconf @@ -910,8 +922,7 @@ current CA keypair. If you intended to start a new CA, run init-pki first." # create necessary files and dirs: err_file="Unable to create necessary PKI files (permissions?)" for i in issued certs_by_serial \ - revoked/certs_by_serial revoked/private_by_serial revoked/reqs_by_serial \ - renewed/certs_by_serial renewed/private_by_serial renewed/reqs_by_serial; + revoked/certs_by_serial revoked/private_by_serial revoked/reqs_by_serial; do mkdir -p "$EASYRSA_PKI/$i" || die "$err_file" done @@ -1392,7 +1403,12 @@ Run easyrsa without commands for usage and command help." # Assign file_name_base and dust off! file_name_base="$1" shift - crt_in="$EASYRSA_PKI/issued/$file_name_base.crt" + + in_dir="$EASYRSA_PKI" + crt_in="$in_dir/issued/$file_name_base.crt" + key_in="$in_dir/private/$file_name_base.key" + req_in="$in_dir/reqs/$file_name_base.req" + creds_in="$in_dir/$file_name_base.creds" # Assign possible "crl_reason" if [ "$1" ]; then @@ -1436,6 +1452,20 @@ $(display_dn x509 "$crt_in") Unable to revoke as the input file is not a valid certificate. Unexpected input in file: $crt_in" + # Verify request + if [ -e "$req_in" ] + then + verify_file req "$req_in" || die "\ +Unable to move request. The file is not a valid request. +Unexpected input in file: $req_in" + fi + + # get the serial number of the certificate -> serial=XXXX + cert_serial="$(easyrsa_openssl x509 -in "$crt_in" -noout -serial)" + # remove the serial= part -> we only need the XXXX part + cert_serial="${cert_serial##*=}" + duplicate_crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem" + # Revoke certificate easyrsa_openssl ca -utf8 -revoke "$crt_in" \ ${crl_reason+ -crl_reason "$crl_reason"} \ @@ -1443,7 +1473,7 @@ input in file: $crt_in" || die "Failed to revoke certificate: revocation command failed." # move revoked files so we can reissue certificates with the same name - move_revoked "$file_name_base" + move_revoked [ "$EASYRSA_SILENT" ] || print # Separate Notice below notice "\ @@ -1460,72 +1490,57 @@ infrastructure in order to prevent the revoked cert from being accepted." # moves revoked certificates to an alternative folder # allows reissuing certificates with the same name move_revoked() { - verify_ca_init + # 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" - [ -n "$1" ] || die "\ -Error: didn't find a file base name as the first argument. -Run easyrsa without commands for usage and command help." - - crt_in="$EASYRSA_PKI/issued/$1.crt" - key_in="$EASYRSA_PKI/private/$1.key" - req_in="$EASYRSA_PKI/reqs/$1.req" - creds_in="$EASYRSA_PKI/$1.creds" - - verify_file x509 "$crt_in" || die "\ -Unable to move revoked input file. The file is not a valid certificate. -Unexpected input in file: $crt_in" - - if [ -e "$req_in" ] - then - verify_file req "$req_in" || die "\ -Unable to move request. The file is not a valid request. -Unexpected input in file: $req_in" - fi - - # get the serial number of the certificate -> serial=XXXX - cert_serial="$(easyrsa_openssl x509 -in "$crt_in" -noout -serial)" - # remove the serial= part -> we only need the XXXX part - cert_serial="${cert_serial##*=}" - - crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem" - crt_by_serial_revoked="$EASYRSA_PKI/revoked/certs_by_serial/$cert_serial.crt" - key_by_serial_revoked="$EASYRSA_PKI/revoked/private_by_serial/$cert_serial.key" - req_by_serial_revoked="$EASYRSA_PKI/revoked/reqs_by_serial/$cert_serial.req" + # NEVER over-write a revoked cert, serial number must be unique + [ -e "$crt_out" ] && die "revoked exists: $crt_out" + [ -e "$key_out" ] && die "revoked exists: $key_out" + [ -e "$req_out" ] && die "revoked exists: $req_out" # make sure revoked dirs exist - [ -d "$EASYRSA_PKI/revoked" ] || \ - mkdir "$EASYRSA_PKI/revoked" - [ -d "$EASYRSA_PKI/revoked/certs_by_serial" ] || \ - mkdir "$EASYRSA_PKI/revoked/certs_by_serial" - [ -d "$EASYRSA_PKI/revoked/private_by_serial" ] || \ - mkdir "$EASYRSA_PKI/revoked/private_by_serial" - [ -d "$EASYRSA_PKI/revoked/reqs_by_serial" ] || \ - mkdir "$EASYRSA_PKI/revoked/reqs_by_serial" + if [ ! -d "$out_dir" ]; then + mkdir -p "$out_dir" || die "Failed to mkdir: $out_dir" + fi + for target in certs_by_serial private_by_serial reqs_by_serial; do + [ -d "$out_dir/$target" ] && continue + mkdir -p "$out_dir/$target" \ + || die "Failed to mkdir: $out_dir/$target" + done - # move crt, key and req file to revoked folders - mv "$crt_in" "$crt_by_serial_revoked" - - # only move the req if we have it - [ -e "$req_in" ] && mv "$req_in" "$req_by_serial_revoked" + # move crt, key and req file to renewed_then_revoked folders + mv "$crt_in" "$crt_out" || die "Failed to move: $crt_in" # only move the key if we have it - [ -e "$key_in" ] && mv "$key_in" "$key_by_serial_revoked" + if [ -e "$key_in" ]; then + mv "$key_in" "$key_out" || die "Failed to move: $key_in" + fi - # move the rest of the files (p12, p7, ...) - for file in "$EASYRSA_PKI/private/$1"\.??? - do - # get file extension - file_ext="${file##*.}" + # only move the req if we have it + if [ -e "$req_in" ]; then + mv "$req_in" "$req_out" || die "Failed to move: $req_in" + fi - if [ -f "$file" ]; then - mv "$file" \ - "$EASYRSA_PKI/revoked/private_by_serial/$cert_serial.$file_ext" \ - || die "Failed to move file: $file" + # move any pkcs files + for pkcs in p12 p7b p8 p1; do + if [ -e "$in_dir/issued/$file_name_base.$pkcs" ]; then + mv "$in_dir/issued/$file_name_base.$pkcs" \ + "$out_dir/certs_by_serial/$cert_serial.$pkcs" \ + || die "Failed to move: $file_name_base.$pkcs" + elif [ -e "$in_dir/private/$file_name_base.$pkcs" ]; then + mv "$in_dir/private/$file_name_base.$pkcs" \ + "$out_dir/private_by_serial/$cert_serial.$pkcs" \ + || die "Failed to move: $file_name_base.$pkcs" + else + : # ok fi done # remove the duplicate certificate in the certs_by_serial folder - rm "$crt_by_serial" || warn \ + rm "$duplicate_crt_by_serial" || warn \ "Failed to remove the duplicate certificate in the certs_by_serial folder" # remove credentials file (if exists) @@ -1542,23 +1557,39 @@ renew() { verify_ca_init # pull filename base: - [ -n "$1" ] || die "\ + [ "$1" ] || die "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and command help." - crt_in="$EASYRSA_PKI/issued/$1.crt" + + # Assign file_name_base and dust off! + file_name_base="$1" + shift + + in_dir="$EASYRSA_PKI" + crt_in="$in_dir/issued/$file_name_base.crt" + key_in="$in_dir/private/$file_name_base.key" + req_in="$in_dir/reqs/$file_name_base.req" + creds_in="$in_dir/$file_name_base.creds" # Upgrade CA index.txt.attr - unique_subject = no up23_upgrade_ca || die "Failed to upgrade CA to support renewal." - # Append 'nopass' + # Set 'nopass' opt_nopass="" - if [ "$2" ]; then - opt_nopass="$2" + if [ "$1" ]; then + opt_nopass="$1" + shift fi - verify_file x509 "$crt_in" || die "\ -Unable to renew as the input file is not a valid certificate. Unexpected -input in file: $crt_in" + # Enforce syntax + if [ "$1" ]; then + die "Syntax error: $1" + fi + + # referenced cert must exist: + [ -f "$crt_in" ] || die "\ +Unable to renew as no certificate was found. Certificate was expected +at: $crt_in" # confirm operation by displaying DN: confirm "Continue with renew: " "yes" " @@ -1567,15 +1598,29 @@ Please confirm you wish to renew the certificate with the following subject: $(display_dn x509 "$crt_in") " # => confirm end - # referenced cert must exist: - [ -f "$crt_in" ] || die "\ -Unable to renew as no certificate was found. Certificate was expected -at: $crt_in" + # Verify certificate + verify_file x509 "$crt_in" || die "\ +Unable to renew as the input file is not a valid certificate. Unexpected +input in file: $crt_in" + + # Verify request + if [ -e "$req_in" ] + then + verify_file req "$req_in" || die "\ +Unable to move request. The file is not a valid request. +Unexpected input in file: $req_in" + fi + + # get the serial number of the certificate -> serial=XXXX + cert_serial="$(easyrsa_openssl x509 -in "$crt_in" -noout -serial)" + # remove the serial= part -> we only need the XXXX part + cert_serial="${cert_serial##*=}" + duplicate_crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem" # Check if old cert is expired or expires within 30 cert_expire_date="$( - easyrsa_openssl x509 -in "$crt_in" -noout -enddate | - sed 's/^notAfter=//' + easyrsa_openssl x509 -in "$crt_in" -noout -enddate | \ + sed 's/^notAfter=//' )" case "$easyrsa_uname" in @@ -1638,11 +1683,10 @@ subjectAltName = $san" fi # move renewed files so we can reissue certificate with the same name - # FIXME: Modify revoke() to also work on the renewed certs subdir - move_renewed "$1" + move_renewed # renew certificate - build_full "$cert_type" "$1" "$opt_nopass" || die "\ + build_full "$cert_type" "$file_name_base" "$opt_nopass" || die "\ Failed to renew certificate: renew command failed." [ "$EASYRSA_SILENT" ] || print # Separate Notice below @@ -1660,72 +1704,57 @@ You may want to revoke the old certificate once the new one has been deployed." # moves renewed certificates to an alternative folder # allows reissuing certificates with the same name move_renewed() { - verify_ca_init + # Set out_dir + out_dir="$EASYRSA_PKI/renewed" + crt_out="$out_dir/issued/$file_name_base.crt" + key_out="$out_dir/private/$file_name_base.key" + req_out="$out_dir/reqs/$file_name_base.req" - [ -n "$1" ] || die "\ -Error: didn't find a file base name as the first argument. -Run easyrsa without commands for usage and command help." - - crt_in="$EASYRSA_PKI/issued/$1.crt" - key_in="$EASYRSA_PKI/private/$1.key" - req_in="$EASYRSA_PKI/reqs/$1.req" - creds_in="$EASYRSA_PKI/$1.creds" - - verify_file x509 "$crt_in" || die "\ -Unable to move renewed input file. The file is not a valid certificate. -Unexpected input in file: $crt_in" - - if [ -e "$req_in" ] - then - verify_file req "$req_in" || die "\ -Unable to move request. The file is not a valid request. -Unexpected input in file: $req_in" - fi - - # get the serial number of the certificate -> serial=XXXX - cert_serial="$(easyrsa_openssl x509 -in "$crt_in" -noout -serial)" - # remove the serial= part -> we only need the XXXX part - cert_serial="${cert_serial##*=}" - - crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem" - crt_by_serial_renewed="$EASYRSA_PKI/renewed/certs_by_serial/$cert_serial.crt" - key_by_serial_renewed="$EASYRSA_PKI/renewed/private_by_serial/$cert_serial.key" - req_by_serial_renewed="$EASYRSA_PKI/renewed/reqs_by_serial/$cert_serial.req" + # NEVER over-write a renewed cert, revoke it first + [ -e "$crt_out" ] && die "renewed exists: $crt_out" + [ -e "$key_out" ] && die "renewed exists: $key_out" + [ -e "$req_out" ] && die "renewed exists: $req_out" # make sure renewed dirs exist - [ -d "$EASYRSA_PKI/renewed" ] || \ - mkdir "$EASYRSA_PKI/renewed" - [ -d "$EASYRSA_PKI/renewed/certs_by_serial" ] || \ - mkdir "$EASYRSA_PKI/renewed/certs_by_serial" - [ -d "$EASYRSA_PKI/renewed/private_by_serial" ] || \ - mkdir "$EASYRSA_PKI/renewed/private_by_serial" - [ -d "$EASYRSA_PKI/renewed/reqs_by_serial" ] || \ - mkdir "$EASYRSA_PKI/renewed/reqs_by_serial" + if [ ! -d "$out_dir" ]; then + mkdir -p "$out_dir" || die "Failed to mkdir: $out_dir" + fi + for target in issued private reqs; do + [ -d "$out_dir/$target" ] && continue + mkdir -p "$out_dir/$target" \ + || die "Failed to mkdir: $out_dir/$target" + done # move crt, key and req file to renewed folders - mv "$crt_in" "$crt_by_serial_renewed" - - # only move the req if we have it - [ -e "$req_in" ] && mv "$req_in" "$req_by_serial_renewed" + mv "$crt_in" "$crt_out" || die "Failed to move: $crt_in" # only move the key if we have it - [ -e "$key_in" ] && mv "$key_in" "$key_by_serial_renewed" + if [ -e "$key_in" ]; then + mv "$key_in" "$key_out" || die "Failed to move: $key_in" + fi - # move the rest of the files (p12, p7, ...) - for file in "$EASYRSA_PKI/private/$1"\.??? - do - # get file extension - file_ext="${file##*.}" + # only move the req if we have it + if [ -e "$req_in" ]; then + mv "$req_in" "$req_out" || die "Failed to move: $req_in" + fi - if [ -f "$file" ]; then - mv "$file" \ - "$EASYRSA_PKI/renewed/private_by_serial/$cert_serial.$file_ext" \ - || die "Failed to move file: $file" + # move any pkcs files + for pkcs in p12 p7b p8 p1; do + if [ -e "$in_dir/issued/$file_name_base.$pkcs" ]; then + mv "$in_dir/issued/$file_name_base.$pkcs" \ + "$out_dir/issued/$file_name_base.$pkcs" \ + || die "Failed to move: $file_name_base.$pkcs" + elif [ -e "$in_dir/private/$file_name_base.$pkcs" ]; then + mv "$in_dir/private/$file_name_base.$pkcs" \ + "$out_dir/private/$file_name_base.$pkcs" \ + || die "Failed to move: $file_name_base.$pkcs" + else + : # ok fi done # remove the duplicate certificate in the certs_by_serial folder - rm "$crt_by_serial" || warn \ + rm "$duplicate_crt_by_serial" || warn \ "Failed to remove the duplicate certificate in the certs_by_serial folder" # remove credentials file (if exists) @@ -1737,6 +1766,155 @@ Unexpected input in file: $req_in" return 0 } #= move_renewed() +# revoke-renewed backend +revoke_renewed() { + verify_ca_init + + # pull filename base: + [ "$1" ] || die "\ +Error: didn't find a file base name as the first argument. +Run easyrsa without commands for usage and command help." + + # Assign file_name_base and dust off! + file_name_base="$1" + shift + + in_dir="$EASYRSA_PKI/renewed" + crt_in="$in_dir/issued/$file_name_base.crt" + key_in="$in_dir/private/$file_name_base.key" + req_in="$in_dir/reqs/$file_name_base.req" + #creds_in="$EASYRSA_PKI/$file_name_base.creds" + + # Assign possible "crl_reason" + if [ "$1" ]; then + crl_reason="$1" + shift + + case "$crl_reason" in + unspecified | \ + keyCompromise |\ + CACompromise | \ + affiliationChanged | \ + superseded | \ + cessationOfOperation | \ + certificateHold ) : # ok + ;; + *) die "Illegal reason: $crl_reason" + esac + else + unset -v crl_reason + fi + + # Enforce syntax + if [ "$1" ]; then + die "Syntax error: $1" + fi + + # referenced cert must exist: + [ -f "$crt_in" ] || die "\ +Unable to revoke as no renewed certificate was found. Certificate was expected +at: $crt_in" + + # confirm operation by displaying DN: + confirm "Continue with revocation: " "yes" " +Please confirm you wish to revoke the renewed certificate with serial $1 and the following subject: + +$(display_dn x509 "$crt_in") +" + + # Verify certificate + verify_file x509 "$crt_in" || die "\ +Unable to revoke as the certificate serial does not match an old one of a valid renewed certificate. Unexpected +certificate in file: $crt_in" + + # Verify request + if [ -e "$req_in" ] + then + verify_file req "$req_in" || die "\ +Unable to move renewed then revoked request. The file is not a valid request. Unexpected +input in file: $req_in" + fi + + # get the serial number of the certificate -> serial=XXXX + cert_serial="$(easyrsa_openssl x509 -in "$crt_in" -noout -serial)" \ + || die "renew-revoked - Failed to retrieve certificate serial number" + # remove the serial= part -> we only need the XXXX part + cert_serial="${cert_serial##*=}" + + # shellcheck disable=SC2086 + easyrsa_openssl ca -utf8 -revoke "$crt_in" \ + ${crl_reason:+ -crl_reason "$crl_reason"} \ + ${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \ + || die "Failed to revoke renewed certificate: revocation command failed." + + # move revoked files + move_renewed_revoked + + notice "\ +IMPORTANT!!! + +Revocation was successful. You must run gen-crl and upload a CRL to your +infrastructure in order to prevent the revoked renewed cert from being accepted. +" # => notice end + + return 0 +} #= revoke_renewed() + +# move-renewed-revoked +# moves renewed then revoked certificates to an alternative folder +move_renewed_revoked() { + # output + out_dir="$EASYRSA_PKI/revoked" + crt_out="$out_dir/certs_by_serial/$cert_serial.crt" + key_out="$out_dir/private_by_serial/$cert_serial.key" + req_out="$out_dir/reqs_by_serial/$cert_serial.req" + + # NEVER over-write a revoked cert, serial number must be unique + [ -e "$crt_out" ] && die "revoked exists: $crt_out" + [ -e "$key_out" ] && die "revoked exists: $key_out" + [ -e "$req_out" ] && die "revoked exists: $req_out" + + # make sure revoked dirs exist + if [ ! -d "$out_dir" ]; then + mkdir -p "$out_dir" || die "move_renewed_revoked - Failed to mkdir: $out_dir" + fi + for target in certs_by_serial private_by_serial reqs_by_serial; do + [ -d "$out_dir/$target" ] && continue + mkdir -p "$out_dir/$target" \ + || die "Failed to mkdir: $out_dir/$target" + done + + # move crt, key and req file to renewed_then_revoked folders + mv "$crt_in" "$crt_out" || die "Failed to move: $crt_in" + + # only move the key if we have it + if [ -e "$key_in" ]; then + mv "$key_in" "$key_out" || die "Failed to move: $key_in" + fi + + # only move the req if we have it + if [ -e "$req_in" ]; then + mv "$req_in" "$req_out" || die "Failed to move: $req_in" + fi + + # move any pkcs files + for pkcs in p12 p7b p8 p1; do + if [ -e "$in_dir/issued/$file_name_base.$pkcs" ]; then + mv "$in_dir/issued/$file_name_base.$pkcs" \ + "$out_dir/certs_by_serial/$cert_serial.$pkcs" \ + || die "Failed to move: $file_name_base.$pkcs" + elif [ -e "$in_dir/private/$file_name_base.$pkcs" ]; then + mv "$in_dir/private/$file_name_base.$pkcs" \ + "$out_dir/private_by_serial/$cert_serial.$pkcs" \ + || die "Failed to move: $file_name_base.$pkcs" + else + : # ok + fi + done + + return 0 +} # => move_renewed_revoked() + # gen-crl backend gen_crl() { verify_ca_init @@ -3165,6 +3343,9 @@ case "$cmd" in revoke) revoke "$@" ;; + revoke-renewed) + revoke_renewed "$@" + ;; renew) renew "$@" ;;