#!/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
#          Graham Weldon

shopt -s extglob;

VERSION="1.10"
MAKE_CA_CONF="/etc/make-ca.conf"

# Get/set defaults
if test -f "${MAKE_CA_CONF}"; then
    . "${MAKE_CA_CONF}"
else
    CERTDATA="certdata.txt"
    PKIDIR="/etc/pki"
    SSLDIR="/etc/ssl"
    CERTUTIL="/usr/bin/certutil"
    KEYTOOL="${JAVA_HOME}/bin/keytool"
    MD5SUM="/usr/bin/md5sum"
    OPENSSL="/usr/bin/openssl"
    TRUST="/usr/bin/trust"
    ANCHORDIR="${PKIDIR}/anchors"
    ANCHORLIST="${PKIDIR}/anchors.md5sums"
    BUNDLEDIR="${PKIDIR}/tls/certs"
    CABUNDLE="${BUNDLEDIR}/ca-bundle.crt"
    SMBUNDLE="${BUNDLEDIR}/email-ca-bundle.crt"
    CSBUNDLE="${BUNDLEDIR}/objsign-ca-bundle.crt"
    CERTDIR="${SSLDIR}/certs"
    KEYSTORE="${PKIDIR}/tls/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

# 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"
CERTDATAY=0
FORCE=0
GET=0
REBUILD=0
WITH_P12=0
WITH_NSS=0
WITH_CS=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}"
        echo ${DESTDIR} | grep -q "^\." && DESTDIR="${PWD}/${DESTDIR}"
        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}"
        CERTDIR="${SSLDIR}/certs"
        LOCALDIR="${SSLDIR}/local"
        echo "${@}" | grep -e "-d " -e "--cadir" > /dev/null 2>&1
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -d/--cadir switch."
          echo ""
          exit 3
        fi

        shift 2
      ;;
      -a | --anchordir)
        check_arg $1 $2
        ANCHORDIR="${2}"
        echo "${@}" | grep -e "-P " -e "--pkidir" > /dev/null 2>&1
        if test "${?}" == "0"; then
          echo "Error! ${1} cannot be used with the -P/--pkidir switch."
          echo ""
          exit 3
        fi
        shift 2
      ;;
      -b | --bundledir)
        check_arg $1 $2
        BUNDLEDIR="${2}"
        shift 2
      ;;
      -d | --cadir)
        check_arg $1 $2
        CERTDIR="${2}"
        echo "$@" |  grep -e "-S" -e "--ssldir" > /dev/null 2>&1
        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
      ;;
      -i | --mscodesign)
        WITH_CS="1"
        shift 1
      ;;
      -j | --javacerts)
        check_arg $1 $2
        KEYSTORE="${2}"
        shift 2
      ;;
      -k | --keytool)
        check_arg $1 $2
        KEYTOOL="${2}"
        shift 2
      ;;
      -l | --localdir)
        check_arg $1 $2
        LOCALDIR="${2}"
        shift 2
      ;;
      -m | --java-p12)
        WITH_P12="1"
        shift 1
      ;;
      -n | --nssdb)
        echo "${2}" | grep -v "^-" > /dev/null
        if [ "$?" -ne "0" -o ! -n "$2" ]; then
          WITH_NSS="1"
          shift 1
        else
          NSSDB="${2}"
          WITH_NSS="1"
          echo "${@}" | grep -e "-P " -e "--pkidir" > /dev/null
          if test "${?}" == "0"; then
            echo "Error! ${1} cannot be used with both an argument and the -P/--pkidir switch."
            echo ""
            exit 3
          fi
          shift 2
        fi
      ;;
      -p | --proxy)
        check_arg $1 $2
        PROXY="${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}"
        WITH_NSS="1"
        shift 2
      ;;
      -u | --trust)
        check_arg $1 $2
        TRUST="${2}"
        shift 2
      ;;
      -f | --force)
        FORCE="1"
        shift 1
      ;;
      -h | --help)
        showhelp
        exit 0
      ;;
      -v | --version)
        echo -e "$(basename ${0}) ${VERSION}\n"
        exit 0
      ;;
      # Handle getopt style short args (use extglob instead of loop)
      -+([a-z,A-Z]))
        # split up the arguments and call recursively with trailing break
        arg="${1}"
        newargs=$( echo ${1} | sed 's@-@@' | \
                                 sed 's/.\{1\}/& /g' | \
                                 sed 's/[^ ]* */-&/g')
        newargs="${newargs} $(echo ${@} | sed "s@${arg}@@")"
        get_args ${newargs}
        break;
      ;;
      *)
        showhelp
        exit 1
      ;;
    esac
  done
}

