#!/usr/bin/sh #---------------------------------------------------------------------------------- # /var/install/bin/certs-find-unrequired-certs - find unrequired certificates # # Copyright (c) 2015-2025 The Eisfair Team, team(at)eisfair(dot)org # # Creation: 2014-04-14 jed # Last Update: $Id$ # # Parameters: # # certs-find-unrequired-certs - show unrequired certificates # # certs-find-unrequired-certs --help - show help # # certs-find-unrequired-certs [--nogui][--archive|--delete][--keep-result][output-file] # # --nogui - don't use GUI to display results # --archive - archive non required certificates # to directory ${certarchdir} # --delete - delete non required certificates # --keep-result - keep result file when not using # --archive or --delete switch # --keep-tmpfile - keep temporarily created file # # output-file - absolute path, default to ${outfile} # # 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 2 of the License, or # (at your option) any later version. #---------------------------------------------------------------------------------- # include eislib . /var/install/include/eislib module_name="`basename ${0}`" # activate debug output #debug_certs=true if ${debug_certs:-false} then exec 2> /tmp/${module_name}-trace$$.log set -x ask_debug=true export ask_debug fi #---------------------------------------------------------------------------------- # show wait #---------------------------------------------------------------------------------- show_wait () { mecho -n " wait [ " colpos=1 while [ -n "`ps --no-headers $!`" ] do mecho --info -n "." sleep 1 colpos=`expr ${colpos} + 1` if [ "${colpos}" -ge 52 ] then mecho " ]" mecho -n " wait [ " colpos=1 fi done mecho " ] " } #---------------------------------------------------------------------------------- # mark intermediate and erroneous certificates # # input : $1 - name of certificate chain list #---------------------------------------------------------------------------------- mark_intermediate_and_erroneous_certs () { _iic_file="$1" _iic_tmpfile=${_iic_file}.TMP rm -f ${_iic_tmpfile} _iic_error_certs=' ' _iic_intermediate_certs=' ' while read LINE do # 1 2 3 4 5 6 # C:eisler.nettworks.org.pem:I:cacert-class-3-root.pem:R:cacert-class-1-root.pem: # get number of fields _iic_num_args=`echo "${LINE}" | awk -F':' '{print NF-1}'` if [ ${_iic_num_args} -gt 4 ] then # if more than 4 fields exist, an intermediate certificate must have been used _iic_idx=1 _iic_newline='' _iic_intermediate_save=0 for FIELD in `echo "${LINE}" | tr ':' ' '` do if [ ${_iic_intermediate_save} -eq 1 ] then # cache name of intermediate certificate file _iic_intermediate_certs="${_iic_intermediate_certs} ${FIELD}" _iic_intermediate_save=0 fi # replace a certificate identifier from the 2nd occurents onwards if [ ${_iic_idx} -gt 2 ] then if [ "${FIELD}" = 'C' ] then FIELD='I' _iic_intermediate_save=1 elif [ "${FIELD}" = 'E' ] then FIELD='E' # cache name of erroneous certificate file _iic_error_certs="${_iic_error_certs} `echo "${_iic_newline}" | cut -d: -f2`" fi fi _iic_newline="${_iic_newline}${FIELD}:" _iic_idx=`expr ${_iic_idx} + 1` done # save result echo "${_iic_newline}" >>${_iic_tmpfile} else # save result if [ "`echo "${LINE}" | cut -d: -f3`" = 'E' ] then # set 'E'rror marker for certificate echo "${LINE}" | sed 's/^.:/E:/' >> ${_iic_tmpfile} else echo "${LINE}" >> ${_iic_tmpfile} fi fi done < ${_iic_file} # process all cached erroneous certificates for CERTS in ${_iic_error_certs} do # replace certificate identifier in first column sed -i "s/^C:${CERTS}:/E:${CERTS}:/" ${_iic_tmpfile} done # process all cached intermediate certificates for CERTS in ${_iic_intermediate_certs} do # replace certificate identifier in first column sed -i "s/^C:${CERTS}:/I:${CERTS}:/" ${_iic_tmpfile} done # replace original file mv ${_iic_tmpfile} ${_iic_file} } #---------------------------------------------------------------------------------- # show help #---------------------------------------------------------------------------------- show_help () { echo "Usage:" echo " certs-find-unrequired-certs --help - show this help" echo echo " certs-find-unrequired-certs [--nogui][--archive|--delete][output-file]" echo echo " --nogui - don't use GUI to display results" echo " --archive - archive non required certificates" echo " to directory '${certarchdir}'" echo " --delete - delete non required certificates" echo " --keep-result - keep result file when not using" echo " --archive or --delete switch" echo " --keep-tmpfile - keep temporarily created file" echo echo " output-file - absolute path, default to" echo " ${outfile}" } #================================================================================== # main #================================================================================== lockdir=/var/lock tmpdir=/tmp ssldir=/usr/local/ssl certdir=${ssldir}/certs certarchdir=${certdir}/archive tmpfile=`mktemp --suffix="-${module_name}" -p ${tmpdir}` certs_title="Find unrequired certificates" max_lock_wait=60 color='' frame='' if [ -f /etc/config.d/setup ] then if $(grep -qE "^MENU=['\"]/var/install/bin/show-menu['\"]" /etc/config.d/setup) then color='--nocolor' frame='--noframe' fi fi # command line parameter action='show' # archive, delete, show nogui=0 keepresult=0 keeptmp=0 outfile=${tmpdir}/certs-analysis-result.txt outlock=${lockdir}/certs-find-unrequired-certs.lck if [ "${ask_debug}" = 'true' ] then keeptmp=1 fi # remove existing output file if it is not currently used ( flock -x -w ${max_lock_wait} 200 rm -f ${outfile} ) 200>${outlock} # read parameter(s) while [ $# -gt 0 ] do case $1 in *-help|*-?|/? ) show_help exit 1 ;; *-nogui ) # don't show result in GUI nogui=1 shift ;; *-archive ) action='archive' if [ ! -d ${certarchdir} ] then mkdir -p ${certarchdir} fi shift ;; *-delete ) action='delete' shift ;; *-keep-result ) keepresult=1 shift ;; *-keep-tmpfile ) keeptmp=1 shift ;; -* ) # skip unknown parameters shift ;; * ) # output file if [ "${action}" = 'show' -a -d "`dirname $1`" ] then outfile="$1" fi shift break ;; esac done # print header clrhome mecho --info "${certs_title} - action: ${action}" mecho # get all existing certificate files, except symbolic links mecho "getting list of certificates..." filelist=`find ${certdir} -maxdepth 1 -type f -name "*.pem" -printf '%f\n' | sort | tr '\n' ':'` # check all certificate chains mecho "resolving certificate chains..." ( _ifs="${IFS}" IFS=':' for CNAME in ${filelist} do # remove certificate verfication result ':FAIL:' or ':OK:' from result /var/install/bin/certs-show-chain --tableview "${CNAME}" | sed -e 's/:FAIL:$/:/' -e 's/:OK:$/:/' >>${tmpfile} done IFS=${_ifs} ) & # entertain the user show_wait # evaluate exit code wait $! mecho "identifying intermediate certificates..." mark_intermediate_and_erroneous_certs "${tmpfile}" if [ $? -eq 0 ] then mecho "analysing certificates..." ( flock -x -w ${max_lock_wait} 200 _ifs="${IFS}" IFS='@' for CNAME in `cut -d: -f1-2 ${tmpfile} | tr '\n' '@'` do ctype=`echo "${CNAME}" | cut -d: -f1` # cert type: E-error in cert chain, # C-client cert, I-intermediate cert, # R-root cert cname=`echo "${CNAME}" | cut -d: -f2` # cert file name # grep count of occurencies (ignore R: entry) ccount=`grep -E -v "^[I|R]:${cname}:" ${tmpfile} | grep -c ":${cname}:"` # if cname is an intermediate certificate somewhere in tmpfile then change ctype to I if [ "${ctype}" = 'C' ] && grep -q ":I:${cname}:" ${tmpfile} then ctype='I' fi case ${action} in archive ) # archive certificate if [ ${ccount} -eq 0 ] then echo "archiving '${cname}' ..." >> ${outfile} mv "${certdir}/${cname}" ${certarchdir}/ fi ;; delete ) # delete certificate if [ ${ccount} -eq 0 ] then echo "deleting '${cname}' ..." >> ${outfile} rm -f "${certdir}/${cname}" fi ;; show|* ) # show analysis result if [ "${ctype}" = 'E' ] then echo "ERROR:${CNAME} (${ccount})" >> ${outfile} else if [ ${ccount} -eq 0 ] then echo "DELETE:${ctype}:${cname} (${ccount})" >> ${outfile} else echo "KEEP:${ctype}:${cname} (${ccount})" >> ${outfile} fi fi ;; esac done IFS=${_ifs} ) 200>${outlock} & # entertain the user show_wait fi # show results if [ -f ${outfile} ] then if [ "${action}" != "show" ] then mecho "updating hashes ..." /var/install/bin/certs-update-hashes --quiet --certdir fi mecho "showing results..." ( flock -x -w ${max_lock_wait} 200 cp ${outfile} ${outfile}.tmp-1 { echo "### ${certs_title} ###" echo case ${action} in archive) echo "# Archive directory: ${certarchdir}" echo ;; show|*) echo "# E - error in certificate chain!" echo "#" echo "# C - client certificate" echo "# I - intermediate certificate" echo "# R - root certificate" echo "# (x) - number of occurencies" echo ;; esac } > ${outfile} # sort output file sort -t: -k1.1,1.4 -k3.1 ${outfile}.tmp-1 > ${outfile}.tmp-2 # make sure that error messages are always shown first { grep -q "^ERROR:" ${outfile}.tmp-2 if [ $? -eq 0 ] then grep "^ERROR:" ${outfile}.tmp-2 echo "# ^^^ check the certficate chain first ^^^" grep -v "^ERROR:" ${outfile}.tmp-2 else cat ${outfile}.tmp-2 fi } >> ${outfile} rm -f ${outfile}.tmp-1 ${outfile}.tmp-2 ) 200>${outlock} if [ ${nogui} -eq 0 ] then # show GUI /var/install/bin/show-doc.cui ${color} ${frame} --title "${certs_title}" ${outfile} else more ${outfile} fi else mecho "nothing to do." fi if [ ${keeptmp} -eq 1 ] then newtmpfile=${tmpdir}/${module_name}-TMP-${EISDATE}.txt rm -f ${newtmpfile} mv ${tmpfile} ${newtmpfile} mecho --info "tmp file '${newtmpfile} preserved." else rm -f ${tmpfile} fi if [ ${keepresult} -eq 1 ] then mecho --info "output file '${outfile}' preserved." else rm -f ${outfile} fi mecho "done." exit 0