#!/bin/bash
# Begin /usr/sbin/make-ca
#
# Script to create p11-kit anchors, OpenSSL certs directory, GnuTLS certificate
# bundle, NSS shared DB, and Java cacerts from upstream certdata.txt and local
# sources
# 
# Authors: DJ Lucas
#          Bruce Dubbs

VERSION="0.7"

# Get/set defaults
if test -f /etc/make-ca.conf; then
    . /etc/make-ca.conf
else
    CERTDATA="certdata.txt"
    PKIDIR="/etc/pki"
    SSLDIR="/etc/ssl"
    CERTUTIL="/usr/bin/certutil"
    KEYTOOL="/opt/jdk/bin/keytool"
    OPENSSL="/usr/bin/openssl"
    ANCHORDIR="${PKIDIR}/anchors"
    CABUNDLE="${SSLDIR}/ca-bundle.crt"
    SMBUNDLE="${SSLDIR}/email-ca-bundle.crt"
    CSBUNDLE="${SSLDIR}/objsign-ca-bundle.crt"
    CERTDIR="${SSLDIR}/certs"
    KEYSTORE="${SSLDIR}/java"
    NSSDB="${PKIDIR}/nssdb"
    LOCALDIR="${SSLDIR}/local"
    DESTDIR=""
    URL="https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt"
fi

# Source must be downloaded over https
# Valid urls for download are below
# Defualt to NSS release brach

# https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
# https://hg.mozilla.org/projects/nss/raw-file/tip/lib/ckfw/builtins/certdata.txt
# https://hg.mozilla.org/mozilla-central/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
# https://hg.mozilla.org/releases/mozilla-beta/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
# https://hg.mozilla.org/releases/mozilla-aurora/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt


# Some data in the certs have UTF-8 characters
# It doesn't really matter which locale, change if you like
export LANG=en_US.utf8

TEMPDIR=$(mktemp -d)
WORKDIR="${TEMPDIR}/work"
WITH_NSS=1
WITH_JAVA=1
CERTDATAY=0
FORCE=0
GET=0
REBUILD=0

function get_args(){
  while test -n "${1}" ; do
    case "${1}" in
      -C | --certdata)
        if test "${REBUILD}" == "0" -a "${GET}" == "0"; then
          check_arg $1 $2
          CERTDATA="${2}"
          CERTDATAY="1"
          shift 2
        else
          echo "Error: ${1} cannot be used with the -r/--rebuild or -g/--get switches."
          exit 3
        fi
        if test ! -f "${CERTDATA}" -a "${GET}" == "0"; then
          echo "Error: ${CERTDATA} not found!"
          exit 3
        fi
      ;;
      -D | --destdir)
        check_arg $1 $2
        DESTDIR="${2}"
        shift 2
      ;;
      -P | --pkidir)
        check_arg $1 $2
        PKIDIR="${2}"
        ANCHORDIR="${PKIDIR}/anchors"
        NSSDB="${PKIDIR}/nssdb"
        echo "${@}" | grep -e "-a " -e "--anchordir" \
                           -e "-n " -e "--nssdb" > /dev/null
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -a/--anchordir or -n/--nssdb switches."
          echo ""
          exit 3
        fi
        shift 2
      ;;
      -S | --ssldir)
        check_arg $1 $2
        SSLDIR="${2}"
        CABUNDLE="${SSLDIR}/ca-bundle.crt"
        CERTDIR="${SSLDIR}/certs"
        KEYSTORE="${SSLDIR}/java"
        LOCALDIR="${SSLDIR}/local"
        echo "${@}" | grep -e "-c " -e "--cafile" \
                           -e "-d " -e "--cadir"  \
                           -e "-j " -e "--javacerts" > /dev/null
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -c/--cafile, -d/--cadir, or"
          echo "-j/--javacerts switches."
          echo ""
          exit 3
        fi

        shift 2
      ;;
      -a | --anchordir)
        check_arg $1 $2
        ANCHORDIR="${2}"
        echo "${@}" | grep -e "-P " -e "--pkidir" > /dev/null
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -P/--pkidir switch."
          echo ""
          exit 3
        fi
        shift 2
      ;;
      -c | --cafile)
        check_arg $1 $2
        CABUNDLE="${2}"
        echo "${@}" | grep -e "-S " -e "--ssldir" > /dev/null
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -S/--ssldir switch."
          echo ""
          exit 3
        fi
        shift 2
      ;;
      -d | --cadir)
        check_arg $1 $2
        CADIR="${2}"
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -S/--ssldir switch."
          echo ""
          exit 3
        fi
        shift 2
      ;;
      -g | --get)
        if test "${REBUILD}" == "0" -a "${CERTDATAY}" == "0"; then
          GET="1"
          CERTDATA="${TEMPDIR}/certdatanew.txt"
          shift 1
        else
          echo "Error: ${1} cannot be used with the -r/--rebuild or -C/--certdata switches."
          exit 3
        fi
      ;;
      -j | --javacerts)
        check_arg $1 $2
        KEYSTORE="${2}"
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -S/--ssldir switch."
          echo ""
          exit 3
        fi
        shift 2
      ;;
      -l | --localdir)
        check_arg $1 $2
        LOCALDIR="${2}"
        shift 2
      ;;
      -n | --nssdb)
        check_arg $1 $2
        NSSDB="${2}"
        echo "${@}" | grep -e "-P " -e "--pkidir" > /dev/null
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -P/--pkidir switch."
          echo ""
          exit 3
        fi
        shift 2
      ;;
      -p | --proxy)
        check_arg $1 $2
        PROXY="${2}"
        shift 2
      ;;
      -k | --keytool)
        check_arg $1 $2
        KEYTOOL="${2}"
        shift 2
      ;;
      -r | --rebuild)
        if test "${CERTDATAY}" == "0" -a "${GET}" == "0"; then
          REBUILD="1"
          FORCE="1"
          shift 1
        else
          echo "Error: ${1} cannot be used with the -C/--certdata or -g/--get switches."
          exit 3
        fi
      ;;
      -s | --openssl)
        check_arg $1 $2
        OPENSSL="${2}"
        shift 2
      ;;
      -t | --certutil)
        check_arg $1 $2
        CERTUTIL="${2}"
        shift 2
      ;;
      -f | --force)
        FORCE="1"
        shift 1
      ;;
      -h | --help)
        showhelp
        exit 0
      ;;
      -v | --version)
        echo -e "$(basename ${0}) ${VERSION}\n"
        exit 0
      ;;
      *)
        showhelp
        exit 1
      ;;
    esac
  done
}

