fix(app-misc/ca-certificates): Never rehash certs during boot.

This kills the massive time-waster we currently have during boot to
cleanup/refresh OpenSSL's certificate directory in /etc/ssl/certs.
System provided certs are now hashed at build time and tempfiles is used
to install the proper links. The certificate bundle is also created at
build time and provided as a symlink. Since tempfiles does not help us
clean up broken links to certs that have been removed that task has been
moved to clean-ca-certificates.service which is still run on every boot.
The update-ca-certificates.service is now only responsible for
recreating the certificate bundle if required. On systems where the user
has never run update-ca-certificates the certificate bundle is just a
symlink to the copy in /usr and update-ca-certificates.service never
runs on boot. If the user does run update-ca-certificates then
update-ca-certificates.service will rewrite the bundle only if the
directory has not been modified since the last time it ran.

There are a few edge cases where if stale hash links may be left if a
system provided cert was replaced a subject that is different enough to
change the hash but not change the file name. I don't expect that to
ever happen though. Also hash collisions between the system certs and
each other or user provided certs are not handled gracefully but should
be rare. tempfiles will report wrong symlink errors in that case.
This commit is contained in:
Michael Marineau 2014-06-11 22:48:49 -07:00
parent 5f55bcc182
commit 12d093e8b1
4 changed files with 61 additions and 17 deletions

View File

@ -25,13 +25,25 @@ RDEPEND="dev-libs/openssl
DEPEND="${RDEPEND}
${PYTHON_DEPS}"
sym_to_usr() {
local l="/etc/ssl/certs/${1##*/}"
local p="../../../usr/share/${PN}/${1}"
echo "L ${l} - - - - ${p}"
}
gen_tmpfiles() {
local certfile
echo "d /etc/ssl - - - - -"
echo "d /etc/ssl/certs - - - - -"
sym_to_usr ca-certificates.crt
for certfile in "$@"; do
local l="/etc/ssl/certs/${certfile##*/}"
local p="../../../usr/share/${PN}/${certfile}"
sym_to_usr "${certfile}"
done
for certfile in "$@"; do
local certhash=$(openssl x509 -hash -noout -in "${certfile}")
# This assumes the hashes have no collisions
local l="/etc/ssl/certs/${certhash}.0"
local p="${certfile##*/}"
echo "L ${l} - - - - ${p}"
done
}
@ -39,20 +51,23 @@ gen_tmpfiles() {
src_compile() {
local certdata="${MY_P}/nss/lib/ckfw/builtins/certdata.txt"
${PYTHON} "${FILESDIR}/certdata2pem.py" "${certdata}" mozilla || die
cat mozilla/*.pem > ca-certificates.crt || die
gen_tmpfiles mozilla/*.pem > ${PN}.conf || die
}
src_install() {
insinto /usr/share/${PN}
doins ca-certificates.crt
doins -r mozilla
dosbin "${FILESDIR}/update-ca-certificates"
systemd_dounit "${FILESDIR}/clean-ca-certificates.service"
systemd_dounit "${FILESDIR}/update-ca-certificates.service"
systemd_enable_service sysinit.target clean-ca-certificates.service
systemd_enable_service sysinit.target update-ca-certificates.service
systemd_dotmpfilesd ${PN}.conf
# Setup initial links in /etc
dodir /etc/ssl/certs
tmpfiles_create
bash "${FILESDIR}/update-ca-certificates" "${D}/etc/ssl/certs" || die
}

View File

@ -0,0 +1,12 @@
[Unit]
Description=Clean up broken links in /etc/ssl/certs
# Since other services depend on the certificate store run this early
DefaultDependencies=no
Wants=systemd-tmpfiles-setup.service
After=systemd-tmpfiles-setup.service
Before=sysinit.target
ConditionPathIsReadWrite=/etc/ssl/certs
[Service]
Type=oneshot
ExecStart=/usr/bin/find -L /etc/ssl/certs -type l -delete

View File

@ -1,27 +1,42 @@
#!/bin/bash
CERTSDIR="${1:-${ROOT}/etc/ssl/certs}"
set -e
CERTSDIR="${ROOT}/etc/ssl/certs"
CERTBUNDLE="${CERTSDIR}/ca-certificates.crt"
SKIP_REHASH=0
while [[ $# -gt 0 ]]; do
case "$1" in
--skip-rehash)
SKIP_REHASH=1 ;;
--help|-h|*)
echo "$0 [--skip-rehash]"
exit ;;
esac
shift
done
if [[ ! -w "${CERTSDIR}" ]]; then
echo "Error: SSL certificate directory ${CERTSDIR} isn't writable" >&2
exit 1
fi
set -e
if [[ ${SKIP_REHASH} -ne 1 ]]; then
c_rehash "${CERTSDIR}"
fi
echo "Pruning broken links in ${CERTSDIR}"
find -L "${CERTSDIR}" -type l -delete
echo "Rehashing certificate files in ${CERTSDIR}"
c_rehash "${CERTSDIR}"
CERTBUNDLE="${CERTSDIR}/ca-certificates.crt"
if [[ ! -e "${CERTBUNDLE}" || "${CERTSDIR}" -nt "${CERTBUNDLE}" ]]; then
echo "Recreating certificate bundle ${CERTBUNDLE}"
TEMPBUNDLE=$(mktemp "${CERTBUNDLE}.XXXXXXXXXX")
trap "rm -f '${CERTSDIR}/${TEMPBUNDLE}'" EXIT
# Use .0 instead of .pem to pull in only what c_rehash validated
cat "${CERTSDIR}"/*.0 > "${TEMPBUNDLE}"
cat "${CERTSDIR}"/*.[0-9] > "${TEMPBUNDLE}"
chmod 644 "${TEMPBUNDLE}"
mv -f "${TEMPBUNDLE}" "${CERTBUNDLE}"
trap - EXIT
# Update the bundle's mtime so future runs know not to regenerate it
touch --reference="${CERTSDIR}" "${CERTBUNDLE}"
fi

View File

@ -1,12 +1,14 @@
[Unit]
Description=Update CA Certificates in /etc/ssl/certs
Description=Update CA bundle at /etc/ssl/certs/ca-certificates.crt
# Since other services depend on the certificate store run this early
DefaultDependencies=no
Wants=systemd-tmpfiles-setup.service
After=systemd-tmpfiles-setup.service
Wants=systemd-tmpfiles-setup.service clean-ca-certificates.service
After=systemd-tmpfiles-setup.service clean-ca-certificates.service
Before=sysinit.target
ConditionPathIsReadWrite=/etc/ssl/certs
# Do nothing if update-ca-certificates has never been run before
ConditionPathIsSymbolicLink=!/etc/ssl/certs/ca-certificates.crt
[Service]
Type=oneshot
ExecStart=/usr/sbin/update-ca-certificates
ExecStart=/usr/sbin/update-ca-certificates --skip-rehash