function check_arg(){
  echo "${2}" | grep "^-" > /dev/null
  if [ "$?" == "0" -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 workstations and servers using only standard GNU utilities, OpenSSL, and"
  echo "P11-Kit. It will optionally generate keystores for 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 root directory - Cannot be used"
  echo "                         with the -a / --anchordir or"
  echo "                         -n / --nssdb switches"
  echo ""
  echo "        -S, --ssldir [/etc/ssl]"
  echo "                         The output SSL root direcotry - Cannot be used"
  echo "                         with the -d / --cadir switch"
  echo ""
  echo "        -a, --anchordir [\$PKIDIR/anchors]"
  echo "                         The output directory for .p11-kit trust anchors"
  echo ""
  echo "        -b, --bundledir [\$PKIDIR/certs]"
  echo "                         The output direcotry for the PEM formated"
  echo "                         bundles"
  echo ""
  echo "        -d, --cadir [\$SSLDIR/certs]"
  echo "                         The output directory for the OpenSSL trusted"
  echo "                         CA certificates"
  echo ""
  echo "        -j, --javacerts [\$PKIDIR/java/cacerts]"
  echo "                         The output directory for the Java cacerts"
  echo "                         file(s)"
  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 source and provide system local"
  echo "                         certifiates"
  echo ""
  echo "        -m, --java-p12"
  echo "                         Export Java PKCS#12 store - this will default to"
  echo "                         \$PKIDIR/java/cacerts.p12 unless modified by"
  echo "                         the '-j/--javacerts' switch"
  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 [\$JAVA_HOME/bin/keytool]"
  echo "                         The path of the Java keytool utility"
  echo ""
  echo "        -s, --openssl [/usr/bin/openssl]"
  echo "                         The path of the openssl utility"
  echo ""
  echo "        -t, --certutil [/usr/bin/certutil]"
  echo "                         The path of the NSS certutil utility"
  echo ""
  echo "        -u, --trust [/usr/bin/trust]"
  echo "                         The path of the p11-kit trust utility"
  echo ""
  echo "        -i, --mscodesign"
  echo "                         Use Microsoft's trust values for code singing"
  echo "                         You must copy /etc/make-ca/CS.txt to \$SSLDIR"
  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 entire 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_moz_distrust(){
  # SERVER
  val=$(grep "CKA_NSS_SERVER_DISTRUST_AFTER" "${1}" | cut -d " " -f 2)
  if test "${val}" == "CK_BBOOL"; then
    val=$(grep "CKA_NSS_SERVER_DISTRUST_AFTER" "${1}" | cut -d " " -f 3)
    if test "${val}" == "CK_FALSE"; then
      mozsadistrust="%00"
    else
      mozsadistrust="UNKNOWN"
    fi
  elif test "${val}" == "MULTILINE_OCTAL"; then
    mozsadistrust=`printf $(grep -A1 "CKA_NSS_SERVER_DISTRUST_AFTER" "${1}" | tail -n1)`
  else
    mozsadistrust="UNKNOWN"
  fi

  # EMAIL
  val=$(grep "CKA_NSS_EMAIL_DISTRUST_AFTER" "${1}" | cut -d " " -f 2)
  if test "${val}" == "CK_BBOOL"; then
    val=$(grep "CKA_NSS_EMAIL_DISTRUST_AFTER" "${1}" | cut -d " " -f 3)
    if test "${val}" == "CK_FALSE"; then
      mozsmdistrust="%00"
    else
      mozsmdistrust="UNKNOWN"
    fi
  elif test "${val}" == "MULTILINE_OCTAL"; then
    mozsmdistrust=`printf $(grep -A1 "CKA_NSS_EMAIL_DISTRUST_AFTER" "${1}" | tail -n1)`
  else
    mozsmdistrust="UNKNOWN"
  fi
  unset val
}

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)
function 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
}