function check_arg(){
  echo "${2}" | grep -v "^-" > /dev/null
  if [ -z "$?" -o ! -n "$2" ]; then
    echo "Error:  $1 requires a valid argument."
    exit 1
  fi
}

function showhelp(){
  echo ""
  echo "`basename ${0}` is a utility to deliver and manage a complete PKI configuration"
  echo "for workstaitons and servers using only standard Unix utilities and OpenSSL. It"
  echo "will optionally generate keystores for OpenJDK and NSS if already installed,"
  echo "using a Mozilla cacerts.txt or like formatted file. It was originally developed"
  echo "for use with Linux From Scratch to minimize dependencies for early system"
  echo "build, but has been written to be generic enough for any Linux distribution."
  echo ""
  echo "        -C, --certdata [certdata.txt]"
  echo "                         The location of the certificates source."
  echo ""
  echo "        -D, --destdir [/]"
  echo "                         Change the output directory and use relative"
  echo "                         paths for all other values."
  echo ""
  echo "        -P, --pkidir [/etc/pki]"
  echo "                         The output PKI directory - Cannot be used with"
  echo "                         the -a / --anchordir or -n / --nssdb switches"
  echo ""
  echo "        -S, --ssldir [/etc/ssl]"
  echo "                         The output SSL root direcotry - Cannot be used"
  echo "                         with the -c / --cafile, -d / --cadir, or"
  echo "                         -j / --javacerts switches"
  echo ""
  echo "        -a, --anchordir [\$PKIDIR/anchors]"
  echo "                         The output directory for OpenSSL trusted"
  echo "                         CA certificates used as trust anchors."
  echo ""
  echo "        -c, --cafile [\$SSLDIR/ca-bundle.crt]"
  echo "                         The output filename for the PEM formated bundle"
  echo ""
  echo "        -d, --cadir [\$SSLDIR/certs]"
  echo "                         The output directory for the OpenSSL trusted"
  echo "                         CA certificates"
  echo ""
  echo "        -j, --javacerts [\$SSLDIR/java"
  echo "                         The output directory for the Java"
  echo "                         cacerts.{jks,p12} files"
  echo ""
  echo "        -l, --localdir [\$SSLDIR/local]"
  echo "                         The path to a local set of OpenSSL trusted"
  echo "                         certificates, used to both override trust bits"
  echo "                         from upstream sources and provide locally"
  echo "                         provided certifiates."
  echo ""
  echo "        -n, --nssdb [\$PKIDIR/nssdb]"
  echo "                         The output path for the shared NSS DB"
  echo ""
  echo "        -p, --proxy [URI:PORT]"
  echo "                         Use proxy server for download"
  echo ""
  echo "        -k, --keytool [PATH]"
  echo "                         The path to the java keytool utility"
  echo ""
  echo "        -s, --openssl [PATH]"
  echo "                         The path to the openssl utility"
  echo ""
  echo "        -t, --certutil [PATH]"
  echo "                         The path the certutil utility"
  echo ""
  echo "        -f, --force      Force run, even if source is not newer"
  echo ""
  echo "        -g, --get        Download certdata.txt directly from Mozilla's"
  echo "                         Mecurial server."
  echo ""
  echo "        -h, --help       Show this help message and exit"
  echo ""
  echo "        -r, --rebuild    Rebuild the enitre PKI tree using the previous"
  echo "                         certdata.txt file."
  echo ""
  echo "        -v. --version    Show version information and exit"
  echo ""
  echo "Example: `basename ${0}` -f -C ~/certdata.txt"
  echo ""
}

