#!/bin/bash # # ldapcrl_checkserial.sh # # Copyright (C) 2013, PrimeKey Solutions AB # # 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 <http://www.gnu.org/licenses/>. # program="ldapcrl_checkserial.sh" version="0.1" function usage() { cat <<EOF $program $version, a semi-interactive utility for checking if a CRL in LDAP contains a specific serial number. Usage: $program [OPTIONS] issuer serial $program is a semi-interactive utility for checking if a CRL in LDAP contains a specific serial number. It is useful for verifying that new CRLs are being published to the LDAP when revoking certificates. The utility is non-interactive unless the user is required to provide credentials for the ldapsearch utility. The script expects two positional arguments: issuer The issuer argument should be set to the issuer DN of the certification authority that has issued the CRL. This parameter will be used for locating the CRLs in the directory tree that have been issued by target CA. serial The serial argument should be set to the serial number of the certificate that has been revoked. The certificate should have been issued by the specified issuer. This serial number will be looked-up in the matching CRLs. The script works by traversing the LDAP tree, looking for all entries that have a CRL attribute. By default the script is expecting the CRLs to be located in the attribute 'certificateRevocationList' (this can be overridden). Each CRL will be checked for presence of the specified serial number. Once the script is finished traversing the LDAP tree, it will output a summary about the number of processed CRLs, and an informative message about whether the check has failed or not. The script will return a non-zero error code if the check has failed. The check is considred as failed if one of the following conditions are met: - There was no CRL in the LDAP direcotry issued by the specified CA. - At least one CRL in the LDAP direcotry that was issued by the specified CA does not contain the specified serial number. The check is considred as successful if and only if there was at least on CRL issued by the specified CA in the LDAP directory, and all of the CRLs issued by the specified CA in the LDAP directory contain the specified serial number. The script relies on using the ldapsearch utility from OpenLDAP for traversing the LDAP directory. Any configuration settings specified in the global or local (user's) LDAP client configuration file will be applied during search. Additional parameters can be passed to the ldapsearch utility as well by using the -L flag. $program accepts the following options: -L ldapop Pass additional option to the ldapsearch utility used for retrieving the CRL from the LDAP database. This option can be specified multiple times. -a attr LDAP attribute that should contain the CRL. Default is 'certificateRevocationList'. -v show script version and licensing information -h show usage help Please report bugs and send feature requests to <branko.majic@primekey.se>. EOF } function version() { cat <<EOF $program, version $version +-----------------------------------------------------------------------+ | Copyright (C) 2013, PrimeKey Solutions AB | | | | 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 <http://www.gnu.org/licenses/>. | +-----------------------------------------------------------------------+ EOF } # If no arguments were given, just show usage help. if [[ -z $1 ]]; then usage exit 0 fi # Set-up default parameters ldapOptions=() crlAttribute="certificateRevocationList" # Parse the arguments while getopts "L:a:vh" opt; do case "$opt" in L) ldapOptions+=("$OPTARG");; a) crlAttribute="$OPTARG";; v) version exit 0;; h) usage exit 0;; *) usage exit 1;; esac done i=$OPTIND shift $(($i-1)) # Read the positional arguments. issuer="$1" serial="$2" # Verify the arguments. if [[ -z $issuer ]]; then echo "Issuer was not specified." >&2 exit 3 fi if [[ -z $serial ]]; then echo "Serial number was not specified." >&2 exit 3 fi if [[ ! $serial =~ ^[0123456789abcdefABCDEF]+$ ]]; then echo "Invalid serial number specified: '$serial'" >&2 echo "Serial number may contain the following characters: 0123456789abcdefABCDEF" >&2 exit 3 fi # Start assembling the ldapsearch command. Remove the cruft from output # (comments etc), and also disable wrapping of long line.x command=("ldapsearch" "-LLL" "-oldif-wrap=no") # Pass the user-provided options. command+=("${ldapOptions[@]}") # Look only for entities that have the request CRL attribute, and return only # that attribute. command+=("(${crlAttribute}=*)") command+=("${crlAttribute}") # Perform the ldap search. Store the result. Bail-out if an error has happened. if ! searchResult=$("${command[@]}"); then echo "Failed to perform ldapsearch with the provided options." >&2 exit 10 fi # Keep track of how many CRLs from the issuer have been processed, how many of # those did contain (matched) the serial number, and how many of those did _not_ # contain the serial number (non-matches). processed=0 matches=0 nonMatches=0 # Output a useful begin message. echo "CRL Issuer: $issuer" echo "Serial number: $serial" echo # Parse the LDAP search result. while read line; do # Have we encountered the DN attribute? if [[ $line =~ ^dn: ]]; then dn="${line#*: }" # Have we encountered the CRL? elif [[ $line =~ ^$crlAttribute ]]; then crl="${line#*:: }" # If we have reached a blank line, that means we can process our DN and CRL now. elif [[ $line =~ ^$ && dn != "" && crl != "" ]]; then # Get the CRL issuer from the list. crlIssuer=$(echo $crl | base64 --decode | openssl crl -noout -inform DER -issuer | sed -e 's#^issuer=/##;s#/#,#g') # If the CRL was issued by requested issuer, process it. if [[ $crlIssuer == $issuer ]]; then echo "Located CRL for issuer under DN '$dn'." let processed++ # Check if the requested serial number is present in the CRL. if echo "$crl" | base64 --decode| openssl crl -inform DER -noout -text | grep 'Serial Number' \ | sed -e 's/[[:blank:]]*Serial Number: //' | grep -q -i "^${serial}$"; then echo "Serial number present." let matches++ else echo "Serial number not present." let nonMatches++ fi fi # Reset the parameters used for parsing. unset dn crl fi done < <(echo -e "${searchResult}\n") echo echo "Total number of CRLs with designated issuer in LDAP: $processed" echo "Number of CRLs that did contain the serial number: $matches" echo "Number of CRLs that did not contain the serial number: $nonMatches" echo if [[ $processed == 0 ]]; then echo "No CRL was found in LDAP directory with the specified issuer DN." exit 11 elif [[ $matches != $processed ]]; then echo "Not all CRLs have passed the test." exit 12 else echo "All CRLs have passed the test." exit 0 fi