function get_p11_label() {
  # $1 == individual nss certificate extracted from certdata.txt
  #       or x509 certificate with OpenSSL text values

  # Start with the label assigned by Mozilla
  p11label=$(grep -m1 "^CKA_LABEL" ${1} | cut -d '"' -f 2 | sed 's@"@@g')

  # If not coming from certdata.txt, get from x509 Subject line
  if [ "${p11label}" == "" ]; then
    # Get the subject line for any certs and do some early normalization
    subjectline=$(grep -m1 "Subject:" ${1} | sed -e 's@\s*=\s*@=@g' \
                       -e "s@(@ - @g" -e "s@)@ - @g" -e 's@\\@-@g' )

    # Try for CN Next (and further normalize) if not from certdata.txt
    p11label="$(echo ${subjectline} | grep -o "CN=.*$" | cut -d '=' -f 2 | \
                sed -e 's@[A-Z]*$@@g' -e 's@, $@@' -e 's@"@@g' \
                    -e 's@,@@g' -e 's@ $@@')"

    # Fallback to the last OU value if CN does not exeist in Subject string
    ## Special case for GlobalSign certs
    if [ "${p11label}" == "" ]; then
        p11label="$(echo ${subjectline} | grep -o "OU=.*$" | \
                    sed 's@OU=.*, OU=@OU=@g'| cut -d '=' -f 2 | \
                    sed -e 's@[A-Z]*$@@' -e 's@, $@@' -e 's@"@@g' \
                        -e 's@,@@g' -e 's@ $@@')"

      # If still empty, fall back to Object value as a last resort
      if [ "${p11label}" == "" ]; then
          p11label="$(echo ${subjectline} | grep -o "O=.*$" | \
                      cut -d '=' -f 2 | sed -e 's@[A-Z]*$@@g' \
                          -e 's@, $@@' -e 's@"@@g' \
                          -e 's@,@@g' -e 's@ $@@')"
      fi
    fi
  fi
}

function get_trust_values() {
  # $1 == individual certificate extracted from NSS certdata.txt

  # Determine certificate trust values for SSL/TLS, S/MIME, and Code Signing
  satrust="$(convert_trust `grep '^CKA_TRUST_SERVER_AUTH' ${1} | \
                  cut -d " " -f 3`)"
  smtrust="$(convert_trust `grep '^CKA_TRUST_EMAIL_PROTECTION' ${1} | \
                  cut -d " " -f 3`)"
  cstrust="$(convert_trust `grep '^CKA_TRUST_CODE_SIGNING' ${1} | \
                  cut -d " " -f 3`)"
  if test "${WITH_CS}" -eq "1"; then
    if test "${cstrust}" == ""; then
      cstrust=$(grep -q "^${keyhash}" "${SSLDIR}/CS.txt" && echo "C")
    fi
  fi

  # Not currently included in NSS certdata.txt
  #catrust="$(convert_trust `grep '^CKA_TRUST_CLIENT_AUTH' ${1} | \
  #                cut -d " " -f 3`)"

  # Determine distrust values
  convert_moz_distrust ${1}
}

function get_p11_trust() {
  # 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

}

