#!/bin/sh
# Copyright (C) 2015-2022 Greenbone AG
#
# SPDX-License-Identifier: AGPL-3.0-or-later

# Manage certificate infrastructure for a GVM installation

# The following TODOS are features deemed desirable which have not yet been
# implemented.
# TODO: Import a certificate
# Does the certificate contain a private key?
# Do we take the key generated for the signing request?

# Script and feed information which will be made available to user through
# command line options and automated tools.
SCRIPT_NAME="gvm-manage-certs"

# LOG_CMD defines the command to use for logging. To have logger log to stderr
# as well as syslog, add "-s" here.
LOG_CMD="logger -t $SCRIPT_NAME"

CA_CERTIFICATE=1
SERVER_CERTIFICATE=2
CLIENT_CERTIFICATE=3

log_write () {
  if [ $QUIET -ne 1 ]
  then
    $LOG_CMD -p daemon.info "$1"
    echo "$1"
  fi
}

log_err () {
  $LOG_CMD -p daemon.err "$1"
  >&2 echo "$1"
}

log_debug () {
  if [ $DEBUG -eq 1 ]
  then
    $LOG_CMD -p daemon.debug "$1"
    echo "$1"
  fi
}

log_verbose () {
  if [ $VERBOSE -eq 1 ]
  then
    log_write "$1"
  fi
}

set_defaults () {
  # Set default values for certificate parameters
  # Lifetime
  GVM_CERTIFICATE_LIFETIME=${GVM_CERTIFICATE_LIFETIME:-730}
  # Country
  GVM_CERTIFICATE_COUNTRY=${GVM_CERTIFICATE_COUNTRY:-"DE"}
  # State
  GVM_CERTIFICATE_STATE=${GVM_CERTIFICATE_STATE:-""}
  # Locality
  GVM_CERTIFICATE_LOCALITY=${GVM_CERTIFICATE_LOCALITY:-"Osnabrueck"}
  # Organization
  GVM_CERTIFICATE_ORG=${GVM_CERTIFICATE_ORG:-"GVM Users"}
  # (Organization unit)
  GVM_CERTIFICATE_ORG_UNIT=${GVM_CERTIFICATE_ORG_UNIT:-""}
  # Subject Alternative Name(s)
  GVM_CERTIFICATE_SAN_DNS=${GVM_CERTIFICATE_SAN_DNS:-""}
  GVM_CERTIFICATE_SAN_URI=${GVM_CERTIFICATE_SAN_URI:-""}
  GVM_CERTIFICATE_SAN_EMAIL=${GVM_CERTIFICATE_SAN_EMAIL:-""}
  GVM_CERTIFICATE_SAN_IP_ADDRESS=${GVM_CERTIFICATE_SAN_IP_ADDRESS:-""}
  GVM_CERTIFICATE_SAN_OTHER_NAME_UTF8=${GVM_CERTIFICATE_SAN_OTHER_NAME_UTF8:-""}

  # Hostname
  if [ -z "$GVM_CERTIFICATE_HOSTNAME" ]
  then
    GVM_CERTIFICATE_HOSTNAME=$(hostname --fqdn 2> /dev/null)
    if [ $? -ne 0 ]
    then
      GVM_CERTIFICATE_HOSTNAME="localhost"
    fi
  fi

  # Certificate Authority (CA) Certificate Parameters
  # CA Lifetime
  GVM_CA_CERTIFICATE_LIFETIME=${GVM_CA_CERTIFICATE_LIFETIME:-3652}
  # Country
  GVM_CA_CERTIFICATE_COUNTRY=${GVM_CA_CERTIFICATE_COUNTRY:-"$GVM_CERTIFICATE_COUNTRY"}
  # State
  GVM_CA_CERTIFICATE_STATE=${GVM_CA_CERTIFICATE_STATE:-"$GVM_CERTIFICATE_STATE"}
  # Locality
  GVM_CA_CERTIFICATE_LOCALITY=${GVM_CA_CERTIFICATE_LOCALITY:-"$GVM_CERTIFICATE_LOCALITY"}
  # Organization
  GVM_CA_CERTIFICATE_ORG=${GVM_CA_CERTIFICATE_ORG:-"$GVM_CERTIFICATE_ORG"}
  # (Organization unit)
  GVM_CA_CERTIFICATE_ORG_UNIT=${GVM_CA_CERTIFICATE_ORG_UNIT:-"Certificate Authority for $GVM_CERTIFICATE_HOSTNAME"}
  # Subject Alternative Name(s)
  GVM_CA_CERTIFICATE_SAN_DNS=${GVM_CA_CERTIFICATE_SAN_DNS:-"$GVM_CERTIFICATE_SAN_DNS"}
  GVM_CA_CERTIFICATE_SAN_URI=${GVM_CA_CERTIFICATE_SAN_URI:-"$GVM_CERTIFICATE_SAN_URI"}
  GVM_CA_CERTIFICATE_SAN_EMAIL=${GVM_CA_CERTIFICATE_SAN_EMAIL:-"$GVM_CERTIFICATE_SAN_EMAIL"}
  GVM_CA_CERTIFICATE_SAN_IP_ADDRESS=${GVM_CA_CERTIFICATE_SAN_IP_ADDRESS:-"$GVM_CERTIFICATE_SAN_IP_ADDRESS"}
  GVM_CA_CERTIFICATE_SAN_OTHER_NAME_UTF8=${GVM_CA_CERTIFICATE_SAN_OTHER_NAME_UTF8:-"$GVM_CERTIFICATE_SAN_OTHER_NAME_UTF8"}
  # Key size
  if [ -z "$GVM_CERTIFICATE_KEYSIZE" ]
  then
    GVM_CERTIFICATE_SECPARAM=${GVM_CERTIFICATE_SECPARAM:-"high"}
  fi

  # Signature algorithm
  GVM_CERTIFICATE_SIGNALG=${GVM_CERTIFICATE_SIGNALG:-"SHA256"}

  # Location in file system where private keys are stored
  GVM_KEY_LOCATION=${GVM_KEY_LOCATION:-"@GVM_STATE_DIR@/private/CA"}
  # Location in file system where certificates are stored
  GVM_CERT_LOCATION=${GVM_CERT_LOCATION:-"@GVM_STATE_DIR@/CA"}

  # Prefix for generated files
  # If not set, files will be named "key.pem" and "cert.pem"
  # If set to "server", files will be named "serverkey.pem" and "servercert.pem"
  GVM_CERT_PREFIX=${GVM_CERT_PREFIX:-""}
}


