%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /backups/router/usr/local/bin/
Upload File :
Create Path :
Current File : //backups/router/usr/local/bin/easyrsa-tools.lib

#!/bin/sh

# Easy-RSA 3 Tools library
#
# 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.

# Easy-RSA 3.x does not source into the environment directly.
# Complain if a user tries to do this:
if [ -z "$EASYRSA_TOOLS_CALLER" ]; then
	return 1
fi

# Set tools version
export EASYRSA_TOOLS_VERSION=321

# Verify OpenVPN binary
verify_openvpn() {
	# Try to find openvpn
	set_var EASYRSA_OPENVPN "$(which openvpn)"
	if [ -f "$EASYRSA_OPENVPN" ]; then
		verbose \
			"verify_openvpn - EASYRSA_OPENVPN='$EASYRSA_OPENVPN'"
	else
		user_error "Cannot find an OpenVPN binary."
	fi
} # => verify_openvpn()

# OpenVPN TLS Auth/Crypt Key
tls_key_gen() {
	case "$1" in
		tls-crypt-v2)
			print "Unavailable."
			cleanup
		;;
		tls-crypt) tls_key_type=TLS-CRYPT ;;
		tls-auth) tls_key_type=TLS-AUTH ;;
		*) die "Unknown key type: '$1'"
	esac

	# Over write error message
	tls_key_error_msg="
If this file is changed then it MUST be redistributed to ALL servers
AND clients, to be in effect. Do NOT change this existing file."

	# Assign possible TLS key sources
	tls_key_file="$EASYRSA_PKI"/private/easyrsa-tls.key
	old_tls_key_file="$EASYRSA_PKI"/easyrsa-keepsafe-tls.key

	# Forbid overwrite - default TLS key
	if [ -f "$tls_key_file" ]; then
		tls_key_data="$(cat "$tls_key_file")"
		case "$tls_key_data" in
			*'TLS-CRYPT'*) tls_key_type=TLS-CRYPT ;;
			*'TLS-AUTH'*) tls_key_type=TLS-AUTH ;;
			*) tls_key_type=UNKNOWN
		esac

		user_error "\
Cannot overwrite existing $tls_key_type Key:
* $tls_key_file
$tls_key_error_msg"
	fi

	# Forbid overwrite - Old TLS key
	if [ -f "$old_tls_key_file" ]; then
		old_tls_key_data="$(cat "$old_tls_key_file")"
		case "$old_tls_key_data" in
			*'TLS-CRYPT'*) tls_key_type=TLS-CRYPT ;;
			*'TLS-AUTH'*) tls_key_type=TLS-AUTH ;;
			*) tls_key_type=UNKNOWN
		esac

		user_error "\
Cannot overwrite existing $tls_key_type Key:
* $old_tls_key_file
$tls_key_error_msg"
	fi

	verify_openvpn

	tls_key_tmp=
	easyrsa_mktemp tls_key_tmp || \
		die "tls_key_gen - easyrsa_mktemp tls_key_tmp"

	# Generate TLS Key
	"$EASYRSA_OPENVPN" --genkey "$1" "$tls_key_tmp" || \
		die "tls_key_gen - --genkey $tls_key_type FAIL"

	# Insert type label
	{
		print "# Easy-RSA $tls_key_type Key"
		cat "$tls_key_tmp"
	} > "$tls_key_file" || \
			die "tls_key_gen - Insert type label FAIL"

	notice "\
$tls_key_type Key generated at:
* $tls_key_file
$tls_key_error_msg"
	verbose "tls_key_gen: openvpn --genkey $tls_key_type OK"
} # => tls_key_gen()

# Get certificate start date
# shellcheck disable=2317 # Unreach - ssl_cert_not_before_date()
ssl_cert_not_before_date() {
	verbose "DEPRECATED: ssl_cert_not_before_date()"
	[ "$#" = 2 ] || die "\
ssl_cert_not_before_date - input error"
	[ -f "$1" ] || die "\
ssl_cert_not_before_date - missing cert"

	fn_ssl_out="$(
		"$EASYRSA_OPENSSL" x509 -in "$1" -noout -startdate
		)" || die "\
ssl_cert_not_before_date - failed: -startdate"

	fn_ssl_out="${fn_ssl_out#*=}"

	force_set_var "$2" "$fn_ssl_out" || die "\
ssl_cert_not_before_date - failed to set var '$*'"

	unset -v fn_ssl_out
} # => ssl_cert_not_before_date()