function write_anchor() {
  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: ${moz_trust}" >> "${anchorfile}"
  echo "modifiable: false" >> "${anchorfile}"
  if test "${mozsadistrust}" != "UNKNOWN"; then
    echo "nss-server-distrust-after: \"${mozsadistrust}\"" >> "${anchorfile}"
  fi
  if test "${mozsmdistrust}" != "UNKNOWN"; then
    echo "nss-email-distrust-after: \"${mozsmdistrust}\"" >> "${anchorfile}"
  fi
  echo "${certcer}" >> "${anchorfile}"
  echo "${certtxt}" | sed 's@^@#@' >> "${anchorfile}"
  echo "Added to p11-kit anchor directory with trust '${satrust},${smtrust},${cstrust}'."
}

function write_nss_db() {
  # $1 == NSS database
  # $2 == x509 certificate in PEM format

  "${CERTUTIL}" -d "sql:${1}" -A \
                -t "${satrust},${smtrust},${cstrust}" \
                -n "${p11label}" -i "${2}" > /dev/null 2>&1
  echo "Added to NSS shared DB with trust '${satrust},${smtrust},${cstrust}'."
}

function write_java_p12() {
  # $1 == cacerts.p12 file
  # $2 == x509 certificate in PEM format

  # Remove existing certificate
  "${KEYTOOL}" -delete -noprompt -alias "${p11label}"       \
               -keystore "${1}"  \
               -storepass 'changeit' > /dev/null 2>&1
  # 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 "${2}" -storetype PKCS12     \
                 -noprompt -alias "${p11label}" -storepass 'changeit' \
                 -keystore "${1}" $EKUVAL  \
                 > /dev/null 2>&1
    echo "Added to Java cacerts (PKCS#12) with trust '${satrust},${smtrust},${cstrust}'."
    unset EKU
    unset EKUVAL
  fi
}

# Process command line arguments
get_args $@

test ! -x "${OPENSSL}" && \
  echo "OpenSSL not found at ${OPENSSL}. Exiting..." && exit 1
mkdir -p "${TEMPDIR}"/{certs,pki/anchors,work}

if test "${WITH_P12}" -eq "1"; then
  test ! -x "${KEYTOOL}" && \
     echo "Java keytool not found at ${KEYTOOL}. Exiting..." && exit 1
  mkdir -p "${TEMPDIR}/ssl/java"
fi

if test "${WITH_NSS}" -eq "1"; then
  test ! -x "${CERTUTIL}" && \
    echo "NSS certutil not found at ${CERTUTIL}. Exiting..." && exit 1
  # Create a blank NSS DB
  mkdir -p "${TEMPDIR}/pki/nssdb"
  "${CERTUTIL}" -N --empty-password -d "sql:${TEMPDIR}/pki/nssdb"
fi

if test "${WITH_CS}" -eq "1"; then
  test ! -f "${SSLDIR}/CS.txt" && \
    echo "List of hashes not found at ${SSLDIR}/CS.txt. Exiting..." && exit 1
fi

# Download certdata.txt if selected
if test "${GET}" == "1"; then
  echo -n "Checking for new version of certdata.txt..."
  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
  echo "done."

  # Error out here if we couldn't get the file
  grep -m1 "<i>" "${TEMPDIR}/certdata.txt.log" > /dev/null 2>&1
  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 -n "Downloading certdata.txt..."
  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}"
  echo "done."
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 2>&1