print_help ()
{
  echo "Usage:"
  echo "  $0 [OPTION] - Manage certificate infrastructure for a GVM installation"
  echo
  echo "Options:"
  echo "  -h             Print help"
  echo "  -a             Automatically set up default infrastructure for GVM"
  echo "  -V             Verify existing GVM certificate infrastructure"
  echo "  -C             Create a certificate authority (CA)"
  echo "  -I             Install a CA certificate"
  echo "  -R             Create a certificate request for a CA"
  echo "  -r             Create a certificate request"
  echo "  -c             Create a certificate request and sign it"
  echo "  -i             Install a certificate"
  echo "  -S             Sign a certificate request"
  echo "  -f             Force overwriting of existing files"
  echo
  echo "Certificate options:"
  echo "  -E             Create a server certificate"
  echo "  -L             Create a client certificate"
  echo "  -A             Skip CA generation in automatic mode"
  echo
  echo "Output control:"
  echo "  -d             Print debug output"
  echo "  -v             Print verbose messages"
  echo "  -q             Be quiet, only print error messages"
  echo
  echo "Configuration:"
  echo "  -e <file>      Read configuration from <file>"
  echo
  echo "All certificate generation options can be set either through the configuration"
  echo "  file or through environment variables like the following:"
  echo
  echo "  GVM_CERTIFICATE_LIFETIME   Days until the certificate will expire"
  echo "  GVM_CERTIFICATE_HOSTNAME   Name to use for the certificate"
  echo "  GVM_CERTIFICATE_SIGNALG    Hash algorithm to use for signing"
  echo
  echo "  GVM_CERTIFICATE_KEYSIZE    Size in bits of the generated key"
  echo "  or"
  echo "  GVM_CERTIFICATE_SECPARAM   GnuTLS security level [low|medium|high|ultra]"
  echo
  echo "  GVM_CERT_DIR               Directory where keys and certificates are stored"
  echo "                                 before installation"
  echo
  echo "  GVM_CERT_PREFIX            Prefix for certificate filename (e.g. \"server\")"
  echo
  echo "For a complete list of options, please refer to the documentation."
  echo

  exit 0
}

