#!/bin/bash
# certbot wrapper for generating certificates

SPATH="$(dirname "$(readlink -f ${BASH_SOURCE[0]})")"    # dir of the script
SNAME="$(basename ${BASH_SOURCE[0]})"                    # name of the script

ASCEDSCONF='local site hostconf certbot'
ASCEDSLIBS='utils'

if [ ! -r "${SPATH}/asceds-certbot-head" ] ; then
   echo "No header ${SPATH}/asceds-certbot-head. Exiting"
   exit 9
fi

. ${SPATH}/asceds-certbot-head

asceds-certbot-gencert-usage()
{
  cat << ENDUSAGE

Usage: asceds-certbot-gencert [-h] [-e] [-d] [-q] -c <client_name> [-s <SAN1>,<SAN2>,...]

Generates new certs using certbot. Options:
-h --> display usage and exit
-e --> execute the certbot command (just echo the command by default)
-q --> no question asked; sends output to the logs in ${ASCEDSLOGDIR}/
-d --> dry-run (off by default)
-c <client_name> --> name of the computer requesting a certificate
-s <SAN1>,... --> comma-separated alternate names of the computer requesting 
                   a certificate not including domain_name

Figures out certbot's options based on the command line input.
Checks if cert manager is authorized to generate requested certificates.
   by matching hostname and SANs with the authorized domains.
Generates new certificates using certbot if -e or if CERTBOTEXEC is not empty:
   Certs/key are generated by certbot and go in /etc/letsencrypt/live/<cert_name>/;
   Checks if a fresh certificate was generated;
   Certs/key are copied to ${ASCEDSHOMEDIR}/<client_name>/ and chown asceds:asceds
   If client is unmanaged, copies the cert files in the web dir and chown ${ASCEDSWEBUSER}.

ENDUSAGE
exit 0
}

# run as root only
asceds-run-asroot

# no dry-run by default
DRYRUN=""

# echo by default if CERTBOTEXEC is not predefined
if [ -n "${CERTBOTEXEC}" ] ; then
   # this states the obvious, just here for the show
   CERTBOTEXEC=''
fi

# output to terminal by default
QUIETRUN=""

# general validation and parse of the command line
if  [ -z "${ARGS}" ] ; then
   asceds-certbot-gencert-usage
else
   for ARG in ${ARGS} ; do
      #echo "ARG=${ARG}"
      if [ "${ARG}" != "${ARG//[^-_a-zA-Z0-9.,]/}" ] ; then
         asceds-error "Malformed argument ${ARG}"
      elif [ "${ARG}" == "-h" ] ; then
         asceds-certbot-gencert-usage
      elif [ "${ARG}" == "-q" ] ; then
         asceds-exec-noninteractive "$*"
         # bring other scripts in non-interactive mode
         QUIETRUN="y"
         shift
      elif [ "${ARG}" == "-d" ] ; then
         shift
         DRYRUN="--dry-run"
      elif [ "${ARG}" == "-e" ] ; then
         shift
         CERTBOTEXEC="eval"
      elif [ "${ARG}" == "-c" ] ; then
         shift
         if [[ "$1" != -* ]] ; then
            DOMAIN_NAME=$( cat <<< $1 | tr '[:upper:]' '[:lower:]' )
            asceds-validate-fqdn "${DOMAIN_NAME}" || \
               asceds-error "Invalid client FQDN ${DOMAIN_NAME}"
         fi
      elif [ "${ARG}" == "-s" ] ; then
         shift
         if [[ "$1" != -* ]] ; then
            NEWVAL=$( cat <<< $1 | tr '[:upper:]' '[:lower:]' )
            if [ -z "${SANS_LIST}" ] ; then
               SANS_LIST="${NEWVAL}"
            else 
               SANS_LIST="${SANS_LIST},${NEWVAL}"
            fi
         fi
      else
         shift
      fi
   done
fi
if [ -z "${DOMAIN_NAME}" ] ; then
   asceds-certbot-gencert-usage
