#!/bin/sh
# Begin make-ca.sh
#
# Script to create OpenSSL certs directory, GNUTLS certificate bundle, and
# NSS shared DB from upstream certdata.txt and local sources
#
# The file certdata.txt must exist in the local directory
# Version number is obtained from the version of the data.
#
# Authors: DJ Lucas
#          Bruce Dubbs
#
# Version 20161109

# Some data in the certs have UTF-8 characters
export LANG=en_US.utf8

certdata="certdata.txt"
TEMPDIR=$(mktemp -d)
WORKDIR="${TEMPDIR}/work"
SSLDIR="/etc/ssl"
NSSDBDIR="/etc/pki/nssdb"

# 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 covert_pem(){
  # concatenate multi-line octacl, printf (oct->char), pipe to openssl
  # argument is single filename
  startline=$(grep -n "^CKA_VALUE" $1 | cut -d ":" -f 1)
  startline=$(( ${startline} + 1 ))
  endlinelist=$(grep -n "^END" $1 | cut -d ":" -f 1)

  # Temporary working files
  tempcert1=`mktemp`
  tempcert2=`mktemp`

  # Loop through endline until it is larger than startline
  for endline in ${endlinelist}; do
    if test "${endline}" -gt "${startline}"; then
      endline=$(( ${endline} - 1 ))
      sed -n "${startline},${endline}p" $1 > "${tempcert1}"
      break
    fi
  done

  # Concatenate the lines in a single string
  for line in `cat "${tempcert1}"`; do echo -n $line >> "${tempcert2}"; done

  # Convert oct->char and pipe to openssl to creat DER format file
  printf `cat "${tempcert2}"` | openssl x509 -text -inform DER -fingerprint
  rm ${tempcert1} ${tempcert2}
  unset tempcert1 tempcert2 startline endlinelist endline line
}
     
if test ! -r $certdata; then
  echo "$certdata must be in the local directory"
  exit 1
fi

REVISION=$(grep CVS_ID $certdata | cut -f4 -d'$')

if test "${REVISION}x" == "x"; then
  echo "WARNING! $certfile has no 'Revision' in CVS_ID"
  echo "Will run conversion unconditionally."
  sleep 3
  VERSION="$(date -u)"
else
  VERSION=$(echo $REVISION | cut -f2 -d" ")
  test -f "${SSLDIR}/ca-bundle.crt" &&
  OLDVERSION=$(grep "VERSION:" "${SSLDIR}/ca-bundle.crt" | cut -d ":" -f 2)
fi

if test "${OLDVERSION}x" == "${VERSION}x"; then
  echo "No update required!"
  exit 0
fi

mkdir -p "${TEMPDIR}"/{certs,ssl/certs,pki/nssdb,work}
cp "${certdata}" "${WORKDIR}"
pushd "${WORKDIR}"

# Create a blank NSS DB
/usr/bin/certutil -N --empty-password -d "${TEMPDIR}/pki/nssdb"

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

# Get a list of ending lines for each cert
CERTENDLIST=`grep -n "^CKA_TRUST_STEP_UP_APPROVED" "${certdata}" | \
                  cut -d ":" -f 1`

# Start a loop
for certbegin in ${CERTBEGINLIST}; do
  for certend in ${CERTENDLIST}; do
    if test "${certend}" -gt "${certbegin}"; then
      break
    fi
  done

  # Dump to a temp file with the name of the file as the beginning line number
  sed -n "${certbegin},${certend}p" "${certdata}" > "${TEMPDIR}/certs/${certbegin}.tmp"
done

unset CERTBEGINLIST CERTDATA CERTENDLIST certbegin certend

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`)"

  # Convert to a PEM formated certificate
  covert_pem "${tempfile}" > tempfile.crt

  # Import all certificates with trust args to the temporary NSS DB
  /usr/bin/certutil -d "${TEMPDIR}/pki/nssdb" -A \
                    -t "${satrust},${smtrust},${cstrust}" \
                    -n "${certname}" -i tempfile.crt

  # Import certificates trusted for SSL/TLS into the temporary OpenSSL
  # certificate directory
  if test "${satrust}x" == "Cx"; then
    keyhash=$(openssl x509 -noout -in tempfile.crt -hash)
    echo -e "# ${certname}:\n" > "${TEMPDIR}/ssl/certs/${keyhash}.pem"
    cat tempfile.crt >> "${TEMPDIR}/ssl/certs/${keyhash}.pem"
    echo "Created ${keyhash}.pem for ${certname}"
  fi

  # Clean up the working direcorty and environment as we go
  rm tempfile.crt
  unset certname satrust smtrust cstrust
done
unset tempfile

# Sanity check
count=$(ls "${TEMPDIR}"/ssl/certs/*.pem | wc -l)
# Historically there have been between 152 and 165 certs
# A minimum of 140 should be safe for a rudimentry sanity check
if test "${count}" -lt "140" ; 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

# Generate the bundle
for cert in "${TEMPDIR}"/ssl/certs/*.pem
do
   cat "${cert}" >> "${TEMPDIR}/ssl/ca-bundle.crt"
done
unset cert

# Not doing a damn thing until thoroughly tested...
echo "${TEMPDIR} is the temporary working directory"

popd > /dev/null
# End make-ca.sh