# Ensure everything is ready to run, prepare temporary directory
set_up ()
{
  # Check if "certtool" binary is available
  if ! type certtool > /dev/null 2>&1
  then
    echo "ERROR: certtool binary not found!"
    exit 1
  fi

  # TODO: certtool version check?

  # Set directory to use for keys and certificates before installation
  if [ -z "$GVM_CERT_DIR" ]
  then
    USE_TEMP_DIR=1
    GVM_CERT_DIR=`mktemp -d`
    log_verbose "Using $GVM_CERT_DIR to temporarily store files."
  else
    USE_TEMP_DIR=0
    if [ ! -w "$GVM_CERT_DIR" ]
    then
      log_err "$GVM_CERT_DIR has to exist and has to be writable. Aborting."
      exit 1
    fi
  fi

  # Logfile for certtool output
  CERTTOOL_LOGFILE="$GVM_CERT_DIR/gvm-manage-certs.log"

  # Filename to use for generated private key
  GVM_KEY_FILENAME=${GVM_KEY_FILENAME:-"$GVM_CERT_DIR/${GVM_CERT_PREFIX}key.pem"}
  # Filename to use for generated certificate
  GVM_CERT_FILENAME=${GVM_CERT_FILENAME:-"$GVM_CERT_DIR/${GVM_CERT_PREFIX}cert.pem"}
  # Filename to use for generated certificate request
  GVM_CERT_REQUEST_FILENAME=${GVM_CERT_REQUEST_FILENAME:-"$GVM_CERT_DIR/${GVM_CERT_PREFIX}request.pem"}
  # Filename to use for generated certificate template
  GVM_CERT_TEMPLATE_FILENAME=${GVM_CERT_TEMPLATE_FILENAME:-"$GVM_CERT_DIR/${GVM_CERT_PREFIX}gvm-cert.cfg"}

  # Filename of CA private key used for signing certificates
  GVM_SIGNING_CA_KEY_FILENAME=${GVM_SIGNING_CA_KEY_FILENAME:-"$GVM_KEY_LOCATION/cakey.pem"}
  # Filename of CA certificate used for signing certificates
  GVM_SIGNING_CA_CERT_FILENAME=${GVM_SIGNING_CA_CERT_FILENAME:-"$GVM_CERT_LOCATION/cacert.pem"}
}

# Create installation directories with correct permissions
set_up_directories ()
{
  log_verbose "Setting up directories"

  if [ ! -d "$GVM_CERT_LOCATION" ]
  then
    mkdir -p "$GVM_CERT_LOCATION"
    if [ $? -ne 0 ]
    then
      log_err "ERROR: Failed to create certificate directory ($GVM_CERT_LOCATION). Aborting."
      exit 1
    fi
  fi

  if [ ! -d "$GVM_KEY_LOCATION" ]
  then
    mkdir -p "$GVM_KEY_LOCATION"
    if [ $? -ne 0 ]
    then
      log_err "ERROR: Failed to create private key directory ($GVM_KEY_LOCATION). Aborting."
      exit 1
    fi
  fi

  chmod 0700 "$GVM_KEY_LOCATION"

  chmod a+rx "$GVM_CERT_LOCATION"
}

# Apply file name prefix setting for generated files
set_prefix ()
{
  GVM_CERT_PREFIX="$1"
  GVM_KEY_FILENAME="$GVM_CERT_DIR/${GVM_CERT_PREFIX}key.pem"
  GVM_CERT_FILENAME="$GVM_CERT_DIR/${GVM_CERT_PREFIX}cert.pem"
  GVM_CERT_REQUEST_FILENAME="$GVM_CERT_DIR/${GVM_CERT_PREFIX}request.pem"
  GVM_CERT_TEMPLATE_FILENAME="$GVM_CERT_DIR/${GVM_CERT_PREFIX}gvm-cert.cfg"
}

# Create a private key
create_private_key ()
{
  log_verbose "Generating private key."

  umask 022

  if [ -z "$GVM_CERTIFICATE_KEYSIZE" ]
  then
    CERTTOOL_PRIVKEY_PARAM="--sec-param $GVM_CERTIFICATE_SECPARAM"
  else
    CERTTOOL_PRIVKEY_PARAM="--bits $GVM_CERTIFICATE_KEYSIZE"
  fi

  # Create a private key
  certtool \
    --generate-privkey $CERTTOOL_PRIVKEY_PARAM \
    --outfile "$1" \
    >> "$CERTTOOL_LOGFILE" 2>&1
  if [ $? -ne 0 ]
  then
    log_err "ERROR: Failed to generate private key, see $CERTTOOL_LOGFILE for details. Aborting."
    exit 1
  fi

  log_write "Generated private key in $1."
}

# Split SAN settings by ';'
split_san_value ()
{
  TEMPLATE_VARIABLE=$1
  ENVIRONMENT_VALUE=$2
  log_debug "Split SAN environment: '$ENVIRONMENT_VALUE'."

  OIFS=$IFS
  IFS=';'

  read -r VALUES <<EOF
$ENVIRONMENT_VALUE
EOF

  for VALUE in $VALUES
  do
    echo "$TEMPLATE_VARIABLE = \"$VALUE\"" >> "$GVM_CERT_TEMPLATE_FILENAME"
  done

  IFS=$OIFS
}

# Create a certificate
create_certificate ()
{
  log_verbose "Generating certificate."

  umask 022

  rm -f "$GVM_CERT_TEMPLATE_FILENAME"

  # Create template using parameters
  if [ $CERTIFICATE_TYPE -eq $CA_CERTIFICATE ]
  then
    if [ -n "$GVM_CA_CERTIFICATE_LIFETIME" ]
    then
      echo "expiration_days = $GVM_CA_CERTIFICATE_LIFETIME" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CA_CERTIFICATE_COUNTRY" ]
    then
      echo "country = \"$GVM_CA_CERTIFICATE_COUNTRY\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CA_CERTIFICATE_STATE" ]
    then
      echo "state = \"$GVM_CA_CERTIFICATE_STATE\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CA_CERTIFICATE_LOCALITY" ]
    then
      echo "locality = \"$GVM_CA_CERTIFICATE_LOCALITY\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CA_CERTIFICATE_ORG" ]
    then
      echo "organization = \"$GVM_CA_CERTIFICATE_ORG\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CA_CERTIFICATE_ORG_UNIT" ]
    then
      echo "unit = \"$GVM_CA_CERTIFICATE_ORG_UNIT\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CA_CERTIFICATE_HOSTNAME" ]
    then
      echo "cn = \"$GVM_CA_CERTIFICATE_HOSTNAME\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CA_CERTIFICATE_SAN_DNS" ]
    then
      split_san_value "dns_name" "$GVM_CA_CERTIFICATE_SAN_DNS"
    fi
    if [ -n "$GVM_CA_CERTIFICATE_SAN_URI" ]
    then
      split_san_value "uri" "$GVM_CA_CERTIFICATE_SAN_URI"
    fi
    if [ -n "$GVM_CA_CERTIFICATE_SAN_EMAIL" ]
    then
      split_san_value "email" "$GVM_CA_CERTIFICATE_SAN_EMAIL"
    fi
    if [ -n "$GVM_CA_CERTIFICATE_SAN_IP_ADDRESS" ]
    then
      split_san_value "ip_address" "$GVM_CA_CERTIFICATE_SAN_IP_ADDRESS"
    fi
    if [ -n "$GVM_CA_CERTIFICATE_SAN_OTHER_NAME_UTF8" ]
    then
      split_san_value "other_name_utf8" "$GVM_CA_CERTIFICATE_SAN_OTHER_NAME_UTF8"
    fi
  else
    if [ -n "$GVM_CERTIFICATE_LIFETIME" ]
    then
      echo "expiration_days = $GVM_CERTIFICATE_LIFETIME" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CERTIFICATE_COUNTRY" ]
    then
      echo "country = \"$GVM_CERTIFICATE_COUNTRY\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CERTIFICATE_STATE" ]
    then
      echo "state = \"$GVM_CERTIFICATE_STATE\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CERTIFICATE_LOCALITY" ]
    then
      echo "locality = \"$GVM_CERTIFICATE_LOCALITY\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CERTIFICATE_ORG" ]
    then
      echo "organization = \"$GVM_CERTIFICATE_ORG\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CERTIFICATE_ORG_UNIT" ]
    then
      echo "unit = \"$GVM_CERTIFICATE_ORG_UNIT\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CERTIFICATE_HOSTNAME" ]
    then
      echo "cn = \"$GVM_CERTIFICATE_HOSTNAME\"" >> $GVM_CERT_TEMPLATE_FILENAME
    fi
    if [ -n "$GVM_CERTIFICATE_SAN_DNS" ]
    then
      split_san_value "dns_name" "$GVM_CERTIFICATE_SAN_DNS"
    fi
    if [ -n "$GVM_CERTIFICATE_SAN_URI" ]
    then
      split_san_value "uri" "$GVM_CERTIFICATE_SAN_URI"
    fi
    if [ -n "$GVM_CERTIFICATE_SAN_EMAIL" ]
    then
      split_san_value "email" "$GVM_CERTIFICATE_SAN_EMAIL"
    fi
    if [ -n "$GVM_CERTIFICATE_SAN_IP_ADDRESS" ]
    then
      split_san_value "ip_address" "$GVM_CERTIFICATE_SAN_IP_ADDRESS"
    fi
    if [ -n "$GVM_CERTIFICATE_SAN_OTHER_NAME_UTF8" ]
    then
      split_san_value "other_name_utf8" "$GVM_CERTIFICATE_SAN_OTHER_NAME_UTF8"
    fi
  fi

  # Add key usage constraints if the certificate type is known
  if [ $CERTIFICATE_TYPE -eq $CA_CERTIFICATE ]
  then
    echo "ca" >> $GVM_CERT_TEMPLATE_FILENAME
    echo "cert_signing_key" >> $GVM_CERT_TEMPLATE_FILENAME
    echo "crl_signing_key" >> $GVM_CERT_TEMPLATE_FILENAME
  fi
  if [ $CERTIFICATE_TYPE -eq $SERVER_CERTIFICATE ]
  then
    # This certificate will be used to encrypt data and sign data.
    # This is the keyEncipherment flag in RFC5280 terminology.
    echo "encryption_key" >> $GVM_CERT_TEMPLATE_FILENAME
    echo "signing_key" >> $GVM_CERT_TEMPLATE_FILENAME
    echo "tls_www_server" >> $GVM_CERT_TEMPLATE_FILENAME
  fi
  if [ $CERTIFICATE_TYPE -eq $CLIENT_CERTIFICATE ]
  then
    # This certificate will be used to sign data.
    # This is the digitalSignature flag in RFC5280 terminology.
    echo "signing_key" >> $GVM_CERT_TEMPLATE_FILENAME
    echo "tls_www_client" >> $GVM_CERT_TEMPLATE_FILENAME
  fi

  if [ $DEBUG -eq 1 ]
  then
    echo "DEBUG: Using the following template ($GVM_CERT_TEMPLATE_FILENAME):" >> "$CERTTOOL_LOGFILE"
    cat $GVM_CERT_TEMPLATE_FILENAME >> "$CERTTOOL_LOGFILE"
  fi

  if [ $CREATE_SELF_SIGNED -eq 1 ]
  then
    # Create a self signed certificate
    log_verbose "  Generating self signed certificate."
    certtool \
      --generate-self-signed \
      --hash "$GVM_CERTIFICATE_SIGNALG" \
      --load-privkey "$GVM_KEY_FILENAME" \
      --outfile "$GVM_CERT_FILENAME" \
      --template "$GVM_CERT_TEMPLATE_FILENAME" \
      >> "$CERTTOOL_LOGFILE" 2>&1
    if [ $? -ne 0 ]
    then
      log_err "ERROR: Failed to create self signed certificate, see $CERTTOOL_LOGFILE for details. Aborting."
      exit 1
    fi

    log_write "Generated self signed certificate in $GVM_CERT_FILENAME."
  else
    # Create a certificate request
    log_verbose "  Generating certificate request."
    certtool \
      --generate-request \
      --load-privkey "$GVM_KEY_FILENAME" \
      --outfile "$GVM_CERT_REQUEST_FILENAME" \
      --template "$GVM_CERT_TEMPLATE_FILENAME" \
      >> "$CERTTOOL_LOGFILE" 2>&1
    if [ $? -ne 0 ]
    then
      log_err "ERROR: Failed to create certificate request, see $CERTTOOL_LOGFILE for details. Aborting."
      exit 1
    fi

    log_write "Generated certificate request in $GVM_CERT_REQUEST_FILENAME."
  fi
}

