From f52b866ebdf894296d7dfc6f59655455e916bc55 Mon Sep 17 00:00:00 2001 From: Richard T Bonhomme Date: Sun, 22 May 2022 13:58:38 +0100 Subject: [PATCH] Improve revocation and renewal functions Changes: * Improve help * Move renew_restore_move() out of die() and back to renewal block. * Minor corrections to user output. * Add detailed description of which files will be moved/removed. * Simplify check/create revoked/renewed directory structures. * Only die on failure to move certificate, otherwise warn only. Some files may not be present. eg. PKCS files, already removed. Manually tested. Signed-off-by: Richard T Bonhomme --- easyrsa3/easyrsa | 207 +++++++++++++++++++++++++++++------------------ 1 file changed, 127 insertions(+), 80 deletions(-) diff --git a/easyrsa3/easyrsa b/easyrsa3/easyrsa index 992c4e6..e9675f5 100755 --- a/easyrsa3/easyrsa +++ b/easyrsa3/easyrsa @@ -191,10 +191,12 @@ cmd_help() { text=" * rewind-renew - Rewind EasyRSA version 3.0 style renewed certificates. + Rewind an EasyRSA version 3.0 'style' renewed certificate. + Once 'rewind' has completed the certificate can be revoked + by using: 'revoke-renewed [reason]' - Moves and renames certs, keys and reqs. Use with caution! - Ref: https://github.com/OpenVPN/easy-rsa/issues/578" + * NOTE: This does NOT 'unrenew' or 'unrevoke' a certificate. + Ref : https://github.com/OpenVPN/easy-rsa/issues/578" ;; gen-crl) text=" @@ -473,9 +475,6 @@ print() { printf "%s\n" "$*" || exit 1; } # Exit fatally with a message to stderr # present even with EASYRSA_BATCH as these are fatal problems die() { - # If renew failed then restore cert, key and req. Otherwise, issue a warning - # If *restore* fails then at least the file-names are not serial-numbers - [ "$restore_failed_renew" ] && renew_restore_move print " Easy-RSA error: @@ -1661,7 +1660,7 @@ 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. +Unable to verify request. The file is not a valid request. Unexpected input in file: $req_in" fi @@ -1687,9 +1686,22 @@ Cannot revoke this certificate because a conflicting file exists. unset -v deny_msg # confirm operation by displaying DN: + warn "\ +This process is destructive! + +These files will be moved to the 'revoked' storage sub-directory: +* $crt_in +* $key_in +* $req_in + +These files will be DELETED: +* All PKCS files for commonName : $file_name_base +* The inline credentials file : $creds_in +* The duplicate certificate : $duplicate_crt_by_serial" + confirm " Continue with revocation: " "yes" "\ - Please confirm you wish to revoke the certificate - with the following subject: +Please confirm you wish to revoke the certificate +with the following subject: $(display_dn x509 "$crt_in") @@ -1717,14 +1729,14 @@ infrastructure in order to prevent the revoked certificate from being accepted." # moves revoked certificates to the 'revoked' folder # allows reissuing certificates with the same name revoke_move() { - # make sure revoked dirs exist - 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" + for target in "$out_dir" \ + "$out_dir/certs_by_serial" \ + "$out_dir/private_by_serial" \ + "$out_dir/reqs_by_serial" + do + [ -d "$target" ] && continue + mkdir -p "$target" || + die "Failed to mkdir: $target" done # move crt, key and req file to renewed_then_revoked folders @@ -1732,40 +1744,41 @@ revoke_move() { # only move the key if we have it if [ -e "$key_in" ]; then - mv "$key_in" "$key_out" || die "Failed to move: $key_in" + mv "$key_in" "$key_out" || warn "Failed to move: $key_in" fi # only move the req if we have it if [ -e "$req_in" ]; then - mv "$req_in" "$req_out" || die "Failed to move: $req_in" + mv "$req_in" "$req_out" || warn "Failed to move: $req_in" fi - # move any pkcs files + # remove any pkcs files for pkcs in p12 p7b p8 p1; do if [ -e "$in_dir/issued/$file_name_base.$pkcs" ]; then # issued - mv "$in_dir/issued/$file_name_base.$pkcs" \ - "$out_dir/certs_by_serial/$cert_serial.$pkcs" \ - || die "Failed to move: $file_name_base.$pkcs" + rm "$in_dir/issued/$file_name_base.$pkcs" || + warn "Failed to remove: $file_name_base.$pkcs" elif [ -e "$in_dir/private/$file_name_base.$pkcs" ]; then # private - mv "$in_dir/private/$file_name_base.$pkcs" \ - "$out_dir/private_by_serial/$cert_serial.$pkcs" \ - || die "Failed to move: $file_name_base.$pkcs" + rm "$in_dir/private/$file_name_base.$pkcs" || + warn "Failed to remove: $file_name_base.$pkcs" else : # ok fi done # remove the duplicate certificate in the certs_by_serial folder - rm "$duplicate_crt_by_serial" || warn \ - "Failed to remove the duplicate certificate in the certs_by_serial folder" + if [ -e "$duplicate_crt_by_serial" ]; then + rm "$duplicate_crt_by_serial" || warn "\ +Failed to remove the duplicate certificate in the certs_by_serial folder" + fi # remove credentials file (if exists) if [ -e "$creds_in" ]; then confirm "Remove inline file ? " "yes" "An inline file exists. $creds_in" - rm "$creds_in" || warn "Failed to remove the inline file." + rm "$creds_in" || warn "\ +Failed to remove inline file: $creds_in" fi return 0 @@ -1818,7 +1831,7 @@ 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. +Unable to verify request. The file is not a valid request. Unexpected input in file: $req_in" fi @@ -1884,9 +1897,22 @@ subjectAltName = $san" fi # confirm operation by displaying DN: + warn "\ +This process is destructive! + +These files will be moved to the 'renewed' storage sub-directory: +* $crt_in +* $key_in +* $req_in + +These files will be DELETED: +* All PKCS files for commonName : $file_name_base +* The inline credentials file : $creds_in +* The duplicate certificate : $duplicate_crt_by_serial" + confirm " Continue with renewal: " "yes" "\ - Please confirm you wish to renew the certificate - with the following subject: +Please confirm you wish to renew the certificate +with the following subject: $(display_dn x509 "$crt_in") @@ -1896,34 +1922,42 @@ subjectAltName = $san" # move renewed files so we can reissue certificate with the same name renew_move - # Set restore on error flag - restore_failed_renew=1 - # renew certificate - build_full "$cert_type" "$file_name_base" "$opt_nopass" || die "\ -Failed to renew certificate: renew command failed." + if build_full "$cert_type" "$file_name_base" "$opt_nopass"; then + : # ok + else + # If renew failed then restore cert, key and req. Otherwise, issue a warning + # If *restore* fails then at least the file-names are not serial-numbers + renew_restore_move + die "\ +Renewal has failed to build a new certificate/key pair." + fi # Success messages notice " * IMPORTANT * -Renew was successful. To revoke the old certificate once the new one has been -deployed, use 'revoke-renewed $file_name_base'" +Renew was successful. To revoke the old certificate, once the new one +has been deployed, use 'revoke-renewed $file_name_base'" return 0 } # => renew() # Restore files on failure to renew renew_restore_move() { - unset -v restore_failed_renew rrm_err + unset -v rrm_err # restore crt, key and req file to PKI folders - if ! mv "$restore_crt_out" "$restore_crt_in"; then + if mv "$restore_crt_out" "$restore_crt_in"; then + : # ok + else warn "Failed to restore: $restore_crt_out" rrm_err=1 fi # only restore the key if we have it if [ -e "$restore_key_out" ]; then - if ! mv "$restore_key_out" "$restore_key_in"; then + if mv "$restore_key_out" "$restore_key_in"; then + : # ok + else warn "Failed to restore: $restore_key_out" rrm_err=1 fi @@ -1931,18 +1965,19 @@ renew_restore_move() { # only restore the req if we have it if [ -e "$restore_req_out" ]; then - if ! mv "$restore_req_out" "$restore_req_in"; then + if mv "$restore_req_out" "$restore_req_in"; then + : # ok + else warn "Failed to restore: $restore_req_out" rrm_err=1 fi fi # messages - [ "$EASYRSA_SILENT" ] || print # Separate Notice below if [ "$rrm_err" ]; then warn "Failed to restore renewed files." else - notice "Renewed files have been restored." + notice "Renewed files have been successfully restored." fi return 0 @@ -1953,13 +1988,14 @@ renew_restore_move() { # allows reissuing certificates with the same name renew_move() { # make sure renewed dirs exist - 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" + for target in "$out_dir" \ + "$out_dir/issued" \ + "$out_dir/private" \ + "$out_dir/reqs" + do + [ -d "$target" ] && continue + mkdir -p "$target" || + die "Failed to mkdir: $target" done # move crt, key and req file to renewed folders @@ -1971,27 +2007,27 @@ renew_move() { restore_key_in="$key_in" restore_key_out="$key_out" if [ -e "$key_in" ]; then - mv "$key_in" "$key_out" || die "Failed to move: $key_in" + mv "$key_in" "$key_out" || warn "Failed to move: $key_in" fi # only move the req if we have it restore_req_in="$req_in" restore_req_out="$req_out" if [ -e "$req_in" ]; then - mv "$req_in" "$req_out" || die "Failed to move: $req_in" + mv "$req_in" "$req_out" || warn "Failed to move: $req_in" fi # remove any pkcs files for pkcs in p12 p7b p8 p1; do if [ -e "$in_dir/issued/$file_name_base.$pkcs" ]; then # issued - rm "$in_dir/issued/$file_name_base.$pkcs" \ - || die "Failed to remove: $file_name_base.$pkcs" + rm "$in_dir/issued/$file_name_base.$pkcs" || + warn "Failed to remove: $file_name_base.$pkcs" elif [ -e "$in_dir/private/$file_name_base.$pkcs" ]; then # private - rm "$in_dir/private/$file_name_base.$pkcs" \ - || die "Failed to remove: $file_name_base.$pkcs" + rm "$in_dir/private/$file_name_base.$pkcs" || + warn "Failed to remove: $file_name_base.$pkcs" else : # ok fi @@ -1999,13 +2035,12 @@ renew_move() { # remove the duplicate certificate in the certs_by_serial folder if [ -e "$duplicate_crt_by_serial" ]; then - rm "$duplicate_crt_by_serial" || warn \ - "Failed to remove the duplicate certificate in the certs_by_serial folder" + rm "$duplicate_crt_by_serial" || warn "\ +Failed to remove the duplicate certificate in the certs_by_serial folder" fi # remove credentials file (if exists) if [ -e "$creds_in" ]; then - confirm "Remove inline file ? " "yes" "An inline file exists. $creds_in" rm "$creds_in" || warn "Failed to remove the inline file." fi @@ -2068,7 +2103,7 @@ 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. +Unable to verify request. The file is not a valid request. Unexpected input in file: $req_in" fi @@ -2077,6 +2112,7 @@ Unexpected input in file: $req_in" || die "renew-revoked - Failed to retrieve certificate serial number" # 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" # output out_dir="$EASYRSA_PKI/revoked" @@ -2094,6 +2130,19 @@ Cannot revoke this certificate because a conflicting file exists. unset -v deny_msg # confirm operation by displaying DN: + warn "\ +This process is destructive! + +These files will be moved to the 'revoked' storage sub-directory: +* $crt_in +* $key_in +* $req_in + +These files will be DELETED: +* All PKCS files for commonName : $file_name_base +* The inline credentials file : $creds_in +* The duplicate certificate : $duplicate_crt_by_serial" + confirm " Continue with revocation: " "yes" "\ Please confirm you wish to revoke the renewed certificate with the following subject: @@ -2124,13 +2173,14 @@ infrastructure in order to prevent the revoked certificate from being accepted." # moves renewed then revoked certificates to the 'revoked' folder revoke_renewed_move() { # make sure revoked dirs exist - 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" + for target in "$out_dir" \ + "$out_dir/certs_by_serial" \ + "$out_dir/private_by_serial" \ + "$out_dir/reqs_by_serial" + do + [ -d "$target" ] && continue + mkdir -p "$target" || + die "Failed to mkdir: $target" done # move crt, key and req file to renewed_then_revoked folders @@ -2138,27 +2188,25 @@ revoke_renewed_move() { # only move the key if we have it if [ -e "$key_in" ]; then - mv "$key_in" "$key_out" || die "Failed to move: $key_in" + mv "$key_in" "$key_out" || warn "Failed to move: $key_in" fi # only move the req if we have it if [ -e "$req_in" ]; then - mv "$req_in" "$req_out" || die "Failed to move: $req_in" + mv "$req_in" "$req_out" || warn "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 # issued - mv "$in_dir/issued/$file_name_base.$pkcs" \ - "$out_dir/certs_by_serial/$cert_serial.$pkcs" \ - || die "Failed to move: $file_name_base.$pkcs" + rm "$in_dir/issued/$file_name_base.$pkcs" || + warn "Failed to remove: $file_name_base.$pkcs" elif [ -e "$in_dir/private/$file_name_base.$pkcs" ]; then # private - mv "$in_dir/private/$file_name_base.$pkcs" \ - "$out_dir/private_by_serial/$cert_serial.$pkcs" \ - || die "Failed to move: $file_name_base.$pkcs" + rm "$in_dir/private/$file_name_base.$pkcs" || + warn "Failed to remove: $file_name_base.$pkcs" else : # ok fi @@ -2234,7 +2282,7 @@ 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. +Unable to verify request. The file is not a valid request. Unexpected input in file: $req_in" fi @@ -2242,7 +2290,7 @@ Unexpected input in file: $req_in" crt_cn="$( easyrsa_openssl x509 -in "$crt_in" -noout -subject -nameopt \ utf8,multiline | grep '^[[:blank:]]*commonName[[:blank:]]*= ' - )" + )" || die "Failed to find commonName in certificate" crt_cn="${crt_cn#*= }" # Set out_dir @@ -2270,7 +2318,6 @@ Cannot renew this certificate because a conflicting file exists. else # Attempt restore mv -f "$crt_out" "$crt_in" - mv -f "$key_out" "$key_in" 2>/dev/null die "Failed to move: $key_in" fi fi