#!/bin/bash # # crlpublisher.sh # # Copyright (C) 2012, Branko Majic # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # program="crlpublisher.sh" version="0.1.2" function usage() { cat <. EOF } function version() { cat < | | | | This program is free software: you can redistribute it and/or modify | | it under the terms of the GNU General Public License as published by | | the Free Software Foundation, either version 3 of the License, or | | (at your option) any later version. | | | | This program is distributed in the hope that it will be useful, | | but WITHOUT ANY WARRANTY; without even the implied warranty of | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | GNU General Public License for more details. | | | | You should have received a copy of the GNU General Public License | | along with this program. If not, see . | +-----------------------------------------------------------------------+ EOF } # # Helper function that parses a CRL. # # Sets: # crlIssuerDn - to the value of the CRL issuer's distinguished name. # crlNumber - to the value of CRL number. # crlFormat - to the type of CRL (PEM/DER). # crlLastUpdate - to the last update time of CRL. # crlNextUpdate - to the next update time of CRL. # function readCrlInfo() { local crlFile="$1" unset crlIssuerDn crlNumber crlFormat crlLastUpdate crlNextUpdate # Detect format of the CRL. if openssl crl -noout -inform DER -in "$crlFile" 2>/dev/null; then crlFormat="DER" elif openssl crl -noout -inform PEM -in "$crlFile" 2>/dev/null; then crlFormat="PEM" else echo "Invalid CRL file '$crlFile'" >&2 return 1 fi # Read the CRL information crlIssuerDn=$(openssl crl -issuer -inform "$crlFormat" -noout -in "$crlFile" | sed -e 's#^issuer=/##;s#/#,#g') # @TODO: The -crlnumber option was added only to more recent versions of OpenSSL. #crlNumber=$(echo "ibase=16;obase=A;$(openssl crl -crlnumber -inform "$crlFormat" -noout -in "$crlFile" | sed -e 's/crlNumber=//')" | bc) crlNumber=$(openssl crl -text -inform "$crlFormat" -noout -in "$crlFile" | grep -A1 'X509v3 CRL Number' | tail -n1 | grep -o '[[:digit:]]\+') crlLastUpdate=$(openssl crl -lastupdate -inform "$crlFormat" -noout -in "$crlFile" | sed -e 's/lastUpdate=//') crlNextUpdate=$(openssl crl -nextupdate -inform "$crlFormat" -noout -in "$crlFile" | sed -e 's/nextUpdate=//') return 0 } # # Helper function for clearing the common parameters. This function output # commands that should be executed in order to clear the parameters (by using # the "echo" command for example). # function clearCommonParameters() { echo "local issuerDn publisher" } # # Helper function for verifying the common parameters # function verifyCommonParameters() { if [[ -z "$issuerDn" ]]; then echo "Publisher ($configFile): missing issuer DN" >&2 return 1 fi if [[ -z "$publisher" ]]; then echo "Publisher ($configFile): missing publisher type." >&2 return 1 fi } # # Implementation of scp-based publisher. # function publish_through_scp() { local configFile="$1" crlFile="$2" local remoteUser="$USER" remoteHost remoteLocation privateKey="$HOME/.ssh/id_rsa" remotePort="22" $(clearCommonParameters) . "$configFile" verifyCommonParameters || return 1 if ! [[ -f $privateKey ]]; then echo "SCP publisher ($configFile): invalid private key - '$privateKey'." >&2 return 1 elif [[ -z $remoteHost ]]; then echo "SCP publisher ($configFile): missing 'remoteHost' option." >&2 return 1 elif [[ -z $remoteLocation ]]; then echo "SCP publisher ($configFile): missing 'remoteLocation' option." >&2 return 1 elif [[ -z "$remotePort" ]]; then echo "SCP publisher ($configFile): missing 'remotePort' option." >&2 return 1 elif [[ ! $remotePort =~ ^[[:digit:]]+$ ]]; then echo "SCP publisher ($configFile): invalid remote port - '$remotePort'." >&2 return 1 fi if [[ $issuerDn == $crlIssuerDn ]]; then if ! scp -P "$remotePort" -i "$privateKey" "$crlFile" "$remoteUser"@"$remoteHost":"$remoteLocation"; then echo "SCP publisher ($configFile): failed to publish CRL for '$crlIssuerDn'." >&2 return 2 fi fi } # # Implementation of archiver. # function publish_through_archiver() { local configFile="$1" crlFile="$2" local archiveDir crlLastUpdateParsed $(clearCommonParameters) . "$configFile" verifyCommonParameters || return 1 if ! [[ -d $archiveDir ]]; then echo "Archiver publisher ($configFile): invalid archive directory - '$archiveDir'" >&2 return 1 fi # Get the issuance date/time of the issued CRL. crlLastUpdateParsed=$(TZ=UTC date -d "$crlLastUpdate" +%F-%T:%z) # Set-up the filename. filename="${crlIssuerDn}_${crlLastUpdateParsed}_${crlNumber}.$crlFormat" # Copy the file. cp "$crlFile" "$archiveDir/$filename" } # If no arguments were given, just show usage help. if [[ -z $1 ]]; then usage exit 0 fi # Parse the arguments while getopts "c:vh" opt; do case "$opt" in c) configDir="$OPTARG";; v) version exit 0;; h) usage exit 0;; *) usage exit 1;; esac done i=$OPTIND shift $(($i-1)) # Figure out which configuration directory to use. if [[ -n $configDir && ! -d $configDir ]]; then echo "Specified configuration directory '$configDir' does not exist." >&2 exit 2 # If no configuration directory was provided, try one of the default ones. elif [[ -z $configDir ]]; then configDir="/etc/crlpublisher" [[ ! -d $configDir ]] && configDir="$HOME/.crlpublisher" if [[ ! -d $configDir ]]; then cat <&2 No configuration directory could be found. Please provide configuration directory path using the -c option, or create configuration directory and the necessary configuration files in one of the following locations: - /etc/crlpublisher/ - $HOME/crlpublisher/ EOF exit 2 fi fi # The first argument should be a CRL file crlFile="$1" # Obtain the issuer's DN first readCrlInfo "$crlFile" || exit $? # Assume that the operation suceeds unless the publisher fails for any reason. operationResult=0 # Process each configuration file while read configFile; do unset publisher eval "$(grep "^publisher=" "$configFile")" # Execute operation and if it failed, that's the status the script will # return. "publish_through_$publisher" "$configFile" "$crlFile" || operationResult="$?" done < <(find "$configDir" -name "*.conf") exit "$operationResult"