sign_certificate ()
{
  log_verbose "Signing certificate request."

  if [ ! -s "$GVM_CERT_REQUEST_FILENAME" ]
  then
    log_err "ERROR: Failed to find certificate request in $GVM_CERT_REQUEST_FILENAME. Aborting."
    exit 1
  fi
  if [ ! -s "$GVM_SIGNING_CA_CERT_FILENAME" ]
  then
    log_err "ERROR: Failed to find CA certificate in $GVM_SIGNING_CA_CERT_FILENAME. Aborting."
    exit 1
  fi
  if [ ! -s "$GVM_SIGNING_CA_KEY_FILENAME" ]
  then
    log_err "ERROR: Failed to find CA key in $GVM_SIGNING_CA_KEY_FILENAME. Aborting."
    exit 1
  fi

  # Sign certificate
  certtool \
    --generate-certificate \
    --hash "$GVM_CERTIFICATE_SIGNALG" \
    --load-request "$GVM_CERT_REQUEST_FILENAME" \
    --outfile "$GVM_CERT_FILENAME" \
    --load-ca-certificate "$GVM_SIGNING_CA_CERT_FILENAME" \
    --load-ca-privkey "$GVM_SIGNING_CA_KEY_FILENAME" \
    --template "$GVM_CERT_TEMPLATE_FILENAME" >> "$CERTTOOL_LOGFILE" 2>&1
  if [ $? -ne 0 ]
  then
    log_err "ERROR: Failed to sign certificate, see $CERTTOOL_LOGFILE for details. Aborting."
    exit 1
  fi

  log_write "Signed certificate request in $GVM_CERT_REQUEST_FILENAME with CA certificate in $GVM_SIGNING_CA_CERT_FILENAME to generate certificate in $GVM_CERT_FILENAME"
}