# Get certificate end date
ssl_cert_not_after_date() {
	verbose "DEPRECATED: ssl_cert_not_after_date()"
	[ "$#" = 2 ] || die "\
ssl_cert_not_after_date - input error"
	[ -f "$1" ] || die "\
ssl_cert_not_after_date - missing cert"

	fn_ssl_out="$(
		"$EASYRSA_OPENSSL" x509 -in "$1" -noout -enddate
		)" || die "\
ssl_cert_not_after_date - failed: -enddate"

	fn_ssl_out="${fn_ssl_out#*=}"

	force_set_var "$2" "$fn_ssl_out" || die "\
ssl_cert_not_after_date - failed to set var '$*'"

	unset -v fn_ssl_out
} # => ssl_cert_not_after_date()

# SSL -- v3 -- startdate iso_8601
# shellcheck disable=2317 # Unreach - iso_8601_cert_startdate()
iso_8601_cert_startdate() {
	verbose "NEW: iso_8601_cert_startdate"
	[ "$#" = 2 ] || die "\
iso_8601_cert_startdate: input error"
	[ -f "$1" ] || die "\
iso_8601_cert_startdate: missing cert"

	# On error return, let the caller decide what to do
	if fn_ssl_out="$(
		"$EASYRSA_OPENSSL" x509 -in "$1" -noout \
			-startdate -dateopt iso_8601
		)"
	then
		: # ok
	else
		# The caller MUST assess this error
		verbose "\
iso_8601_cert_startdate: GENERATED ERROR"
		return 1
	fi

	fn_ssl_out="${fn_ssl_out#*=}"

	force_set_var "$2" "$fn_ssl_out" || die "\
iso_8601_cert_startdate: failed to set var '$*'"

	unset -v fn_ssl_out
} # => iso_8601_cert_startdate()

# SSL -- v3 -- enddate iso_8601
iso_8601_cert_enddate() {
	verbose "NEW: iso_8601_cert_enddate"
	[ "$#" = 2 ] || die "\
iso_8601_cert_enddate: input error"
	[ -f "$1" ] || die "\
iso_8601_cert_enddate: missing cert"

	# On error return, let the caller decide what to do
	if fn_ssl_out="$(
		"$EASYRSA_OPENSSL" x509 -in "$1" -noout \
			-enddate -dateopt iso_8601
		)"
	then
		: # ok
	else
		# The caller MUST assess this error
		verbose "\
iso_8601_cert_enddate: GENERATED ERROR"
		return 1
	fi

	fn_ssl_out="${fn_ssl_out#*=}"

	force_set_var "$2" "$fn_ssl_out" || die "\
iso_8601_cert_enddate: failed to set var '$*'"

	unset -v fn_ssl_out
} # => iso_8601_cert_enddate()

# Number of days from NOW@today as timestamp seconds
days_to_timestamp_s() {
	verbose "REQUIRED: days_to_timestamp_s: uses date"
	# check input
	[ "$#" = 2 ] || die "\
days_to_timestamp_s: input error"

	in_days="$1"
	in_seconds="$(( in_days * 60 * 60 * 24 ))"

	# There are NO OS dependencies for this use of date
	# OS dependencies
	# Linux and Windows
	# date.exe does not allow +%s as input
	# MacPorts GNU date
	if timestamp_s="$(
			date +%s 2>/dev/null
			)"
	then : # ok

	# Darwin, BSD
	elif timestamp_s="$(
			date +%s 2>/dev/null
			)"
	then : # ok

	# busybox
	elif timestamp_s="$(
			busybox date +%s 2>/dev/null
			)"
	then : # ok

	# Something else
	else
		die "\
days_to_timestamp_s: 'date +%s' failed"
	fi

	# Add period
	timestamp_s="$(( timestamp_s + in_seconds ))"

	# Return timestamp_s
	force_set_var "$2" "$timestamp_s" || die "\
days_to_timestamp_s: force_set_var - $2 - $timestamp_s"

	unset -v in_days in_seconds timestamp_s
} # => days_to_timestamp_s()