# 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
  # 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)"
  keyhash="$(${OPENSSL} x509 -noout -in tempfile.crt -hash)"

  # Get trust values for the certifcate
  get_trust_values "${tempfile}"

  # Get p11-kit label, oid, and values
  get_p11_label "${tempfile}"

  # Get p11 trust and OID values
  get_p11_trust

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

  # Place certificate into trust anchors dir
  anchorfile="${TEMPDIR}/pki/anchors/${keyhash}.p11-kit"
  moz_trust="true"
  write_anchor

  # Import all certificates with trust args to the temporary NSS DB
  if test "${WITH_NSS}" == "1"; then
    write_nss_db ${TEMPDIR}/pki/nssdb tempfile.crt
  fi

  # Import all certificates with trust args to the java cacerts.p12 file
  if test "${WITH_P12}" == "1"; then
    write_java_p12 "${TEMPDIR}/ssl/java/cacerts.p12" tempfile.crt
  fi

  # Clean up the directory and environment as we go
  rm -f tempfile.crt
  unset keyhash subject count
  unset mozsadistrust mozsmdistrust anchorfile moz_trust
  unset trustlist rejectlist satrust smtrust cstrust catrust
  unset p11trust p11oid p11value trustp11 p11label

  echo -e "\n"
done
unset tempfile

# Backup any anchors with PKIX extensions - any object-id that is not 2.5.29.37
# only do if anchordir already exists
if test -d "${DESTDIR}${ANCHORDIR}"; then
  mkdir -p ${TEMPDIR}/override
  for file in $( grep -r "^object-id: 2\.5\.29\.[0-9]" "${DESTDIR}${ANCHORDIR}" | \
                 grep -v "2\.5\.29\.37" | cut -d ":" -f 1); do
    cp "${file}" "${TEMPDIR}/override/"
  done
fi

# Install anchors in $ANCHORDIR
if test -d "${DESTDIR}${ANCHORDIR}"; then
  rm -rf "${DESTDIR}${ANCHORDIR}"