# Install a certificate
# Where should the certificate and the key be installed to?
install_cert ()
{
  log_verbose "Installing certificate and key."

  if [ ! -s "$GVM_KEY_FILENAME" ] || [ ! -s "$GVM_CERT_FILENAME" ]
  then
    log_err "ERROR: Files to install ($GVM_KEY_FILENAME and $GVM_CERT_FILENAME) not found. Aborting."
    exit 1
  fi

  if [ ! -d "$GVM_CERT_LOCATION" ] || [ ! -d "$GVM_KEY_LOCATION" ]
  then
    log_verbose "Install destinations do not exist as directories, attempting to create them."
    set_up_directories
  fi


  if [ ! -w "$GVM_CERT_LOCATION" ] || [ ! -w "$GVM_KEY_LOCATION" ]
  then
    log_err "ERROR: Install destinations are not writable. Aborting."
    exit 1
  fi

  KEY_INSTALL="$GVM_KEY_LOCATION/${1}key.pem"
  if [ -f "$KEY_INSTALL" ] && [ $FORCE -ne 1 ]
  then
    echo "$KEY_INSTALL exists already, not overwriting."
    echo "Use '-f' parameter to overwrite existing files."
    exit 1
  else
    cp "$GVM_KEY_FILENAME" "$KEY_INSTALL"
    if [ $? -ne 0 ]
    then
      log_err "Failed to install $GVM_KEY_FILENAME to $KEY_INSTALL.  Aborting."
      exit 1
    else
      log_write "Installed private key to $KEY_INSTALL."
    fi
  fi

  CERT_INSTALL="$GVM_CERT_LOCATION/${1}cert.pem"
  if [ -f "$CERT_INSTALL" ] && [ $FORCE -ne 1 ]
  then
    echo "$CERT_INSTALL exists already, not overwriting."
    echo "Use '-f' parameter to overwrite existing files."
    exit 1
  else
    cp "$GVM_CERT_FILENAME" "$CERT_INSTALL"
    if [ $? -ne 0 ]
    then
      log_err "Failed to install $GVM_CERT_FILENAME to $CERT_INSTALL.  Aborting."
      exit 1
    else
      log_write "Installed certificate to $CERT_INSTALL."
    fi
  fi
}