fi

asceds-echo "Hostname: ${DOMAIN_NAME}"
asceds-echo "SANS: ${SANS_LIST}"

# client config file checks and info
if [ -f "${ASCEDSHOMEDIR}/${DOMAIN_NAME}_cert.conf" ] ; then
   NO_RETURN=''
   NO_ASCEDS=''
   ASCEDS_CERT_ID=''
   CLIENT_ENC=''
   asceds-parse-certconf "${ASCEDSHOMEDIR}/${DOMAIN_NAME}_cert.conf"
   if [ -n "${NO_ASCEDS}" ] ; then
      echo "Client ${DOMAIN_NAME} is unmanaged, configured by ${NO_ASCEDS}"
      if [ -z "${ASCEDS_CERT_ID}" ] ; then
	 ASCEDS_CERT_ID=$( cat /dev/urandom | tr -dc 'a-zA-Z0-9' | \
	                   fold -w 32 | head -n 1 )
	 asceds-refresh-var "${ASCEDSHOMEDIR}/${DOMAIN_NAME}_cert.conf" \
	                    "ASCEDS_CERT_ID" "${ASCEDS_CERT_ID}"
      fi
   elif [ -n "${NO_RETURN}" ] ; then
      echo "Client ${DOMAIN_NAME} is managed but marked read-only"
   fi
   if [ "${DOMAIN_NAME}" != "${CLIENT_DOMAIN_NAME}" ] ; then
      asceds-error "Bad CLIENT_DOMAIN_NAME in ${ASCEDSHOMEDIR}/${DOMAIN_NAME}_cert.conf"
   fi
else
   asceds-error "No config file for client ${DOMAIN_NAME}"
fi

# check if certbot was configured and is executable
if [ -z "${CERTBOTBIN}" ] ; then
   asceds-error "No value was configured for certbot"
fi
if [ ! -x "$( readlink -f ${CERTBOTBIN} )" ] ; then
   asceds-error "No executable was found for certbot"
fi

# initial certbot option list
CERTBOTOPTS="certonly ${CERTBOTOPTS}"
if [ -n "${ACMESERVER}" ] ; then
   CERTBOTOPTS="${CERTBOTOPTS} --server ${ACMESERVER}"
fi
if [ -n "${EABKID}" ] ; then
   CERTBOTOPTS="${CERTBOTOPTS} --eab-kid ${EABKID}"
fi
if [ -n "${EABHMACKEY}" ] ; then
   CERTBOTOPTS="${CERTBOTOPTS} --eab-hmac-key ${EABHMACKEY}"
fi
if [ -n "${DRYRUN}" ] ; then
   CERTBOTOPTS="${CERTBOTOPTS} ${DRYRUN} "
fi

# set the key type
asceds-set-certbot-key 

# Checks if cert manager is authorized to generate requested certificates.
asceds-check-domains "${DOMAIN_NAME}" "${SANS_LIST}"

# add DOMAIN_NAME and SANS_LIST to certbot's arguments
CERTBOTOPTS="${CERTBOTOPTS} --cert-name ${DOMAIN_NAME} --domain ${DOMAIN_NAME} "
if [ -n "${SANS_LIST}" ] ; then
   CERTBOTOPTS="${CERTBOTOPTS} -d ${SANS_LIST} "
fi

echo

if [ -z "${CERTBOTEXEC}" ] ; then
   echo
   echo "Certbot command to run:"
   echo "${CERTBOTBIN} ${CERTBOTOPTS}"
   if [ -z "${NO_ASCEDS}" ] ; then
      echo "Make sure that client ${DOMAIN_NAME} has asceds installed"
      echo "and initialized with this certificate manager"
      echo "before performing the real run !!!!!!!" 
   fi