fi
install -dm755 "${DESTDIR}${ANCHORDIR}"
install -m644 "${TEMPDIR}"/pki/anchors/*.p11-kit "${DESTDIR}${ANCHORDIR}"

# Restore anchors with PKIX extensions
if test -f "${TEMPDIR}"/override/*.p11-kit; then
  cp "${TEMPDIR}"override/*.p11-kit "${DESTDIR}${ANCHORDIR}"
fi

# 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}" > /dev/null 2>&1
  install -m644 "${TEMPDIR}"/pki/nssdb/{cert9.db,key4.db,pkcs11.txt} \
                 "${DESTDIR}${NSSDB}"
fi

# Install Java cacerts.p12 in ${KEYSTORE}
if test "${WITH_P12}" == "1"; then
  test -f "${DESTDIR}${KEYSTORE}/cacerts.p12" &&
          rm -f "${DESTDIR}${KEYSTORE}/cacerts.p12"
  install -m644 "${TEMPDIR}/ssl/java/cacerts.p12" \
                 "${DESTDIR}${KEYSTORE}/cacerts.p12"
fi

# 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
    get_p11_label ${cert}
    keyhash=$("${OPENSSL}" x509 -noout -in "${cert}" -hash)
    subject=$("${OPENSSL}" x509 -noout -in "${cert}" -subject)
    if test "${p11label}" == ""; then
      # This will always be OpenSSL, values will be separated by spaces
      p11label=$( echo "${subject}" | grep -o "CN = .*" | sed 's@CN = @@' | cut -d "," -f 1)
    fi
    echo "Certificate:  ${p11label}"
    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" > /dev/null 2>&1 && echo "C")
    smtrust=$(echo "${trustlist}" | \
              grep "E-mail Protection" > /dev/null 2>&1 && echo "C")
    cstrust=$(echo "${trustlist}" | \
              grep "Code Signing" > /dev/null 2>&1 && echo "C")
    if test "${WITH_CS}" -eq "1"; then
      if test "${cstrust}" == ""; then
        cstrust=$(grep -q "^${keyhash}" "${SSLDIR}/CS.txt" && echo "C")
      fi
    fi
    catrust=$(echo "${trustlist}" | \
              grep "Client Auth" > /dev/null 2>&1 && 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" > /dev/null 2>&1 && echo "p"); fi
    if test "${smtrust}" == ""; then smtrust=$(echo "${rejectlist}" | \
              grep "E-mail Protection" > /dev/null 2>&1 && echo "p"); fi
    if test "${cstrust}" == ""; then cstrust=$(echo "${rejectlist}" | \
              grep "Code Signing" > /dev/null 2>&1 && echo "p"); fi
    if test "${catrust}" == ""; then catrust=$(echo "${rejectlist}" | \
              grep "Client Auth" > /dev/null 2>&1 && echo "p"); fi


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

    # Get p11 trust and OID values
    get_p11_trust

    # Place certificate into trust anchors dir
    anchorfile="${DESTDIR}${ANCHORDIR}/${keyhash}.p11-kit"
    moz_trust="false"
    mozsadistrust="UNKNOWN"
    mozsmdistrust="UNKNOWN"
    write_anchor

    # Generate working copy
    "${OPENSSL}" x509 -in "${cert}" -text -fingerprint > tempfile.crt

    # Add to Shared NSS DB
    if test "${WITH_NSS}" == "1"; then
      write_nss_db "${DESTDIR}${NSSDB}" tempfile.crt
    fi

    # Import certificate (with trust args) into the java cacerts.p12 file
    if test "${WITH_P12}" == "1"; then
      write_java_p12 "${DESTDIR}${KEYSTORE}/cacerts.p12" tempfile.crt
    fi

    unset keyhash subject count
    unset mozsadistrust mozsmdistrust anchorfile moz_trust
    unset trustlist rejectlist satrust smtrust cstrust catrust
    unset p11trust p11oid p11value trustp11 p11label
    echo -e "\n"

  done
  unset cert
fi

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

# Clean up the mess
popd > /dev/null 2>&1
rm -rf "${TEMPDIR}"
# Build ANCHORLIST
"${MD5SUM}" "${DESTDIR}${ANCHORDIR}"/* > "${DESTDIR}${ANCHORLIST}"

# Build alternate formats using p11-kit trust
install -dm755 "${DESTDIR}${CERTDIR}" "${DESTDIR}${BUNDLEDIR}" "${DESTDIR}${KEYSTORE}"
echo "Extracting OpenSSL certificates to:"
echo -n "${DESTDIR}${CERTDIR}..."
"${TRUST}" extract --filter=ca-anchors --format=openssl-directory \
                   --overwrite --comment "${DESTDIR}${CERTDIR}" \
                   && echo "Done!" || echo "Failed!!!"
echo "Extracting GNUTLS server auth certificates to:"
echo -n "${DESTDIR}${CABUNDLE}..."
"${TRUST}" extract --filter=ca-anchors --format=pem-bundle \
                   --purpose server-auth --overwrite --comment "${DESTDIR}${CABUNDLE}" \
                   && echo "Done!" || echo "Failed!!!"
echo "Extracting GNUTLS S-Mime certificates to:"
echo -n "${DESTDIR}${SMBUNDLE}..."
"${TRUST}" extract --filter=ca-anchors --format=pem-bundle \
                   --purpose email --overwrite --comment "${DESTDIR}${SMBUNDLE}" \
                   && echo "Done!" || echo "Failed!!!"
echo "Extracting GNUTLS code signing certificates to:"
echo -n "${DESTDIR}${CSBUNDLE}..."
"${TRUST}" extract --filter=ca-anchors --format=pem-bundle \
                   --purpose code-signing --overwrite --comment \
                   "${DESTDIR}${CSBUNDLE}" && echo "Done!" || echo "Failed!!!"
echo "Extracting Java cacerts (JKS) to:"
echo -n "${DESTDIR}${KEYSTORE}/cacerts..."
"${TRUST}" extract --filter=ca-anchors --format=java-cacerts \
                   --purpose server-auth --overwrite \
                   --comment "${DESTDIR}${KEYSTORE}/cacerts" \
                   && echo "Done!" || echo "Failed!!!"

# End /usr/sbin/make-ca