# Convert certificate date to timestamp seconds since epoch
# Used to verify iso_8601 calculated seconds since epoch
cert_date_to_timestamp_s() {
	verbose "DEPRECATED: cert_date_to_timestamp_s"
	# check input
	[ "$#" = 2 ] || die "\
cert_date_to_timestamp_s: input error"

#die "* NOT ALLOWED: cert_date_to_timestamp_s()"

	in_date="$1"

	# OS dependencies
	# Linux and Windows
	# date.exe does not allow +%s as input
	# MacPorts GNU date
	if timestamp_s="$(
			date -d "$in_date" +%s \
				2>/dev/null
			)"
	then : # ok

	# Darwin, BSD
	elif timestamp_s="$(
			date -j -f '%b %d %T %Y %Z' \
				"$in_date" +%s 2>/dev/null
			)"
	then : # ok

	# busybox
	elif timestamp_s="$(
			busybox date -D "%b %e %H:%M:%S %Y" \
				-d "$in_date" +%s 2>/dev/null
			)"
	then : # ok

	# Something else
	else
		die "\
cert_date_to_timestamp_s:
'date' failed for in_date=$in_date"
	fi

	# Return timestamp_s
	force_set_var "$2" "$timestamp_s" || die "\
cert_date_to_timestamp_s: force_set_var - $2 - $timestamp_s"

	unset -v in_date timestamp_s
} # => cert_date_to_timestamp_s()

# Build a Windows date.exe compatible input field
# iso_8601 date
db_date_to_iso_8601_date() {
	verbose "iso_8601: db_date_to_iso_8601_date"
	# check input
	[ "$#" = 2 ] || die "\
db_date_to_iso_8601_date - input error"

	# Expected format: '230612235959Z'
	in_date="$1"
	verbose "db_date_to_iso_8601_date: in_date=$in_date"

	# Consume $in_date string
	# yyyy is expected to be only 'yy'
	yyyy="${in_date%???????????}"
	in_date="${in_date#"$yyyy"}"

	# When yyyy is only two digits prepend century
	if [ "${#yyyy}" = 2 ]; then
		yyyy="${yyyy#0}"
		if [ "$yyyy" -lt 70 ]; then
			if [ "${#yyyy}" = 2 ]; then
				yyyy="20${yyyy}"
			else
				yyyy="200${yyyy}"
			fi
		else
			if [ "${#yyyy}" = 2 ]; then
				yyyy="19${yyyy}"
			else
				yyyy="190${yyyy}"
			fi
		fi
	fi
	verbose "db_date_to_iso_8601_date: yyyy=$yyyy"

	mm="${in_date%?????????}"
	in_date="${in_date#"$mm"}"
	dd="${in_date%???????}"
	in_date="${in_date#"$dd"}"
	HH="${in_date%?????}"
	in_date="${in_date#"$HH"}"
	MM="${in_date%???}"
	in_date="${in_date#"$MM"}"
	SS="${in_date%?}"
	in_date="${in_date#"$SS"}"
	TZ="$in_date"

	# Assign iso_8601 date
	out_date="${yyyy}-${mm}-${dd} ${HH}:${MM}:${SS}${TZ}"
	verbose "db_date_to_iso_8601_date: out_date=$out_date"

	# Return out_date
	force_set_var "$2" "$out_date" || die "\
db_date_to_iso_8601_date: force_set_var - $2 - $out_date"

	unset -v in_date out_date yyyy mm dd HH MM SS TZ
} # => db_date_to_iso_8601_date()

# Certificate expiry
will_cert_be_valid() {
	[ -f "$1" ] || die "will_cert_be_valid - Missing file"
	case "$2" in (*[!1234567890]*|0*)
		die "will_cert_be_valid - Non-decimal" ;;
	esac

	# is the cert still valid at this future date
	"$EASYRSA_OPENSSL" x509 -in "$1" -noout -checkend "$2"
} # => will_cert_be_valid()

# SC2295: Expansion inside ${..} need to be quoted separately,
# otherwise they match as patterns. (what-ever that means ;-)
# Unfortunately, Windows sh.exe has an weird bug.
# Try in sh.exe: t='   '; s="a${t}b${t}c"; echo "${s%%"${t}"*}"

