# DF-0621 — Verdict: NOT REPRODUCED (hardening inconsistency only)

## Verdict
NOT REPRODUCED as an unprivileged escalation. The code-path
inconsistency is real (confirmed by source trace) but the "privilege
bypass" is in the wrong direction for unpriv users: it can only
*fail to deny* a capsicum-restricted root credential. An
unprivileged user (cr_uid != 0) gets `priv=0` either way, and cannot
even reach `rip6_output` because `rip6_attach` (raw_ip6.c:530)
requires `SYSCAP_NONET_RAW`.

This is correctly classified as a Medium hardening / correctness
finding, not a Critical/High escalation.

## Source trace
- sys/netinet6/raw_ip6.c:297-299 — the weak check:
  ```c
  priv = 0;
  if (so->so_cred->cr_uid == 0)
      priv = 1;
  ```
- sys/netinet6/raw_ip6.c:302-304 — `priv` flows to
  `ip6_setpktoptions(control, &opt, ..., priv)`.
- sys/netinet6/ip6_output.c:2555 — `ip6_setpktoption` takes `priv`
  parameter.
- sys/netinet6/ip6_output.c:2712 — `if (!priv) return (EPERM);` for
  IPV6_NEXTHOP.
- sys/netinet6/ip6_output.c:2763 — `if (!priv) return (EPERM);` for
  IPV6_HOPOPTS.
- sys/netinet6/ip6_output.c:1156-1158 — the **correct** check used by
  the setsockopt (sticky-option) path:
  ```c
  privileged = (td == NULL ||
                caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT)) ?
               0 : 1;
  ```
- sys/netinet6/ip6_output.c:1871 — `ip6_pcbopts` (IPV6_PKTOPTIONS)
  hardcodes `priv = 0` — a separate (also-incorrect-but-the-other-way)
  inconsistency noted by the reviewer.

## Reachability test
- sys/netinet6/raw_ip6.c:530 — `caps_priv_check(ai->p_ucred,
  SYSCAP_NONET_RAW | __SYSCAP_NULLCRED)` gates raw IPv6 socket
  creation.
- Empirically on this guest: `socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)`
  as uid=1001 → EPERM.
- Therefore an unprivileged user cannot reach `rip6_output` at all.

## Impact ceiling
- **No unpriv escalation** demonstrable.
- Hardening gap: a capsicum-sandboxed root process (one that has had
  `SYSCAP_RESTRICTEDROOT` revoked but still has `SYSCAP_NONET_RAW`)
  could set IPV6_NEXTHOP via per-send cmsg even though the system
  policy intended to deny it. This is a real but narrow
  defense-in-depth concern.
- Realistic deployments rarely combine `cr_uid==0` with capsicum
  restriction of `SYSCAP_RESTRICTEDROOT`, so practical impact is low.

## Fix validation
The fix is one line: replace the weak `cr_uid == 0` check with the
proper `caps_priv_check_td(curthread, SYSCAP_RESTRICTEDROOT)`.
Validated by:
1. Source trace shows the fix is correct.
2. fix.diff applies cleanly with `patch -p1 --dry-run` against
   sys/netinet6/raw_ip6.c.

A kernel build/boot test is not strictly required for a
hardening-only finding with no reproduced behavior to compare
against, but the change is a 3-line surgical replacement that
matches the existing pattern at ip6_output.c:1156.

## Kernel references
- sys/netinet6/raw_ip6.c:297-299 — weak cr_uid check (BUG)
- sys/netinet6/raw_ip6.c:302-304 — priv flows to ip6_setpktoptions
- sys/netinet6/raw_ip6.c:530 — SYSCAP_NONET_RAW gate on raw IPv6 socket
- sys/netinet6/ip6_output.c:1156-1158 — correct capsicum check (setsockopt path)
- sys/netinet6/ip6_output.c:2555 — ip6_setpktoption priv parameter
- sys/netinet6/ip6_output.c:2712 — IPV6_NEXTHOP EPERM gate
- sys/netinet6/ip6_output.c:2763 — IPV6_HOPOPTS EPERM gate
- sys/netinet6/ip6_output.c:1871 — ip6_pcbopts hardcodes priv=0 (related inconsistency)