else
   asceds-echo "Generating certificate ..."
   eval ${CERTBOTBIN} ${CERTBOTOPTS}
   
   # test if successfully generated 
   asceds-cert-freshtest "/etc/letsencrypt/live/${DOMAIN_NAME}/cert.pem" || \
      asceds-error "No fresh cert files in /etc/letsencrypt/live/${DOMAIN_NAME}/"
      
   # fresh cert files:
   # /etc/letsencrypt/live/${DOMAIN_NAME}/privkey.pem
   # /etc/letsencrypt/live/${DOMAIN_NAME}/fullchain.pem
   # /etc/letsencrypt/live/${DOMAIN_NAME}/cert.pem
   # /etc/letsencrypt/renewal/${DOMAIN_NAME}.conf
   
   openssl x509 -dates -noout < /etc/letsencrypt/live/${DOMAIN_NAME}/cert.pem

   # set days before expiration for renewal (default = 30 days)
   # sed -i -e "s/# renew_before_expiry = 30 days/renew_before_expiry = 365 days/g" \
   #     /etc/letsencrypt/renewal/${DOMAIN_NAME}.conf


   # copy certificate files to asceeds home dir 
   if [ -z "${ASCEDSHOMEDIR}/${DOMAIN_NAME}" ] ; then
      asceds-error "Bad transfer dir ${ASCEDSHOMEDIR}/${DOMAIN_NAME}
        Something is really wrong if this is executed."
   fi
   # initialize client dir
   if [ ! -d "${ASCEDSHOMEDIR}/${DOMAIN_NAME}" ] ; then
      asceds-echo "No ${ASCEDSHOMEDIR}/${DOMAIN_NAME} ... fixing"
      mkdir -p ${ASCEDSHOMEDIR}/${DOMAIN_NAME}
      ${CP} ${ASCEDSETCDIR}/asceds-site.conf ${ASCEDSHOMEDIR}/${DOMAIN_NAME}/
      if [ "${DOMAIN_NAME}" != "${ASCEDSCRTMGR}" ] && \
         [ "${DOMAIN_NAME}" != "$( hostname -f | tr '[:upper:]' '[:lower:]' )" ] && \
         [ -z "${NO_ASCEDS}" ] ; then
         touch ${ASCEDSHOMEDIR}/${DOMAIN_NAME}/.newsiteconf
      fi
   fi
   for CERTFILE in privkey.pem fullchain.pem cert.pem ; do
      ${CP} -p /etc/letsencrypt/live/${DOMAIN_NAME}/${CERTFILE} \
           ${ASCEDSHOMEDIR}/${DOMAIN_NAME}/
   done
   if [ -z "${NO_ASCEDS}" ] ; then
      touch ${ASCEDSHOMEDIR}/${DOMAIN_NAME}/.newcerts
   fi
   chown -R asceds:asceds ${ASCEDSHOMEDIR}/${DOMAIN_NAME}

   # for unmanaged clients, copy certificate files to the website
   if [ -n "${NO_ASCEDS}" ] ; then
      if [ -z "${ASCEDS_CERT_ID}" ] ; then
         asceds-error "ASCEDS_CERT_ID was created but the variable is empty"
      fi
      if [ ! -d "${ASCEDSWEBDIR}/html/cert/keys/${ASCEDS_CERT_ID}" ] ; then
         mkdir ${ASCEDSWEBDIR}/html/cert/keys/${ASCEDS_CERT_ID}
      fi
      for CERTFILE in cert.pem fullchain.pem privkey.pem ; do
	 ${CP} -p /etc/letsencrypt/live/${DOMAIN_NAME}/${CERTFILE} \
	      ${ASCEDSWEBDIR}/html/cert/keys/${ASCEDS_CERT_ID}/
      done

      # generate pfx file
      cd ${ASCEDSWEBDIR}/html/cert/keys/${ASCEDS_CERT_ID}/
      openssl pkcs12 -inkey privkey.pem -in fullchain.pem -export \
                     -passout pass:${NO_ASCEDS} -out combined.pfx
                     
      # adjust ownership
      chown -R ${ASCEDSWEBUSER} ${ASCEDSWEBDIR}/html/cert/keys/${ASCEDS_CERT_ID}

   fi
fi