verify ()
{
  ALL_OK=1

  if [ -d "$GVM_KEY_LOCATION" ]
  then
    echo "OK: Directory for keys ($GVM_KEY_LOCATION) exists."
  else
    echo "ERROR: Directory for keys ($GVM_KEY_LOCATION) not found!"
    ALL_OK=0
  fi
  if [ -d "$GVM_CERT_LOCATION" ]
  then
    echo "OK: Directory for certificates ($GVM_CERT_LOCATION) exists."
  else
    echo "ERROR: Directory for certificates ($GVM_CERT_LOCATION) not found!"
    ALL_OK=0
  fi
  if [ -s "$GVM_KEY_LOCATION/cakey.pem" ]
  then
    echo "OK: CA key found in $GVM_KEY_LOCATION/cakey.pem"
  else
    echo "ERROR: CA key not found in $GVM_KEY_LOCATION/cakey.pem"
    ALL_OK=0
  fi
  if [ -s "$GVM_CERT_LOCATION/cacert.pem" ]
  then
    echo "OK: CA certificate found in $GVM_CERT_LOCATION/cacert.pem"
  else
    echo "ERROR: CA certificate not found in $GVM_CERT_LOCATION/cacert.pem"
    ALL_OK=0
  fi

  # TODO: Check file permissions

  certtool \
    --verify \
    --load-ca-certificate $GVM_CERT_LOCATION/cacert.pem \
    --infile $GVM_CERT_LOCATION/cacert.pem \
    >> "$CERTTOOL_LOGFILE" 2>&1
  if [ $? -eq 0 ]
  then
    echo "OK: CA certificate verified."
  else
    echo "ERROR: CA certificate failed verification, see $CERTTOOL_LOGFILE for details. Aborting."
    ALL_OK=0
  fi

  for cert in $(find $GVM_CERT_LOCATION -name "*pem" ! -name cacert.pem)
  do
    certtool \
      --verify \
      --load-ca-certificate $GVM_CERT_LOCATION/cacert.pem \
      --infile $cert \
      >> "$CERTTOOL_LOGFILE" 2>&1
    if [ $? -eq 0 ]
    then
      echo "OK: Certificate $cert verified."
    else
      echo "ERROR: Certificate $cert failed verification, see $CERTTOOL_LOGFILE for details. Aborting."
    ALL_OK=0
    fi
  done

  echo
  if [ $ALL_OK -eq 1 ]
  then
    echo "OK: Your GVM certificate infrastructure passed validation."
  else
    echo "ERROR: Your GVM certificate infrastructure did NOT pass validation."
    echo "       See messages above for details."
    exit 1
  fi

  exit 0
}


# Clean up
clean_up ()
{
  if [ $USE_TEMP_DIR -eq 1 ]
  then
    if [ $DEBUG -ne 1 ]
    then
      log_write "Removing temporary directory $GVM_CERT_DIR."
      rm -rf $GVM_CERT_DIR
    else
      echo "DEBUG: Not removing $GVM_CERT_DIR in debug mode."
    fi
  fi
}

# Parse command line options
if [ $# -eq 0 ]
then
  print_help
fi

AUTO=0
AUTO_SKIP_CA=0
INSTALL_CERTIFICATE=0
INSTALL_CA=0
CREATE_CERTIFICATE=0
CREATE_SELF_SIGNED=0
CERTIFICATE_TYPE=0
CREATE_SERVER_CERTIFICATE=0
CREATE_CLIENT_CERTIFICATE=0
CREATE_CSR=0
CREATE_CA=0
SIGN_CERTIFICATE=0
VERIFY=0
VERBOSE=0
DEBUG=0
QUIET=0
FORCE=0

while getopts haAsiIcfdCSVvqe:ELrR OPTION
do
  case "$OPTION" in
    h)
      print_help
      ;;
    a)
      AUTO=1
      ;;
    A)
      AUTO_SKIP_CA=1
      ;;
    s)
      CREATE_CERTIFICATE=1
      CREATE_SELF_SIGNED=1
      ;;
    i)
      INSTALL_CERTIFICATE=1
      ;;
    I)
      INSTALL_CA=1
      ;;
    c)
      CREATE_CERTIFICATE=1
      CREATE_SELF_SIGNED=0
      ;;
    f)
      FORCE=1
      ;;
    d)
      DEBUG=1
      ;;
    C)
      CREATE_CA=1
      ;;
    S)
      SIGN_CERTIFICATE=1
      ;;
    v)
      VERBOSE=1
      ;;
    V)
      VERIFY=1
      ;;
    q)
      QUIET=1
      ;;
    e)
      CONFIGURATION_FILE="$OPTARG"
      ;;
    E)
      CERTIFICATE_TYPE=$SERVER_CERTIFICATE
      ;;
    L)
      CERTIFICATE_TYPE=$CLIENT_CERTIFICATE
      ;;
    r)
      CREATE_CSR=1
      ;;
    R)
      CERTIFICATE_TYPE=$CA_CERTIFICATE
      CREATE_CSR=1
      ;;
    \?)
      print_help
      ;;
  esac