# Read db
# shellcheck disable=SC2295 # nested expand - read_db()
read_db() {
	TCT='	' # tab character
	db_in="$EASYRSA_PKI/index.txt"
	pki_r_issued="$EASYRSA_PKI/renewed/issued"
	pki_r_by_sno="$EASYRSA_PKI/renewed/certs_by_serial"
	unset -v target_found

	while read -r db_status db_notAfter db_record; do

		verbose "***** Read next record *****"

		# Recreate temp session
		remove_secure_session || \
			die "read_db - remove_secure_session"
		locate_support_files
		secure_session || \
			die "read_db - secure_session"
		# Recreate openssl-easyrsa.cnf (Temp)
		write_global_safe_ssl_cnf_tmp

		# Interpret the db/certificate record
		unset -v db_serial db_cn db_revoke_date db_reason
		case "$db_status" in
		V|E)
			# Valid
			db_serial="${db_record%%${TCT}*}"
			db_record="${db_record#*${TCT}}"
			db_cn="${db_record#*/CN=}"; db_cn="${db_cn%%/*}"
			cert_issued="$EASYRSA_PKI/issued/$db_cn.crt"
			cert_r_issued="$pki_r_issued/$db_cn.crt"
			cert_r_by_sno="$pki_r_by_sno/$db_serial.crt"
		;;
		R)
			# Revoked
			db_revoke_date="${db_record%%${TCT}*}"
			db_reason="${db_revoke_date#*,}"
			if [ "$db_reason" = "$db_revoke_date" ]; then
				db_reason="None given"
			else
				db_revoke_date="${db_revoke_date%,*}"
			fi
			db_record="${db_record#*${TCT}}"

			db_serial="${db_record%%${TCT}*}"
			db_record="${db_record#*${TCT}}"
			db_cn="${db_record#*/CN=}"; db_cn="${db_cn%%/*}"
		;;
		*) die "Unexpected status: $db_status"
		esac

		# Output selected status report for this record
		case "$report" in
		expire)
		# Certs which expire before EASYRSA_PRE_EXPIRY_WINDOW days
			case "$db_status" in
			V|E)
				case "$target" in
				'') expire_status_v2 "$cert_issued" ;;
				*)
					if [ "$target" = "$db_cn" ]; then
						expire_status_v2 "$cert_issued"
					fi
				esac
			;;
			*)
				: # Ignore ok
			esac
		;;
		revoke)
		# Certs which have been revoked
			case "$db_status" in
			R)
				case "$target" in
				'') revoke_status ;;
				*)
					if [ "$target" = "$db_cn" ]; then
						revoke_status
					fi
				esac
			;;
			*)
				: # Ignore ok
			esac
		;;
		renew)
		# Certs which have been renewed but not revoked
			case "$db_status" in
			V|E)
				case "$target" in
				'') renew_status ;;
				*)
					if [ "$target" = "$db_cn" ]; then
						renew_status
					fi
				esac
			;;
			*)
				: # Ignore ok
			esac
		;;
		*) die "Unrecognised report: $report"
		esac

		# Is db record for target found
		if [ "$target" = "$db_cn" ]; then
			target_found=1
		fi

	done < "$db_in"

	# Add CA to show-expire
	case  "$report" in
	expire)
		# Extract -endate
		ca_enddate="$(
			"$EASYRSA_OPENSSL" x509 -in "$EASYRSA_PKI"/ca.crt \
				-noout -enddate
		)"
		ca_enddate="${ca_enddate#*=}"

		# Check CA for expiry
		if will_cert_be_valid "$EASYRSA_PKI"/ca.crt \
			"$pre_expire_window_s" 1>/dev/null
		then
			: # cert will still be valid by expiry window
		else
			# Print CA expiry date
			printf '%s%s\n' \
				"CA certificate will expire on $ca_enddate"
		fi
	esac

	# Check for target found/valid commonName, if given
	if [ "$target" ]; then
		[ "$target_found" ] || \
			warn "Certificate for $target was not found"
	fi
} # => read_db()

# Expire status
expire_status_v2() {
	# expiry seconds
	pre_expire_window_s="$((
		EASYRSA_PRE_EXPIRY_WINDOW * 60*60*24
		))"

	# The certificate for CN should exist but may not
	if [ -f "$1" ]; then
		verbose "expire_status: cert exists"

		if will_cert_be_valid "$1" "$pre_expire_window_s" \
			 1>/dev/null
		then
			: # cert will still be valid by expiry window
		else
			# cert will expire
			# ISO8601 date - OpenSSL v3 only
			if ! iso_8601_cert_enddate "$1" cert_not_after_date \
					2>/dev/null
			then
				# Standard date - OpenSSL v1
				ssl_cert_not_after_date "$1" cert_not_after_date
			fi

			# show expiring cert details
			printf '%s%s\n' \
				"$db_status | Serial: $db_serial | " \
				"$cert_not_after_date | CN: $db_cn"
		fi
	else
		: # issued cert does not exist, ignore other certs
	fi
} # => expire_status_v2()

# Revoke status
revoke_status() {
	# Translate db date to usable date
	cert_revoke_date=
	db_date_to_iso_8601_date "$db_revoke_date" cert_revoke_date

	printf '%s%s%s\n' \
		"$db_status | Serial: $db_serial | " \
		"Revoked: $cert_revoke_date | " \
		"Reason: $db_reason | CN: $db_cn"
} # => revoke_status()

# Renewed status
# renewed certs only remain in the renewed folder until revoked
# Only ONE renewed cert with unique CN can exist in renewed folder
renew_status() {
	# Does a Renewed cert exist ?
	# files in issued are file name, or in serial are SerialNumber
	unset -v \
		cert_file_in cert_is_issued cert_is_serial renew_is_old

	# Find renewed/issued/CN
	if [ -f "$cert_r_issued" ]; then
		cert_file_in="$cert_r_issued"
		cert_is_issued=1
	fi

	# Find renewed/cert_by_serial/SN
	if [ -f "$cert_r_by_sno" ]; then
		cert_file_in="$cert_r_by_sno"
		cert_is_serial=1
		renew_is_old=1
	fi

	# Both should not exist
	if [ "$cert_is_issued" ] && [ "$cert_is_serial" ]; then
		die "Too many certs"
	fi

	# If a renewed cert exists
	if [ "$cert_file_in" ]; then
		# get the serial number of the certificate
		ssl_cert_serial "$cert_file_in" cert_serial

		# db serial must match certificate serial, otherwise
		# this is an issued cert that replaces a renewed cert
		if [ "$db_serial" != "$cert_serial" ]; then
			information "\
serial mismatch:
  db_serial:    $db_serial
  cert_serial:  $cert_serial
  cert_file_in: $cert_file_in"
			return 0
		fi

		# Use cert date
		# Assigns cert_not_after_date
		ssl_cert_not_after_date \
			"$cert_file_in" cert_not_after_date

		# Highlight renewed/cert_by_serial
		if [ "$renew_is_old" ]; then
			printf '%s%s\n' \
				"*** $db_status | Serial: $db_serial | " \
				"Expires: $cert_not_after_date | CN: $db_cn"
		else
			printf '%s%s\n' \
				"$db_status | Serial: $db_serial | " \
				"Expires: $cert_not_after_date | CN: $db_cn"
		fi

	else
		# Cert is valid but not renewed
		: # ok - ignore
	fi
} # => renew_status()

# cert status reports
status() {
	[ "$#" -gt 0 ] || die "status - input error"
	report="$1"
	target="$2"

	# test fix: https://github.com/OpenVPN/easy-rsa/issues/819
	export LC_TIME=C.UTF-8

	# If no target file then add Notice
	if [ -z "$target" ]; then
		# Select correct Notice
		case "$report" in
		expire)
			notice "\
* Showing certificates which expire in less than \
$EASYRSA_PRE_EXPIRY_WINDOW days (--days):"
		;;
		revoke)
			notice "\
* Showing certificates which are revoked:"
		;;
		renew)
			notice "\
* Showing certificates which have been renewed but NOT revoked:

*** Marks those which require 'rewind-renew' \
before they can be revoked."
		;;
		*) warn "Unrecognised report: $report"
		esac
	fi

	# Create report
	read_db
} # => status()

# renew backend
renew() {
	# 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

	# Assign input files
	in_dir="$EASYRSA_PKI"
	crt_in="$in_dir/issued/${file_name_base}.crt"
	key_in="$in_dir/private/${file_name_base}.key"
	req_in="$in_dir/reqs/${file_name_base}.req"
	creds_in="$in_dir/${file_name_base}.creds"
	inline_in="$in_dir/inline/${file_name_base}.inline"

	# Upgrade CA index.txt.attr - unique_subject = no
	print 'unique_subject = no' > "$EASYRSA_PKI/index.txt.attr" || \
		die "Failed to upgrade CA to support renewal."

	# deprecate ALL options
	while [ "$1" ]; do
		case "$1" in
			nopass)
				warn "\
Option 'nopass' is not supported by command 'renew'."
			;;
			*) 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

	# Verify request
	if [ -f "$req_in" ]; then
		verify_file req "$req_in" || user_error "\
Input file is not a valid request:
* $req_in"
	else
		user_error "\
Missing request file:
* $req_in"
	fi

	# Get cert commonName
	cert_CN="$(
			display_dn x509 "$crt_in" | grep 'commonName'
		)" || die "renew - display_dn of cert failed"

	# Get req commonName
	req_CN="$(
			display_dn req "$req_in" | grep 'commonName'
		)" || die "renew - display_dn of req failed"

	# For renew, cert_CN must match req_CN
	[ "$cert_CN" = "$req_CN" ] || user_error \
		"Certificate cannot be renewed due to commonName mismatch"
	verbose "renew - cert_CN MATCH req_CN"

	# get the serial number of the certificate
	ssl_cert_serial "$crt_in" cert_serial || \
		die "$cmd: Failed to get cert serial number!"

	# Duplicate cert by serial file
	dup_dir="$EASYRSA_PKI/certs_by_serial"
	dup_crt_by_serial="$dup_dir/${cert_serial}.pem"

	# Set out_dir
	out_dir="$EASYRSA_PKI/renewed"
	crt_out="$out_dir/issued/${file_name_base}.crt"

	# NEVER over-write a renewed cert, revoke it first
	deny_msg="\
Cannot renew this certificate, a conflicting file exists:
*"
	[ -f "$crt_out" ] && \
		user_error "$deny_msg certificate: $crt_out"
	unset -v deny_msg

	# Make inline directory
	[ -d "$EASYRSA_PKI/inline" ] ||	\
		mkdir -p "$EASYRSA_PKI/inline" || \
			die "Failed to create inline directoy."

	# Extract certificate usage from old cert
	ssl_cert_x509v3_eku "$crt_in" cert_type

	# Use SAN from old cert ONLY
	if grep 'X509v3 Subject Alternative Name' "$crt_in"; then
		EASYRSA_SAN="$(
			"$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -text | \
				grep -A 1 'X509v3 Subject Alternative Name' | \
					sed -e s/'^\ *'// \
						-e /'X509v3 Subject Alternative Name'/d
		)" || die "renew - EASYRSA_SAN: easyrsa_openssl subshell"
		verbose "renew: EASYRSA_SAN: ${EASYRSA_SAN}"

		# --san-crit
		unset -v EASYRSA_SAN_CRIT
		if grep -q 'X509v3 Subject Alternative Name: critical' \
			"$crt_in"
		then
			export EASYRSA_SAN_CRIT='critical,'
			verbose "renew: --san-crit ENABLED"
		fi

		export EASYRSA_EXTRA_EXTS="\
$EASYRSA_EXTRA_EXTS
subjectAltName = ${EASYRSA_SAN_CRIT}${EASYRSA_SAN}"
		verbose "renew: EASYRSA_EXTRA_EXTS: ${EASYRSA_EXTRA_EXTS}"
	fi

	# --bc-crit
	if grep -q 'X509v3 Basic Constraints: critical' "$crt_in"
	then
		export EASYRSA_BC_CRIT=1
		verbose "renew: --bc-crit ENABLED"
	fi

	# --ku-crit
	if grep -q 'X509v3 Key Usage: critical' "$crt_in"
	then
		export EASYRSA_KU_CRIT=1
		verbose "renew: --ku-crit ENABLED"
	fi

	# --eku-crit
	if grep -q 'X509v3 Extended Key Usage: critical' "$crt_in"
	then
		export EASYRSA_EKU_CRIT=1
		verbose "renew: --eku-crit ENABLED"
	fi

	# Disable options not supported by renew
	unset -v EASYRSA_CP_EXTS EASYRSA_AUTO_SAN EASYRSA_NEW_SUBJECT

	# confirm operation by displaying Warning
	confirm "Continue with 'renew' ? " yes "\
WARNING: This process is destructive!

These files will be MOVED to the 'renewed' sub-directory:
* $crt_in

These files will be DELETED:
All PKCS files for commonName: $file_name_base

