Skip to content

fix(ipsec-xfrm): reject noncanonical and ambiguous SA lookup marks #419

Description

@VerifiedOrganic

Problem

XfrmMark accepts arbitrary public value and mask fields at SA and policy install, query, rekey, and removal boundaries. Linux does not use pair equality for SA lookup:

  • xfrm_mark_get returns the incoming lookup value as value & mask while retaining the raw pair on the state;
  • __xfrm_state_lookup matches when (incoming_lookup_value & existing.mask) == existing.value.

A noncanonical stored lookup mark with bits outside its mask can therefore become unaddressable: the masked left-hand side can never equal the raw existing value. The same noncanonical CREATE request can also behave differently from an exact-pair mock map check.

Canonical marks with different masks still have an asymmetric overlap case. For one SA destination/protocol/SPI tuple:

  • existing { value: 0x10, mask: 0xf0 }, then incoming { value: 0x11, mask: 0xff } collides;
  • existing { value: 0x11, mask: 0xff }, then incoming { value: 0x10, mask: 0xf0 } can be accepted.

After the second order, a later GETSA or DELSA lookup can match more than one stored state, so pair equality and a caller-supplied mark do not by themselves make deletion exact. This is material to the exact readback/removal and staged-recovery contract introduced around #409.

This issue is distinct from #416. That issue repairs Mock CREATE|EXCL duplicate preservation and Linux collision-predicate parity. This issue defines which lookup marks are valid and when an SA removal can honestly be described as exact.

Required contract

  • Lookup marks must be canonical: value & mask == value.
  • Some(XfrmMark { mask: 0, .. }) must be rejected for lookup identity; None is the unmarked representation.
  • Enforce the invariant through a validated construction surface or at every applicable Linux and Mock install, rekey, query, relocation, and removal boundary. Do not apply lookup-identity restrictions blindly to the separate SA output-mark semantics.
  • Define the safety contract for overlapping canonical masks on the same SA destination/protocol/SPI tuple. An API named exact must either prove a non-overlapping lookup domain, restrict accepted marks to a profile that makes overlap impossible, or fail closed before deletion when that proof is unavailable.
  • Do not treat exact Rust Option<XfrmMark> equality as proof that Linux GETSA or DELSA can select only that stored object.
  • Keep Debug, Display, and validation errors redaction-safe; report only stable field/reason labels.

Required tests

  • Reject lookup marks whose value contains bits outside the mask without issuing a Linux netlink request or mutating Mock state.
  • Reject zero-mask Some lookup marks and preserve None as the only unmarked form.
  • Cover SA install, rekey, query, legacy removal, exact query, and exact removal on both Linux request encoding and the public MockXfrmBackend; cover policy boundaries where the same canonical lookup-mark invariant applies.
  • Cover the asymmetric canonical pair above in both insertion orders and lock the documented Linux result.
  • In the order Linux can admit both overlapping states, prove the exact-removal boundary does not claim or perform an unproven single-object delete.
  • Prove same-mask disjoint values and canonical full-mask values remain supported.
  • Qualify public documentation so marked/unmarked and distinct mark pairs are not described as unconditionally separate exact SA identities.

Current downstream impact

The public ePDG intent code uses unmarked SA lookup identities (mark: None) for the initial/default SWu Child SAs. When optional per-bearer steering is engaged, it uses canonical full-mask marks for dedicated outbound SAs and policies; the default outbound SA remains unmarked. This issue therefore does not block the initial unmarked SWu call path, but it does block a broad claim that arbitrary marked SA recovery is exact.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrustPull requests that update rust codestatus: in progressImplementation underway; check linked branches and PRs before overlapping work.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions