Post-SA-26:52 audit of crypto_dispatch()/crp_etype consumers in src

Luis Castellanos <[email protected]>
Newsgroups gmane.os.freebsd.security.general
Message-ID <CAJ86RmtQ3=g-hgOWmiEQEVdD=h_3=vogYo2ZAwXert48448Qiw@mail.gmail.com>
Following the publication of FreeBSD-SA-26:52.if_wg (CVE-2026-58085), I
audited all in-tree consumers of crypto_dispatch() in sys/ to determine
whether the crp_etype read omission was isolated to wg_crypto.c or present
in other subsystems.

The files examined against the stable/14 tree are:

  sys/dev/wg/wg_crypto.c
  sys/geom/eli/g_eli_crypto.c
  sys/geom/eli/g_eli_integrity.c
  sys/geom/eli/g_eli_privacy.c
  sys/net/if_ovpn.c
  sys/netipsec/xform_ah.c
  sys/netipsec/xform_esp.c
  sys/netipsec/xform_ipcomp.c
  sys/opencrypto/ktls_ocf.c

Summary: the pattern from SA-26:52 (CRYPTO_F_CBIMM + reading only the
dispatch return value without checking crp_etype) is not present in any
other consumer. All remaining files route crp_etype through their callback
or read it explicitly after a synchronous wait. The bug was contained.

However, sys/net/if_ovpn.c exhibits a related fragility worth noting.


1. CONFIRMED ISOLATION OF SA-26:52 PATTERN
-------------------------------------------

The vulnerable pattern in wg_crypto.c prior to the fix was:

  crp.crp_flags = CRYPTO_F_IV_SEPARATE | CRYPTO_F_CBIMM;
  ret = crypto_dispatch(&crp);
  crypto_destroyreq(&crp);
  return (ret);                   /* crp_etype never read */

This pattern does not appear elsewhere. All other consumers use one of:

  (a) CRYPTO_F_CBIFSYNC with a registered callback that reads crp_etype
      (xform_ah.c, xform_esp.c, xform_ipcomp.c, if_ovpn.c)

  (b) CRYPTO_F_CBIFSYNC with an explicit synchronous wait on crp_opaque
      followed by reading crp_etype (g_eli_crypto.c)

  (c) Dynamic sync/async detection via CRYPTO_SESS_SYNC() with separate
      code paths for each (ktls_ocf.c)

xform_esp.c additionally distinguishes EBADMSG from transport errors at
line 538, which is the correct behavior for an AEAD consumer.


2. HARDENING OBSERVATION IN sys/net/if_ovpn.c
----------------------------------------------

if_ovpn.c does not share the SA-26:52 bug: crp_etype is correctly read
inside ovpn_decrypt_rx_cb() and ovpn_encrypt_tx_cb(). However, two
related issues are observable:

(a) No CRYPTO_SESS_SYNC() guard at session creation.

    wg_crypto.c post-fix enforces synchronous dispatch at init time:

      if (!CRYPTO_SESS_SYNC(chacha20_poly1305_sid)) {
              crypto_freesession(chacha20_poly1305_sid);
              return (ENXIO);
      }

    if_ovpn.c creates sessions with CRYPTOCAP_F_HARDWARE |
CRYPTOCAP_F_SOFTWARE
    (if_ovpn.c:939-940) without this guard. Under CRYPTO_F_CBIFSYNC
semantics,
    an async hardware provider will queue the callback rather than invoke it
    immediately. The callback does read crp_etype correctly, so this is not
    currently exploitable as an authentication bypass. However, the session
is
    not guaranteed to be synchronous, which differs from the defensive
posture
    now established in wg_crypto.c.

(b) crp not freed on dispatch error in the receive path.

    In the receive path (if_ovpn.c, around line 2635-2644):

      if (V_async_crypto)
              ret = crypto_dispatch_async(crp, CRYPTO_ASYNC_ORDERED);
      else
              ret = crypto_dispatch(crp);
      if (ret != 0) {
              OVPN_COUNTER_ADD(sc, lost_data_pkts_in, 1);
              if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);
      }
      return (true);

    When crypto_dispatch() returns non-zero (dispatch failure, not crypto
    failure), crp is not freed before the function returns. The encrypt
    path (around line 2187-2196) frees crp only when ret != 0 in a
    different branch that does not cover all error cases uniformly.

    This is distinct from crp_etype handling and is a resource management
    issue rather than an authentication bypass. Reproducibility depends on
    whether the selected OCF provider can fail dispatch under memory
    pressure.


3. METHODOLOGY NOTE
--------------------

crp_etype handling was traced per consumer through:
  - crp_flags selection (CBIMM vs CBIFSYNC vs explicit sync wait)
  - crypto_dispatch() / crypto_dispatch_async() call sites
  - presence of callback registration vs inline result read
  - session creation flags (CRYPTOCAP_F_SOFTWARE enforces sync;
    CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE permits async providers)

All observations above are directly traceable to stable/14 source as of
commit 825c6f45b147 (stable/14-n274644).

I am happy to prepare a patch for the if_ovpn crp free-on-error path if
that would be useful.

Luis F. Castellanos
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.