Control: found -1 ca-certificates-java/20260311
Control: affects -1 ca-certificates

Hi,

Some additional data on this bug, plus root causes for why the
`update-ca-certificates --fresh` workaround does not reflect deletions.

1. Impact: the eMudhra intermediate compromise (CVE-2026-91403)
================================================================

On Ubuntu 24.04 (ca-certificates 20260816ubuntu0.24.04.1,
ca-certificates-java 20240118), the security update that deselects
"emSign ECC Root CA - G3" and "Comodo AAA Services root" was applied by
unattended-upgrades. /etc/ssl/certs was updated correctly ("4 added,
5 removed"), but /etc/ssl/certs/java/cacerts was left untouched (mtime
unchanged since the package was first installed). Java clients on that
host kept trusting the compromised chains; curl did not:

    curl https://dv-g3b-emudhra.we-have-a-problem.com/      -> rejected
    java (system JRE, same host, same URL)                  -> trusted

The same happened on the previous ca-certificates upgrade on that host
(20240203 -> 20260601~24.04.1), so every distrust since installation was
missed.

2. Upgrade path (the original report): still present on sid
===========================================================

This is the same root cause Arnaud analysed in #1069251 (now assigned to
src:ca-certificates). Reproduced in a debian:sid container with
ca-certificates 20260816+nmu1 and ca-certificates-java 20260311 (script
attached): ca-certificates.postinst runs
`update-ca-certificates --hooksdir ""` first, which removes the symlink
without running hooks, and then the deferred trigger run sees
"0 added, 0 removed" and hands an empty list to the jks-keystore hook.

To confirm that the pre-run is the cause (not proposed as the fix),
replacing the hook-less pre-run with a no-op makes the deselected root
disappear from cacerts on the same upgrade path.

A real fix probably needs the hook-less run to preserve its
ADDED/REMOVED lists for the deferred run, or the deferred run to
reconcile cacerts against /etc/ssl/certs instead of relying on a diff.

3. `--fresh` does not reach the hook
====================================

update-ca-certificates never passes --fresh to the hooks:

    ( cat "$ADDED"
      cat "$REMOVED" ) | "$hook" || echo "E: $hook exited with code $?."

so /etc/ca-certificates/update.d/jks-keystore always takes the `*)` branch
and fires `update-ca-certificates-java` rather than
`update-ca-certificates-java-fresh`. With --fresh, update-ca-certificates
first clears all symlinks and recreates them, so the hook receives only
"+" lines and nothing is ever removed from cacerts. That explains why
`update-ca-certificates --fresh` alone is not a workaround.

Passing --fresh through:

    ( cat "$ADDED"
      cat "$REMOVED" ) | "$hook" $( [ "$fresh" = 1 ] && echo --fresh ) || ...

makes the deselected root disappear, but only under an English locale,
because of the next issue.

4. The "fresh" reconciliation is locale-dependent
=================================================

ca-certificates-java.postinst sets only LC_CTYPE=C.UTF-8, and then parses
the existing aliases from translated keytool output:

    cacerts_aliases=$(keytool ${castore} -storepass "$storepass" -list -rfc \
        | sed -n 's/^Alias name: *debian://ip' | tr '\n' ' ')

keytool follows LC_MESSAGES/LANG for its output ("별칭 이름:" in Korean,
"Aliasname:" in German), so cacerts_aliases is empty and no "-" lines are
generated. Same environment the postinst uses, on sid:

    LANG=C.UTF-8      LC_CTYPE=C.UTF-8 -> 120 aliases parsed
    LANG=ko_KR.UTF-8  LC_CTYPE=C.UTF-8 -> 0
    LANG=de_DE.UTF-8  LC_CTYPE=C.UTF-8 -> 0

Suggested fix:

    cacerts_aliases=$(LC_ALL=C.UTF-8 keytool ${castore} ...

(or export LC_ALL=C.UTF-8 at the top of the postinst instead of
LC_CTYPE only).

5. Results (debian:sid, ISRG Root X1 deselected, then checked in cacerts)
=========================================================================

                                          C.UTF-8   ko_KR     de_DE
  package upgrade path, unpatched         present   present   present
  package upgrade path, no pre-run (*)    absent    absent    absent
  update-ca-certificates --fresh          present   present   present
  --fresh + hook receives --fresh         absent    present   present
  --fresh + hook --fresh + LC_ALL fix     absent    absent    absent

  (*) diagnostic only, see section 2

Items 2 and 3 are in src:ca-certificates (postinst and
update-ca-certificates), item 4 is in src:ca-certificates-java, hence
the "affects".

Workaround for affected systems, which does not depend on the locale
because the keystore is rebuilt from scratch (as Cyril found):

    rm -f /etc/ssl/certs/java/cacerts
    update-ca-certificates --fresh

The reproducer script is attached.

Thanks,
Kamilake

Reply via email to