done

if [ -n "$CONFIGURATION_FILE" ]
then
  if [ -r "$CONFIGURATION_FILE" ]
  then
    log_verbose "Reading configuration from $CONFIGURATION_FILE."
    . "$CONFIGURATION_FILE"
  else
    log_err "Configuration file $CONFIGURATION_FILE could not be read. Aborting."
    exit 1
  fi
fi

set_defaults

if [ $AUTO -eq 1 ]
then
  set_up

  CERTS_EXIST=0

  if [ $AUTO_SKIP_CA -eq 1 ]
  then
    if [ -s "$GVM_CERT_LOCATION/servercert.pem" ] \
      || [ -s "$GVM_CERT_LOCATION/clientcert.pem" ]
    then
      CERTS_EXIST=1
    fi
  else
    if [ -s "$GVM_CERT_LOCATION/cacert.pem" ] \
      || [ -s "$GVM_CERT_LOCATION/servercert.pem" ] \
      || [ -s "$GVM_CERT_LOCATION/clientcert.pem" ]
    then
      CERTS_EXIST=1
    fi
  fi

  if [ $CERTS_EXIST -eq 1 ]
  then
    if [ $FORCE -ne 1 ]
    then
      echo "Existing certificate infrastructure found, aborting."
      echo "Use '-f' parameter to overwrite existing certificates."
      exit 1
    fi
  fi

  log_verbose "Creating new certificate infrastructure in automatic mode."

  if [ $AUTO_SKIP_CA -ne 1 ]
  then
    CERTIFICATE_TYPE=$CA_CERTIFICATE
    CREATE_SELF_SIGNED=1
    set_prefix "ca"
    create_private_key "$GVM_KEY_FILENAME"
    create_certificate
    log_verbose "  CA certificate generated."
    install_cert "${GVM_CERT_PREFIX}"
    log_verbose "  CA certificate and key installed."
    CREATE_SELF_SIGNED=0
  else
    log_verbose "Skipping CA creation as requested."
  fi

  CERTIFICATE_TYPE=$SERVER_CERTIFICATE
  set_prefix "server"
  create_private_key "$GVM_KEY_FILENAME"
  create_certificate
  sign_certificate
  log_verbose "  Server certificate generated."
  install_cert "${GVM_CERT_PREFIX}"
  log_verbose "  Server certificate and key installed."

  CERTIFICATE_TYPE=$CLIENT_CERTIFICATE
  set_prefix "client"
  create_private_key "$GVM_KEY_FILENAME"
  create_certificate
  sign_certificate
  log_verbose "  Client certificate generated."
  install_cert "${GVM_CERT_PREFIX}"
  log_verbose "  Client certificate and key installed."

  clean_up

  exit 0
fi

if [ $VERIFY -eq 1 ]
then
  set_up
  verify
fi

if [ $CREATE_CSR -eq 1 ]
then
  set_up
  if [ $CERTIFICATE_TYPE -eq $CA_CERTIFICATE ]
  then
    set_prefix "ca"
  fi
  create_private_key "$GVM_KEY_FILENAME"
  create_certificate
fi

if [ $CREATE_CA -eq 1 ]
then
  set_up
  set_prefix "ca"
  create_private_key "$GVM_KEY_FILENAME"
  CERTIFICATE_TYPE=$CA_CERTIFICATE
  CREATE_SELF_SIGNED=1
  create_certificate
fi

if [ $INSTALL_CA -eq 1 ]
then
  set_up
  install_cert "ca"
fi

if [ $CREATE_CERTIFICATE -eq 1 ]
then
  set_up
  create_private_key "$GVM_KEY_FILENAME"
  create_certificate
  if [ $CREATE_SELF_SIGNED -ne 1 ]
  then
    sign_certificate
  fi

  if [ $INSTALL_CERTIFICATE -eq 1 ]
  then
    install_cert "$GVM_CERT_PREFIX"
  fi
fi

if [ $SIGN_CERTIFICATE -eq 1 ]
then
  set_up
  sign_certificate
fi

if [ $INSTALL_CERTIFICATE -eq 1 ]
then
  set_up
  install_cert "$GVM_CERT_PREFIX"
fi

# If the files have been installed, clean up the generation directory.
if [ $INSTALL_CERTIFICATE -eq 1 ] || [ $INSTALL_CA -eq 1 ]
then
  clean_up
fi

exit 0