The inline credentials files:
* $creds_in
* $inline_in"

	# move renewed files
	# so we can reissue certificate with the same name
	renew_move
	error_undo_renew_move=1

	# Set to modify sign-req confirmation message
	local_request=1

	# renew certificate
	# EASYRSA_BATCH=1
	if sign_req "$cert_type" "$file_name_base"
	then
		unset -v error_undo_renew_move
	else
		# If renew failed then restore cert.
		# Otherwise, issue a warning
		renew_restore_move
		die "Renewal has failed to build a new certificate."
	fi

	# inline it
	# Over write existing because renew is successful
	if inline_creds "$file_name_base" > "$inline_in"
	then
		notice "\
Inline file created:
* $inline_in"
	else
		warn "\
INCOMPLETE Inline file created:
* $inline_in"
	fi

	# Success messages
	notice "\
Renew was successful.

                    * IMPORTANT *

Renew has created a new certificate, to replace the old one.

To revoke the old certificate, once the new one has been deployed,
use command 'revoke-renewed $file_name_base'"
} # => renew()

# Restore files on failure to renew
renew_restore_move() {
	# restore crt file to PKI folders
	rrm_err=
	if mv "$restore_crt_out" "$restore_crt_in"; then
		: # ok
	else
		warn "Failed to restore: $restore_crt_in"
		rrm_err=1
	fi

	# messages
	if [ "$rrm_err" ]; then
		warn "Failed to restore renewed files."
	else
		notice "\
Renew FAILED but files have been successfully restored."
	fi
} # => renew_restore_move()

# renew_move
# moves renewed certificates to the 'renewed' folder
# allows reissuing certificates with the same name
renew_move() {
	# make sure renewed dirs exist
	easyrsa_mkdir "$out_dir"
	easyrsa_mkdir "$out_dir"/issued

	# move crt to renewed folders
	# After this point, renew is possible!
	restore_crt_in="$crt_in"
	restore_crt_out="$crt_out"
	mv "$crt_in" "$crt_out" || \
		die "Failed to move: $crt_in"

	# Remove files that can be recreated:
	# remove any pkcs files
	for pkcs in p12 p7b p8 p1; do
		# issued
		rm -f "$in_dir/issued/$file_name_base.$pkcs"
		# private
		rm -f "$in_dir/private/$file_name_base.$pkcs"
	done

	# remove credentials file
	if [ -f "$creds_in" ]; then
		rm "$creds_in" || warn "\
Failed to remove credentials file:
* $creds_in"
	fi

	# remove inline file
	if [ -f "$inline_in" ]; then
		rm "$inline_in" || warn "\
Failed to remove inline file:
* $inline_in"
	fi
} # => renew_move()

# Verify certificate against CA
verify_cert() {
	# pull filename base:
	[ "$1" ] || user_error "\
Error: didn't find a <file-name-base> 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

	# function opts support
	while [ "$1" ]; do
		case "$1" in
			# batch flag, return status [0/1] to calling
			# program.  Otherwise, exit 0 on completion.
			batch) EASYRSA_BATCH=1 ;;
			*) warn "Ignoring unknown command option: '$1'"
		esac
		shift
	done

	in_dir="$EASYRSA_PKI"
	ca_crt="$in_dir/ca.crt"
	crt_in="$in_dir/issued/$file_name_base.crt"

	# Cert file must exist
	[ -f "$crt_in" ] || user_error "\
No certificate found for the input:
* '$crt_in'"

	# Verify file is a valid cert
	verify_file x509 "$crt_in" || user_error "\
Input is not a valid certificate:
* $crt_in"

	# Silent SSL or not
	if [ "$EASYRSA_SILENT_SSL" ]; then
		# Test SSL out
		# openssl direct call because error is expected
		if "$EASYRSA_OPENSSL" verify \
			-CAfile "$ca_crt" "$crt_in" >/dev/null
		then
			verify_cert_ok=1
		else
			unset -v verify_cert_ok
		fi
	else
		if "$EASYRSA_OPENSSL" verify \
			-CAfile "$ca_crt" "$crt_in"
		then
			verify_cert_ok=1
		else
			unset -v verify_cert_ok
		fi
	fi

	# Return cert status
	if [ "$verify_cert_ok" ]; then
		notice "\
  Certificate name:    $file_name_base
  Verification status: GOOD"
	else
		notice "\
  Certificate name:    $file_name_base
  Verification status: FAILED"

		# Exit with error (batch mode)
		if [ "$EASYRSA_BATCH" ]; then
			# exit with error at cleanup
			easyrsa_exit_with_error=1
			# Return error for internal callers
			return 1
		fi
	fi
} # => verify_cert()

# vim: ft=sh nu ai sw=8 ts=8 noet

Zerion Mini Shell 1.0