# Convert CKA_TRUST values to trust flags for certutil
function convert_trust(){
  case $1 in
    CKT_NSS_TRUSTED_DELEGATOR)
      echo "C"
    ;;
    CKT_NSS_NOT_TRUSTED)
      echo "p"
    ;;
    CKT_NSS_MUST_VERIFY_TRUST)
      echo ""
    ;;
  esac
}

function convert_trust_arg(){
  case $1 in
    C)
      case $2 in
        sa)
          echo "-addtrust serverAuth"
        ;;
        sm)
          echo "-addtrust emailProtection"
        ;;
        cs)
          echo "-addtrust codeSigning"
        ;;
        ca)
          echo "-addtrust clientAuth"
        ;;
      esac
    ;;
    p)
      case $2 in
        sa)
          echo "-addreject serverAuth"
        ;;
        sm)
          echo "-addreject emailProtection"
        ;;
        cs)
          echo "-addreject codeSigning"
        ;;
        ca)
          echo "-addreject clientAuth"
        ;;
      esac
    ;;
    *)
      echo ""
    ;;
  esac
}
    
# Define p11-kit ext value constants (see p11-kit API documentation)
get-p11-val() {
  case $1 in
    p11sasmcs)
      p11value="0%2a%06%03U%1d%25%01%01%ff%04 0%1e%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%01%06%08%2b%06%01%05%05%07%03%03"
    ;;

    p11sasm)
      p11value="0 %06%03U%1d%25%01%01%ff%04%160%14%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%01"
    ;;

    p11sacs)
      p11value="0 %06%03U%1d%25%01%01%ff%04%160%14%06%08%2b%06%01%05%05%07%03%01%06%08%2b%06%01%05%05%07%03%03"
    ;;

    p11sa)
      p11value="0%16%06%03U%1d%25%01%01%ff%04%0c0%0a%06%08%2b%06%01%05%05%07%03%01"
    ;;

    p11smcs)
      p11value="0 %06%03U%1d%25%01%01%ff%04%160%14%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%03"
    ;;

    p11sm)
      p11value="0%16%06%03U%1d%25%01%01%ff%04%0c0%0a%06%08%2b%06%01%05%05%07%03%04"
    ;;

    p11cs)
      p11value="0%16%06%03U%1d%25%01%01%ff%04%0c0%0a%06%08%2b%06%01%05%05%07%03%03"
    ;;

    p11)
      p11value="0%18%06%03U%1d%25%01%01%ff%04%0e0%0c%06%0a%2b%06%01%04%01%99w%06%0a%10"
    ;;
  esac
}

# Process command line arguments
get_args $@

test -x "${CERTUTIL}" || WITH_NSS=0
test -x "${KEYTOOL}" || WITH_JAVA=0
test ! -x "${OPENSSL}" && echo "OpenSSL not found at ${OPENSSL}. Exiting..." &&
exit 1

mkdir -p "${TEMPDIR}"/{certs,ssl/{certs,java},pki/{nssdb,anchors},work}

# Download certdata.txt if selected
if test "${GET}" == "1"; then
  HOST=$(echo "${URL}" | /usr/bin/cut -d / -f 3)
  _url=$(echo "${URL}" | sed 's@raw-file@log@')
  SARGS="-ign_eof -connect ${HOST}:443"
  if test "${PROXY}x" != "x"; then
    SARGS="${SARGS} -proxy ${PROXY}"
  fi
  echo GET ${_url} | \
  ${OPENSSL} s_client ${SARGS} 2>/dev/null > "${TEMPDIR}/certdata.txt.log"
  unset _url

  # Error out here if we couldn't get the file
  grep -m1 "<i>" "${TEMPDIR}/certdata.txt.log" 2>&1>/dev/null
  if test "$?" -gt 0; then
    echo "Unable to get revision from server! Exiting."
    exit 1
  fi

  # See if we need to update before downloading the file
  REVISION=$(grep -m1 "<i>" "${TEMPDIR}/certdata.txt.log" | cut -d "<" -f 1)
  if test -e "${DESTDIR}${SSLDIR}/certdata.txt"; then
    OLDVERSION=$(grep "^# Revision:" "${DESTDIR}${SSLDIR}/certdata.txt" | \
                      cut -d ":" -f 2)
    if test "${OLDVERSION}x" == "${REVISION}x" -a "${FORCE}" == "0"; then
      echo "No update required! Use --force to update anyway."
      exit 0
    fi
  fi

  # Download the new file
  echo GET ${URL} | \
  ${OPENSSL} s_client ${SARGS} 2>/dev/null >> "${CERTDATA}"
  _line=$(( $(grep -n "certdata.txt" "${CERTDATA}" | cut -d ":" -f 1) - 1))
  sed -e "1,${_line}d" -i "${CERTDATA}"
  sed "1i # Revision:${REVISION}" -i "${CERTDATA}"
fi

if test "${REBUILD}" == "1"; then
  CERTDATA="${DESTDIR}${SSLDIR}/certdata.txt"
fi

if test ! -r "${CERTDATA}"; then
  echo "${CERTDATA} was not found. The certdata.txt file must be in the local"
  echo "directory, speficied with the -C/--certdata switch, or downloaded with"
  echo "the -g/--get switch."
  exit 1
fi

REVISION=$(grep "^# Revision" "${CERTDATA}" | cut -d ":" -f 2)

if test "${REVISION}x" == "x"; then
  echo "WARNING! ${CERTDATA} has no 'Revision' value."
  echo "Will run conversion unconditionally."
  sleep 2
  REVISION="$(date -u +%Y%m%d-%H%M)"
  echo "# Revision:${REVISION}" > "${WORKDIR}/certdata.txt"
else
  if test "${FORCE}" == "1"; then
    echo "Output forced. Will run conversion unconditionally."
    sleep 2
  elif test "${DESTDIR}x" == "x"; then
    test -f "${CABUNDLE}" &&
    OLDVERSION=$(grep "^# Revision:" "${CABUNDLE}" | cut -d ":" -f 2)
    if test "${OLDVERSION}x" == "${REVISION}x"; then
      echo "No update required! Use --force to update anyway."
      exit 0
    fi
  fi
fi

cat "${CERTDATA}" >> "${WORKDIR}/certdata.txt"
pushd "${WORKDIR}" > /dev/null

if test "${WITH_NSS}" == "1"; then
  # Create a blank NSS DB
  "${CERTUTIL}" -N --empty-password -d "sql:${TEMPDIR}/pki/nssdb"
fi

# Get a list of starting lines for each cert
CERTBEGINLIST=`grep -n "^# Certificate" "${WORKDIR}/certdata.txt" | \
                      cut -d ":" -f1`

# Dump individual certs to temp file
for certbegin in ${CERTBEGINLIST}; do
  awk "NR==$certbegin,/^CKA_TRUST_STEP_UP_APPROVED/" "${WORKDIR}/certdata.txt" \
      > "${TEMPDIR}/certs/${certbegin}.tmp" 
done

unset CERTBEGINLIST certbegin

for tempfile in ${TEMPDIR}/certs/*.tmp; do
  # Get a name for the cert
  certname="$(grep "^# Certificate" "${tempfile}" | cut -d '"' -f 2)"

  # Determine certificate trust values for SSL/TLS, S/MIME, and Code Signing
  satrust="$(convert_trust `grep '^CKA_TRUST_SERVER_AUTH' ${tempfile} | \
                  cut -d " " -f 3`)"
  smtrust="$(convert_trust `grep '^CKA_TRUST_EMAIL_PROTECTION' ${tempfile} | \
                  cut -d " " -f 3`)"
  cstrust="$(convert_trust `grep '^CKA_TRUST_CODE_SIGNING' ${tempfile} | \
                  cut -d " " -f 3`)"
  # Not currently included in NSS certdata.txt
  #catrust="$(convert_trust `grep '^CKA_TRUST_CLIENT_AUTH' ${tempfile} | \
  #                cut -d " " -f 3`)"

  # Get args for OpenSSL trust settings
  saarg="$(convert_trust_arg "${satrust}" sa)"
  smarg="$(convert_trust_arg "${smtrust}" sm)"
  csarg="$(convert_trust_arg "${cstrust}" cs)"
  # Not currently included in NSS certdata.txt
  #caarg="$(convert_trust_arg "${catrust}" ca)"

  # Convert to a PEM formated certificate
  printf $(awk '/^CKA_VALUE/{flag=1;next}/^END/{flag=0}flag{printf $0}' \
  "${tempfile}") | "${OPENSSL}" x509 -text -inform DER -fingerprint \
  > tempfile.crt

  # Get individual values for certificates
  certkey="$(${OPENSSL} x509 -in tempfile.crt -noout -pubkey)"
  certcer="$(${OPENSSL} x509 -in tempfile.crt)"
  certtxt="$(${OPENSSL} x509 -in tempfile.crt -noout -text)"

  # Get p11-kit label, oid, and values
  p11label="$(grep -m1 "Issuer" ${tempfile} | grep -o CN=.*$ | \
              cut -d ',' -f 1 | sed 's@CN=@@')"

  # if distrusted at all, x-distrusted
  if test "${satrust}" == "p" -o "${smtrust}" == "p" -o "${cstrust}" == "p"
  then
      # if any distrusted, x-distrusted
      p11trust="x-distrusted: true"
      p11oid="1.3.6.1.4.1.3319.6.10.1"
      p11value="0.%06%0a%2b%06%01%04%01%99w%06%0a%01%04 0%1e%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%01%06%08%2b%06%01%05%05%07%03%03"
  else
      p11trust="trusted: true"
      p11oid="2.5.29.37"
      trustp11="p11"
      if test "${satrust}" == "C"; then
          trustp11="${trustp11}sa"
      fi
      if test "${smtrust}" == "C"; then
          trustp11="${trustp11}sm"
      fi
      if test "${cstrust}" == "C"; then
          trustp11="${trustp11}cs"
      fi
      get-p11-val "${trustp11}"
  fi

  # Get a hash for the cert
  keyhash=$("${OPENSSL}" x509 -noout -in tempfile.crt -hash)

  # Print information about cert
  echo "Certificate:  ${certname}"
  echo "Keyhash:      ${keyhash}"

  # Place certificate into trust anchors dir
  anchorfile="${TEMPDIR}/pki/anchors/${keyhash}.pem"
  echo "[p11-kit-object-v1]" >> "${anchorfile}"
  echo "label: \"${p11label}\"" >> "${anchorfile}"
  echo "class: x-certificate-extension" >> "${anchorfile}"
  echo "object-id: ${p11oid}" >> "${anchorfile}"
  echo "value: \"${p11value}\"" >> "${anchorfile}"
  echo "modifiable: false" >> "${anchorfile}"
  echo "${certkey}" >> "${anchorfile}"
  echo "" >> "${anchorfile}"
  echo "[p11-kit-object-v1]" >> "${anchorfile}"
  echo "label: \"${p11label}\"" >> "${anchorfile}"
  echo "${p11trust}" >> "${anchorfile}"
  echo "nss-mozilla-ca-policy: true" >> "${anchorfile}"
  echo "modifiable: false" >> "${anchorfile}"
  echo "${certcer}" >> "${anchorfile}"
  echo "${certtxt}" | sed 's@^@#@' >> "${anchorfile}"
  echo "Added to p11-kit anchor directory with trust '${satrust},${smtrust},${cstrust}'."
  
  # Import certificate into the temporary certificate directory with
  # trust arguments
  "${OPENSSL}" x509 -in tempfile.crt -text -fingerprint \
      -setalias "${certname}" ${saarg} ${smarg} ${csarg}    \
      > "${TEMPDIR}/ssl/certs/${keyhash}.pem"
  echo "Added to OpenSSL certificate directory with trust '${satrust},${smtrust},${cstrust}'."

  # Import all certificates with trust args to the temporary NSS DB
  if test "${WITH_NSS}" == "1"; then
    "${CERTUTIL}" -d "sql:${TEMPDIR}/pki/nssdb" -A \
                  -t "${satrust},${smtrust},${cstrust}" \
                  -n "${certname}" -i tempfile.crt
    echo "Added to NSS shared DB with trust '${satrust},${smtrust},${cstrust}'."
  fi

  # Import all certificates with trust args to the java cacerts.p12 file
  if test "${WITH_JAVA}" == "1"; then
    # Remove existing certificate
    "${KEYTOOL}" -delete -noprompt -alias "${certname}"       \
                 -keystore "${TEMPDIR}/ssl/java/cacerts.p12"  \
                 -storepass 'changeit' 2>&1> /dev/null
    # Determine ExtendedKeyUsage
    EKU=""
    EKUVAL=""
    if test "${satrust}" == "C"; then EKU="serverAuth"; fi
    if test "${smtrust}" == "C"; then
      if test "${EKU}" == ""; then
        EKU="clientAuth"
      else
        EKU="${EKU},clientAuth"
      fi
    fi
    if test "${cstrust}" == "C"; then
      if test "${EKU}" == ""; then
        EKU="codeSigning"
      else
        EKU="${EKU},codeSigning"
      fi
    fi
    if test "${EKU}" != ""; then
      EKUVAL="-ext EKU=${EKU}"
      "${KEYTOOL}" -importcert -file tempfile.crt -storetype PKCS12     \
                   -noprompt -alias "${certname}" -storepass 'changeit' \
                   -keystore "${TEMPDIR}/ssl/java/cacerts.p12" $EKUVAL  \
                   2>&1> /dev/null | \
      sed -e "s@Certificate was a@A@" \
          -e 's@keystore@Java cacerts (PKCS#12) with trust '${satrust},${smtrust},${cstrust}'.@' \
          | sed 's@p@@'
      unset EKU
      unset EKUVAL
    fi
  fi

  # Clean up the directory and environment as we go
  rm -f tempfile.crt
  unset keyhash subject certname
  unset satrust smtrust cstrust catrust sarg smarg csarg caarg
  unset p11trust p11oid p11value trustp11 certkey certcer certtxt
  echo -e "\n"
done
unset tempfile

# Sanity check
count=$(ls "${TEMPDIR}"/ssl/certs/*.pem | wc -l)
# Historically there have been between 152 and 190 certs
# A minimum of 150 should be safe for a rudimentry sanity check
if test "${count}" -lt "150" ; then
    echo "Error! Only ${count} certificates were generated!"
    echo "Exiting without update!"
    echo ""
    echo "${TEMPDIR} is the temporary working directory"
    exit 2
fi
unset count

# Install NSS Shared DB
if test "${WITH_NSS}" == "1"; then
  sed -e "s@${TEMPDIR}/pki/nssdb@${NSSDB}@"              \
      -e 's/library=/library=libnsssysinit.so/'          \
      -e 's/Flags=internal/Flags=internal,moduleDBOnly/' \
      -i "${TEMPDIR}/pki/nssdb/pkcs11.txt" 
  test -d "${DESTDIR}${NSSDB}" && rm -rf "${DESTDIR}${NSSDB}"
  install -dm755 "${DESTDIR}${NSSDB}" 2>&1>/dev/null
  install -m644 "${TEMPDIR}"/pki/nssdb/{cert9.db,key4.db,pkcs11.txt} \
                 "${DESTDIR}${NSSDB}" 
fi

# Install anchors in $ANCHORDIR
test -d "${DESTDIR}${ANCHORDIR}" && rm -rf "${DESTDIR}${ANCHORDIR}"
install -dm755 "${DESTDIR}${ANCHORDIR}" 2>&1>/dev/null
install -m644 "${TEMPDIR}"/pki/anchors/*.pem "${DESTDIR}${ANCHORDIR}"

# Install certificates in $CERTDIR
test -d "${DESTDIR}${CERTDIR}" && rm -rf "${DESTDIR}${CERTDIR}"
install -dm755 "${DESTDIR}${CERTDIR}" 2>&1>/dev/null
install -m644 "${TEMPDIR}"/ssl/certs/*.pem "${DESTDIR}${CERTDIR}"

# Install Java cacerts.p12 in ${KEYSTORE}
test -f "${DESTDIR}${KEYSTORE}/cacerts.p12" &&
        rm -f "${DESTDIR}${KEYSTORE}/cacerts.p12"
install -dm755 "${DESTDIR}${KEYSTORE}"
install -m644 "${TEMPDIR}/ssl/java/cacerts.p12" "${DESTDIR}${KEYSTORE}"

# Import any certs in $LOCALDIR
# Don't do any checking, just trust the admin
if test -d "${LOCALDIR}"; then
  echo "Processing local certificates..."
  for cert in `find "${LOCALDIR}" -name "*.pem"`; do
    # Get some information about the certificate
    keyhash=$("${OPENSSL}" x509 -noout -in "${cert}" -hash)
    subject=$("${OPENSSL}" x509 -noout -in "${cert}" -subject)
    count=1
    while test "${count}" -lt 10; do
      echo "${subject}" | cut -d "/" -f "${count}" | grep "CN=" >/dev/null \
           && break
      let count++
    done
    certname=$(echo "${subject}" | cut -d "/" -f "${count}" | sed 's@CN=@@')

    echo "Certificate:  ${certname}"
    echo "Keyhash:      ${keyhash}"

    # Get trust information
    trustlist=$("${OPENSSL}" x509 -in "${cert}" -text -trustout | \
                       grep -A1 "Trusted Uses")
    satrust=""
    smtrust=""
    cstrust=""
    catrust=""
    satrust=$(echo "${trustlist}" | \
              grep "TLS Web Server" 2>&1> /dev/null && echo "C")
    smtrust=$(echo "${trustlist}" | \
              grep "E-mail Protection" 2>&1 >/dev/null && echo "C")
    cstrust=$(echo "${trustlist}" | \
              grep "Code Signing" 2>&1 >/dev/null && echo "C")
    catrust=$(echo "${trustlist}" | \
              grep "Client Auth" 2>&1 >/dev/null && echo "C")

    # Get reject information
    rejectlist=$("${OPENSSL}" x509 -in "${cert}" -text -trustout | \
                     grep -A1 "Rejected Uses")
    if test "${satrust}" == ""; then satrust=$(echo "${rejectlist}" | \
              grep "TLS Web Server" 2>&1> /dev/null && echo "p"); fi
    if test "${smtrust}" == ""; then smtrust=$(echo "${rejectlist}" | \
              grep "E-mail Protection" 2>&1> /dev/null && echo "p"); fi
    if test "${cstrust}" == ""; then cstrust=$(echo "${rejectlist}" | \
              grep "Code Signing" 2>&1> /dev/null && echo "p"); fi
    if test "${catrust}" == ""; then catrust=$(echo "${rejectlist}" | \
              grep "Client Auth" 2>&1> /dev/null && echo "p"); fi


    # Place certificate into trust anchors dir
    p11label="$(grep -m1 "Issuer" ${cert} | grep -o CN=.*$ | \
                cut -d ',' -f 1 | sed 's@CN=@@')"

    # if distrusted at all, x-distrusted
    if test "${satrust}" == "p" -o "${smtrust}" == "p" -o "${cstrust}" == "p"
    then
        # if any distrusted, x-distrusted
        p11trust="x-distrusted: true"
        p11oid="1.3.6.1.4.1.3319.6.10.1"
        p11value="0.%06%0a%2b%06%01%04%01%99w%06%0a%01%04 0%1e%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%01%06%08%2b%06%01%05%05%07%03%03"
    else
        p11trust="trusted: true"
        p11oid="2.5.29.37"
        trustp11="p11"
        if test "${satrust}" == "C"; then
            trustp11="${trustp11}sa"
        fi
        if test "${smtrust}" == "C"; then
            trustp11="${trustp11}sm"
        fi
        if test "${cstrust}" == "C"; then
            trustp11="${trustp11}cs"
        fi
        get-p11-val "${trustp11}"
    fi

    anchorfile="${DESTDIR}${ANCHORDIR}/${keyhash}.pem"

    echo "[p11-kit-object-v1]" >> "${anchorfile}"
    echo "label: \"${p11label}\"" >> "${anchorfile}"
    echo "class: x-certificate-extension" >> "${anchorfile}"
    echo "object-id: ${p11oid}" >> "${anchorfile}"
    echo "value: \"${p11value}\"" >> "${anchorfile}"
    echo "modifiable: false" >> "${anchorfile}"
    echo "${certkey}" >> "${anchorfile}"
    echo "" >> "${anchorfile}"
    echo "[p11-kit-object-v1]" >> "${anchorfile}"
    echo "label: \"${p11label}\"" >> "${anchorfile}"
    echo "${p11trust}" >> "${anchorfile}"
    echo "modifiable: false" >> "${anchorfile}"
    echo "${certcer}" >> "${anchorfile}"
    echo "${certtxt}" | sed 's@^@#@' >> "${anchorfile}"
    echo "Added to p11-kit anchor directory with trust '${satrust},${smtrust},${cstrust}'."

    # Install into OpenSSL certificate store

    # Get args for OpenSSL trust settings
    saarg="$(convert_trust_arg "${satrust}" sa)"
    smarg="$(convert_trust_arg "${smtrust}" sm)"
    csarg="$(convert_trust_arg "${cstrust}" cs)"
    # Not currently included in NSS certdata.txt
    #caarg="$(convert_trust_arg "${catrust}" ca)"

    "${OPENSSL}" x509 -in "${cert}" -text -fingerprint                    \
                      -setalias "${certname}" ${saarg} ${smarg} ${csarg}  \
                      >> "${DESTDIR}${CERTDIR}/${keyhash}.pem"
    echo "Added to OpenSSL certificate directory with trust '${satrust},${smtrust},${cstrust},${catrust}'."

    # Add to Shared NSS DB
    if test "${WITH_NSS}" == "1"; then
      "${OPENSSL}" x509 -in "${cert}" -text -fingerprint | \
      "${CERTUTIL}" -d "sql:${DESTDIR}${NSSDB}" -A                   \
                    -t "${satrust},${smtrust},${cstrust}"  \
                    -n "${certname}"
      echo "Added to NSS shared DB with trust '${satrust},${smtrust},${cstrust}'."
    fi
    # Import certificate (with trust args) into the java cacerts.p12 file
    if test "${WITH_JAVA}" == "1"; then
      # Remove existing certificate
      "${KEYTOOL}" -delete -noprompt -alias "${certname}"         \
                   -keystore "${DESTDIR}${KEYSTORE}/cacerts.p12"  \
                   -storepass 'changeit' 2>&1> /dev/null
      # Determing ExtendedKeyUsage
      EKU=""
      if test "${satrust}" == "C"; then EKU="serverAuth"; fi
      if test "${catrust}" == "C"; then
        if test "${EKU}" == ""; then
          EKU="clientAuth"
        else
          EKU="${EKU},clientAuth"
        fi
      fi
      if test "${cstrust}" == "C"; then
        if test "${EKU}" == ""; then
          EKU="codeSigning"
        else
          EKU="${EKU},codeSigning"
        fi
      fi
      if test "${EKU}" != ""; then
        EKUVAL="-ext EKU=${EKU}"
        "${OPENSSL}" x509 -in "${cert}" -text -fingerprint                    \
                     -setalias "${certname}" > "${TEMPDIR}/tempcert.pem"

        "${KEYTOOL}" -importcert -noprompt -alias "${certname}"    \
                     -keystore "${DESTDIR}${KEYSTORE}/cacerts.p12" \
                     -storepass 'changeit' $EKUVAL                 \
                     -file "${TEMPDIR}/tempcert.pem"               \
                     2>&1> /dev/null | \
        sed -e "s@Certificate was a@A@" \
            -e 's@keystore@Java cacerts (PKCS#12) with trust '${satrust},${smtrust},${cstrust}'.@' \
            | sed 's@p@@'
        rm -f "${TEMPDIR}/tempcert.pem"
        unset EKU
        unset EKUVAL
      fi
    fi

    unset keyhash subject count certname
    unset trustlist rejectlist satrust smtrust cstrust catrust
    unset p11trust p11oid p11value trustp11 certkey certcer certtxt
    echo ""

  done
  unset cert
fi

# Build cacerts.jks and ca-bundle.crt
# Generate the bundle
bundlefile=`basename "${CABUNDLE}"`
bundledir=`echo "${CABUNDLE}" | sed "s@/${bundlefile}@@"`
install -vdm755 "${DESTDIR}${bundledir}" 2>&1>/dev/null
rm -f "${DESTDIR}${CABUNDLE}"
rm -f "${DESTDIR}${SMBUNDLE}"
rm -f "${DESTDIR}${CSBUNDLE}"
rm -f "${DESTDIR}${KEYSTORE}/cacerts.jks"


echo "# Revision:${REVISION}" > "${DESTDIR}${CABUNDLE}"
echo "# Revision:${REVISION}" > "${DESTDIR}${SMBUNDLE}"
echo "# Revision:${REVISION}" > "${DESTDIR}${CSBUNDLE}"

echo "Processing certs for Java (JKS) and GNUTLS stores..."
# Generate the bundle

for cert in `find "${DESTDIR}${CERTDIR}" -name "*.pem"`; do
  # Get some information about the certificate
  keyhash=$("${OPENSSL}" x509 -noout -in "${cert}" -hash)
  certname=$(grep "Alias" "${cert}")

  # Get trust information
  trustlist=$("${OPENSSL}" x509 -in "${cert}" -text -trustout | \
                     grep -A1 "Trusted Uses")
  satrust=""
  smtrust=""
  cstrust=""
  satrust=$(echo "${trustlist}" | \
            grep "TLS Web Server" 2>&1> /dev/null && echo "C")
  smtrust=$(echo "${trustlist}" | \
            grep "E-mail Protection" 2>&1 >/dev/null && echo "C")
  cstrust=$(echo "${trustlist}" | \
            grep "Code Signing" 2>&1 >/dev/null && echo "C")

  if test "${satrust}x" == "Cx"; then
    echo ""
    echo "${certname}" | sed 's@Alias:@Certificate: @'
    echo "Keyhash:      ${keyhash}"

    # Append to the CA bundle
    "${OPENSSL}" x509 -in "${cert}" -text -fingerprint \
         > "${TEMPDIR}/ssl/certs/${keyhash}.pem"
    cat "${TEMPDIR}/ssl/certs/${keyhash}.pem" >> "${DESTDIR}${CABUNDLE}"
    echo "Added to GnuTLS certificate bundle."

    # Add to Java keystore (JKS)
    if test "${WITH_JAVA}" == "1"; then
      # Remove certificate if it already exists
      "${KEYTOOL}" -delete -noprompt -alias "${certname}"        \
                   -keystore "${DESTDIR}${KEYSTORE}/cacerts.jks" \
                   -storepass 'changeit' 2>&1> /dev/null
      # Import it
      "${KEYTOOL}" -importcert -file "${TEMPDIR}/ssl/certs/${keyhash}.pem" \
                   -noprompt -alias "${certname}" -storetype JKS           \
                   -keystore "${DESTDIR}${KEYSTORE}/cacerts.jks"           \
                   -storepass 'changeit' 2>&1> /dev/null |        \
      sed -e 's@Certificate was a@A@' -e 's@keystore@Java (JKS) keystore.@'
    fi
  fi
  if test "${smtrust}x" == "Cx"; then
    echo ""
    echo "${certname}" | sed 's@Alias:@Certificate: @'
    echo "Keyhash:      ${keyhash}"

    # Append to the s-mime bundle
    "${OPENSSL}" x509 -in "${cert}" -text -fingerprint \
         > "${TEMPDIR}/ssl/certs/${keyhash}.pem"
    cat "${TEMPDIR}/ssl/certs/${keyhash}.pem" >> "${DESTDIR}${SMBUNDLE}"
    echo "Added to s-mime certificate bundle."
  fi
  if test "${cstrust}x" == "Cx"; then
    echo ""
    echo "${certname}" | sed 's@Alias:@Certificate: @'
    echo "Keyhash:      ${keyhash}"

    # Append to the code signing bundle
    "${OPENSSL}" x509 -in "${cert}" -text -fingerprint \
         > "${TEMPDIR}/ssl/certs/${keyhash}.pem"
    cat "${TEMPDIR}/ssl/certs/${keyhash}.pem" >> "${DESTDIR}${CSBUNDLE}"
    echo "Added to code signing certificate bundle."
  fi
done

"${OPENSSL}" rehash "${DESTDIR}${CERTDIR}" 2>&1>/dev/null
popd > /dev/null

# Install certdata.txt
if test "${REBUILD}" == "0"; then
  install -m644 "${WORKDIR}/certdata.txt" "${DESTDIR}${SSLDIR}/certdata.txt"
fi

# Clean up the mess
rm -rf "${TEMPDIR}"

# End /usr/sbin/make-ca
