#!/usr/bin/sh #---------------------------------------------------------------------------------- # /var/install/bin/certs-request-cert - request and show certificate # # Copyright (c) 2001-2025 The Eisfair Team, team(at)eisfair(dot)org # # Creation: 2007-01-03 jed # Last Update: $Id$ # # Usage: # # certs-request-cert [--quiet][--norehash][--replace][--writecert][--certdetails] # http|imap|imaptls|ldap|pop3|pop3tls|smtp|ssmtp server-name # [port-number] # --downloadss - Download root/self-signed certificates # --norehash - Do not update hashes # --replace - An existing certificate will be moved to the ./old directory # before the new one is downloaded. # --writecert - The certificate will be requested and saved as file. # --simulate - The certificate will be requested and printed on the screen. # # --certdetails - show more details about the processed certificates # --quiet - Be quiet # # port-number - This is an optional parameter for smtp or http # # return: 0 - certificate download successful # 1 - error, invalid number of parameters given # 2 - error, server not reachable # 3 - error, certificate download failed # # 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 . /var/install/include/jedlib module_name=`basename $0` # debug mode true/false #debug=true if ${debug:-false} then exec 2> /tmp/${module_name}-trace$$.log set -x ask_debug=true export ask_debug fi #------------------------------------------------------------------------------ # my own echo #------------------------------------------------------------------------------ myecho () { case $1 in --std) _me_switch=$1 shift _me_outstr="`echo "$*" | sed -r 's/^(-)?-std //g'`" ;; --info) _me_switch=$1 shift _me_outstr="`echo "$*" | sed -r 's/^(-)?-info //g'`" ;; --warn) _me_switch=$1 shift _me_outstr="`echo "$*" | sed -r 's/^(-)?-warn //g'`" ;; --error) _me_switch=$1 shift _me_outstr="`echo "$*" | sed -r 's/^(-)?-error //g'`" ;; * ) _me_switch='' _me_label='' _me_outstr="$*" ;; esac if [ ${quiet_mode} -eq 0 ] then # be verbose mecho ${_me_switch} "${_me_outstr}" fi } #---------------------------------------------------------------------------------- # check if certificate is valid # input : $1 - name of CRL file # return: 0 - valid # 1 - not valid #---------------------------------------------------------------------------------- is_valid_cert () { _ivc_cert="$1" _ivc_ret=1 if [ -n "${_ivc_cert}" ] then echo "${_ivc_cert}" | grep -q '\---BEGIN CERTIFICATE---' if [ $? -eq 0 ] then echo "${_ivc_cert}" | grep -q '\---END CERTIFICATE---' if [ $? -eq 0 ] then _ivc_ret=0 fi fi fi return ${_ivc_ret} } #---------------------------------------------------------------------------------- # check if port is accessible # input : $1 - server name # $2 - port number # return: 0 - successful # 1 - unsuccessful #---------------------------------------------------------------------------------- check_port_availabilty () { sname=$1 sport=$2 ${vardir}/bin/check_open_port.pl "${sname}" "${sport}" _cp_ret=$? return ${_cp_ret} } #---------------------------------------------------------------------------------- # show IMAP server certificate # input : $1 - server name # $2 - port number (optional) - default: 993 #---------------------------------------------------------------------------------- check_imap() { sname=$1 sport=$2 if [ -z "${sport}" ] then sport=993 fi # ... -connect mail.example.org:993 echo "00000001 LOGOUT" | ${openssl_bin} s_client -CApath ${certdir} -showcerts -connect "${sname}:${sport}" ${proxy_connect} 2>&1 } #---------------------------------------------------------------------------------- # show IMAP server certificate with STARTTLS # input : $1 - server name # $2 - port number (optional) - default: 143 #---------------------------------------------------------------------------------- check_imaptls() { sname=$1 sport=$2 if [ -z "${sport}" ] then sport=143 fi # ... -starttls imap -connect mail.example.org:143 echo "QUIT" | ${openssl_bin} s_client -CApath ${certdir} -showcerts -starttls imap -connect "${sname}:${sport}" ${proxy_connect} 2>&1 } #---------------------------------------------------------------------------------- # show POP3 server certificate # input : $1 - server name # $2 - port number (optional) - default: 995 #---------------------------------------------------------------------------------- check_pop3() { sname=$1 sport=$2 if [ -z "${sport}" ] then sport=995 fi # ... -connect mail.example.org:995 echo "QUIT" | ${openssl_bin} s_client -CApath ${certdir} -showcerts -connect "${sname}:${sport}" ${proxy_connect} 2>&1 } #---------------------------------------------------------------------------------- # show POP3 server certificate with STARTTLS # input : $1 - server name # $2 - port number (optional) - default: 110 #---------------------------------------------------------------------------------- check_pop3tls() { sname=$1 sport=$2 if [ -z "${sport}" ] then sport=110 fi # ... -starttls pop3 -connect mail.example.org:110 echo "QUIT" | ${openssl_bin} s_client -CApath ${certdir} -showcerts -starttls pop3 -connect "${sname}:${sport}" ${proxy_connect} 2>&1 } #---------------------------------------------------------------------------------- # show LDAP server certificate # input : $1 - server name # $2 - port number (optional) - default: 636 #---------------------------------------------------------------------------------- check_ldap() { sname=$1 sport=$2 if [ -z "${sport}" ] then sport=636 fi # ... ldap.example.org:636 echo "QUIT" | ${openssl_bin} s_client -CApath ${certdir} -showcerts "${sname}:${sport}" ${proxy_connect} 2>&1 } #---------------------------------------------------------------------------------- # show SMTP server certificate with STARTTLS # input : $1 - server name # $2 - port number (optional) - default: 25 #---------------------------------------------------------------------------------- check_smtp() { sname=$1 sport=$2 # default to use STARTTLS # ... -starttls smtp -connect mail.example.org:25 # ... -starttls smtp -connect mail.example.org:587 if [ -z "${sport}" ] then sport=25 fi ( echo "HELO `hostname`" echo "QUIT" )| ${openssl_bin} s_client -CApath ${certdir} -showcerts -starttls smtp -crlf -connect "${sname}:${sport}" ${proxy_connect} 2>&1 } #---------------------------------------------------------------------------------- # show SSMTP server certificate # input : $1 - server name # $2 - port number (optional) - default: 465 #---------------------------------------------------------------------------------- check_ssmtp() { sname=$1 sport=$2 # old fashion direct SSL connection # ... -connect mail.example.org:465 if [ -z "${sport}" ] then sport=465 fi ( echo "HELO `hostname`" echo "QUIT" )| ${openssl_bin} s_client -CApath ${certdir} -showcerts -connect "${sname}:${sport}" ${proxy_connect} 2>&1 } #---------------------------------------------------------------------------------- # show website certificate # $1 - server name # $2 - port number (optional) - default: 443 #---------------------------------------------------------------------------------- check_http() { sname=$1 sport=$2 if [ -z "${sport}" ] then sport=443 fi # ... -connect www.example.org:443 or better # SNI: ... -servername www.example.org -connect www.example.org:443 echo "GET / HTTP/1.0" | ${openssl_bin} s_client -CApath ${certdir} -showcerts -servername "${sname}" -connect "${sname}:${sport}" ${proxy_connect} 2>&1 } #---------------------------------------------------------------------------------- # show help #---------------------------------------------------------------------------------- show_help() { # help mecho mecho "Usage: ${pgmname} [--norehash][--replace][--writecert][--simulate][--certdetails][--quiet]" mecho " http|imap|imaptls|ldap|pop3|pop3tls|smtp|ssmtp server-name [port-number]" mecho " --downloadss - Download root/self-signed certificates" mecho " --norehash - Do not update hashes" mecho " --replace - An existing certificate will be moved to the ./old directory" mecho " before the new one is downloaded." mecho " --writecert - The certificate will be requested and saved as file." mecho " --simulate - The certificate will be requested and printed to screen." mecho " --certdetails - Show more details about the processed certificates" mecho " --quiet - Be quiet" mecho " port-number - This is an optional parameter for smtp or http" mecho } #================================================================================== # main #================================================================================== pgmname="`basename $0`" # set platform specific parameters vardir=/var/certs tmpdir=/tmp ssldir=/usr/local/ssl certdir=${ssldir}/certs oldcertdir=${certdir}/old ignore_list=${ssldir}/certs-request-ignore-list openssl_bin=/usr/bin/openssl # check installed OpenSSL version # OpenSSL 1.0.2l-fips 25 May 2017 openssl_version=`${openssl_bin} version | sed 's/^openssl \([0-9.]*\).*$/\1/i'` proxy_connect='' if [ "`compare_version --text "${openssl_version}" '1.1'`" = 'old' ] then # old OpenSSL version: < v1.1.0, e.g. v1.0.2 openssl_separator=' ' if [ -n "${HTTP_PROXY}" ] then myecho --error "proxy usage is not supported by this OpenSSL version!" exit 1 fi else # new OpenSSL version: >= v1.1.0 openssl_separator='=' if [ -n "${HTTP_PROXY}" ] then proxy_connect="-proxy ${HTTP_PROXY}" if [ -n "${HTTP_PROXY_USER}" ] then # a proxy authentication has been requested. myecho --error "proxy authentication is not supported by the OpenSSL command!" exit 1 fi fi fi cert_details=0 cert_ok=0 quiet_mode=0 no_rehash=0 write_cert_to_file=0 simulate=0 replace_existing_file=0 download_selfsigned=0 while [ 1 ] do case "$1" in -certdetails|--certdetails) cert_details=1 shift ;; -downloadss|--downloadss) download_selfsigned=1 shift ;; -quiet|--quiet) quiet_mode=1 shift ;; -norehash|--norehash) no_rehash=1 shift ;; -replace|--replace) # replace mode replace_existing_file=1 shift ;; -writecert|--writecert) # write certificate to file write_cert_to_file=1 shift ;; -simulate|--simulate) # simulate writing certificate to file simulate=1 shift ;; -help|--help|-?|/?) # show help show_help exit 1 ;; *) break ;; esac done run_return=1 if [ $# -ge 2 ] then option="$1" server_name="`echo "$2" | sed 's#^.*://##'`" # remove protocol if given, e.g. https:// port_number="$3" case ${option} in pop) option='pop3' ;; smtps) option='ssmtp' ;; web) option='http' ;; esac case ${option} in http|imap|imaptls|ldap|pop3|pop3tls|smtp|ssmtp) # check certificate if [ -z "${port_number}" ] then case ${option} in http) port_number=443;; imap) port_number=993;; imaptls) port_number=143;; ldap) port_number=636;; pop3) port_number=995;; pop3tls) port_number=110;; smtp) port_number=25;; ssmtp) port_number=465;; esac fi run_return=3 if check_port_availabilty "${server_name}" "${port_number}" then run_return=4 if [ ${write_cert_to_file} -eq 1 -o ${simulate} -eq 1 ] then # request certificate tmpfile=`mktemp -p ${tmpdir}` myecho "requesting ${option} certificate from server '${server_name}' ..." # 1. request certificate data # 2. get rid of extra information and keep only the certificate data check_${option} "${server_name}" "${port_number}" | sed -n '/-BEGIN/,/-END/p' > ${tmpfile} # 1. remove empty lines # 2. add @-character at end of a certificate lines (-----END CERTIFICATE-----) # 3. remove new line character and end of a certificate lines cert_chain=`sed -e '/^[[:space:]]*$/d' -e 's/\(-----END CERTIFICATE-----\)$/\1@/g' ${tmpfile} | \ awk '{if (sub(/@$/,"@")) printf "%s", $0; else print $0}'` _ifsorg=$IFS IFS=@ for CERT in ${cert_chain} do # loop through all certificates which have been returned if is_valid_cert "${CERT}" then # valid certificate - update it # subject= /CN=mail.telejeck.de # subject= /CN=*.helimail.de # subject= /C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G2 # subject= /C=US/O=GeoTrust Inc./OU=Starfield Class 2 Certification Authority # subject= /C=US/ST=Virginia/L=Herndon/O=SWsoft, Inc./OU=Confixx/CN=confixx/emailAddress=info@confixx.com cert_ok=1 # get rid of possible double-quotes (") subject="`echo "${CERT}" | ${openssl_bin} x509 -noout -subject | tr -d '"'`" anchor="CN" echo ${subject} | grep -q "CN *=" if [ $? -ne 0 ] then # default CN= anchor not found echo ${subject} | grep -q "OU *=" if [ $? -eq 0 ] then # alternative OU= anchor found anchor="OU" fi fi common_name_org="`echo "${subject}" | sed -e "s#^subject=.*${anchor} *=##g" -e 's/\*\.//' -e 's/^ *//' \ -e 's/ *$//' -e 's#^\(.*\)/.*$#\1#' -e 's/[^A-Za-z0-9 _.-]//g' -e 's/ /_/g'`" common_name="`echo "${common_name_org}" | tr '[:upper:]' '[:lower:]'`" subject_hash=`echo "${CERT}" | ${openssl_bin} x509 -noout -subject_hash` issuer_hash=`echo "${CERT}" | ${openssl_bin} x509 -noout -issuer_hash` # check if alternative names are given echo "${CERT}" | ${openssl_bin} x509 -certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_aux -text -noout | grep -q "X509v3 Subject Alternative Name:" if [ $? -eq 0 ] then # yes, go on ... alt_names=`echo "${CERT}" | ${openssl_bin} x509 -certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_aux -text -noout | sed -n '/X509v3 Subject Alternative Name:/,/X509v3/p' | grep -E -v "X509v3|^[[:space:]]*$" | grep -E "DNS:|IP:" | sed -e 's/,//g' -e 's/ *DNS:/:/g' -e 's/ *IP Address:/:/g' -e 's/^://'` else # no alt_names='' fi # notAfter=Jan 9 22:15:11 2018 GMT enddate=`echo "${CERT}" | ${openssl_bin} x509 -noout -enddate | sed 's#^notAfter= *##g' | xargs -0 date +'%d.%m.%Y' -d` if [ -n "${common_name}" -a -n "${subject_hash}" ] then # check if certificate is on block list ignore_cert=0 if [ -s ${ignore_list} ] then grep -E -i -q "^${common_name}$|^${subject_hash}$" ${ignore_list} if [ $? -eq 0 ] then ignore_cert=1 fi fi # check if certificate is a root certificate/self signed if [ ${download_selfsigned} -eq 0 ] then # skip root/self signed certificates if [ "${subject_hash}" = "${issuer_hash}" ] then # self signed, skip certificate ... ignore_cert=2 fi fi case ${ignore_cert} in 0 ) # target file outfile="${certdir}/${common_name}.pem" backup_file="${oldcertdir}/${common_name}-${EISDATE}.pem" if [ ${replace_existing_file} -eq 1 ] then if [ ${simulate} -eq 0 ] then # do not simulate request, force replacement # of an existing certificate file if [ -f "${certdir}/${subject_hash}.0" -o -f "${outfile}" ] then if [ ! -d ${oldcertdir} ] then # create target directory mkdir -p ${oldcertdir} fi if [ ! -f "${backup_file}" ] then # move file to backup directory if [ -f "${outfile}" ] then mv "${outfile}" "${backup_file}" fi # move file behind hash to backup directory if [ -f "${certdir}/${subject_hash}.0" ] then realfile="`realpath "${certdir}/${subject_hash}.0"`" if [ -n "${realfile}" -a -f "${realfile}" ] then backup_realfile="`basename "${realfile}"`" backup_realfile="${oldcertdir}/`echo "${backup_realfile}" | sed "s/^\(.*\)\.pem/\1-${EISDATE}.pem/"`" mv "${realfile}" "${backup_realfile}" else myecho --warn "Backup file '${backup_realfile}' already exists!" fi rm "${certdir}/${subject_hash}.0" fi else myecho --warn "Backup file '${backup_file}' already exists!" fi fi fi else # do not replace an existing certificate file idx=1 until [ ! -f "${outfile}" ] do outfile="${certdir}/${common_name}-${idx}.pem" idx=`expr ${idx} + 1` done fi if [ ${simulate} -eq 0 ] then # do not simulate request, go on ... if [ ${replace_existing_file} -eq 1 -a -f "${outfile}" ] then # force replacement of an existing certificate file rm -f "${outfile}" fi if [ ! -f "${outfile}" ] then myecho "saving certificate to file '`basename ${outfile}`' ..." # check if the second level domain of the common name # is part of the server name to catch also wildcard # certificates. e.g. 'domain.de' part from 'mail.domain.de' echo "${server_name}" | grep -q "`echo "${common_name}" | sed 's/^.*\.\(\w*\.\w*\)$/\1/'`$" if [ $? -eq 0 ] then myecho "valid until: ${enddate}" fi cleartext_cert=`echo "${CERT}" | ${openssl_bin} x509 -noout -text` { echo "${cleartext_cert}" echo "${CERT}" } > ${outfile} # set access rights chmod 0644 ${outfile} if [ ${cert_details} -eq 1 ] then # show detailed certificate details (used by e.g. mail-addon-certs echo "FILE:${outfile} HNAME:${server_name}:${port_number} CNAME:${common_name} ALTNAME:${alt_names}" fi fi else # simulate certificate request - do not write any file myecho "certificate file '`basename ${outfile}`' ..." # check if the second level domain of the common name # is part of the server name to catch also wildcard # certificates. e.g. 'domain.de' part from 'mail.domain.de' echo "${server_name}" | grep -q "`echo "${common_name}" | sed 's/^.*\.\(\w*\.\w*\)$/\1/'`$" if [ $? -eq 0 ] then myecho "valid until: ${enddate}" fi cleartext_cert=`echo "${CERT}" | ${openssl_bin} x509 -noout -text` echo "${cleartext_cert}" echo "${CERT}" if [ ${cert_details} -eq 1 ] then # show detailed certificate details (used by e.g. mail-addon-certs echo "FILE:${outfile} HNAME:${server_name}:${port_number} CNAME:${common_name} ALTNAME:${alt_names}" fi no_rehash=1 fi ;; 1 ) myecho "skipping certificate '${common_name_org}' because it's on the block list." no_rehash=1 ;; 2 ) myecho "skipping certificate '${common_name_org}' because it's a root/self-signed one." no_rehash=1 ;; esac fi run_return=0 fi done IFS=${_ifsorg} if [ ${no_rehash} -eq 0 -a ${cert_ok} -eq 1 ] then # recreate hash values myecho "recreating hashes ..." /var/install/bin/certs-update-hashes --quiet --certdir fi rm -f ${tmpfile} myecho "finished." else # print to screen myecho "requesting ${1} certificate from server '${server_name}' ..." check_${1} "${server_name}" "${port_number}" run_return=0 fi else # port not reachable myecho --error "server not reachable or port not accessible: ${server_name}:${port_number}" fi ;; *) # help show_help ;; esac else # help show_help fi exit ${run_return} #================================================================================== # End #==================================================================================