# DF-0621 — rip6_output priv check uses cr_uid==0 instead of capsicum

## Bug
`rip6_output` (sys/netinet6/raw_ip6.c:297-299) computes the privileged
flag for per-send `cmsg` (IPV6_NEXTHOP, IPV6_HOPOPTS, IPV6_DSTOPTS,
IPV6_RTHDR, ...) using a weak credential test:

```c
priv = 0;
if (so->so_cred->cr_uid == 0)
    priv = 1;
```

This `priv` flows to `ip6_setpktoptions` (raw_ip6.c:302) →
`ip6_setpktoption` (sys/netinet6/ip6_output.c:2555) where it gates
restricted IPv6 options (e.g. line 2712 `if (!priv) return (EPERM)`
for IPV6_NEXTHOP; line 2763 for IPV6_HOPOPTS).

The **sticky-option** path (setsockopt via `ip6_ctloutput`) correctly
uses the proper capsicum check at sys/netinet6/ip6_output.c:1156-1158:

```c
privileged = (td == NULL ||
              caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT)) ?
             0 : 1;
```

The inconsistency: `rip6_output` should use the same capsicum check
but instead tests only `cr_uid == 0`.

## Direction of the inconsistency (important)
This is NOT an "unpriv user gains privilege" bug. The check grants
`priv=1` only when `cr_uid == 0` (root). It does NOT grant privilege
to non-root users. The inconsistency is:

- A process with `cr_uid == 0` but with the `SYSCAP_RESTRICTEDROOT`
  capability **revoked** (capsicum-sandboxed root) would still get
  `priv=1` via `rip6_output` (the cmsg path), even though the
  setsockopt path would correctly deny it.

This is a **defense-in-depth / hardening gap**, not a privilege
escalation. The bypass is of capability-based restrictions on an
already-root credential.

## Reachability on this guest
`rip6_output` is the `pru_send` for SOCK_RAW IPv6 sockets. Opening
one requires `SYSCAP_NONET_RAW` (raw_ip6.c:530: `caps_priv_check(...,
SYSCAP_NONET_RAW | __SYSCAP_NULLCRED)`). On this guest an
unprivileged user (uid=1001) gets EPERM:

```
$ ./check
[*] DF-0621 reachability check
[*] trying to open AF_INET6 SOCK_RAW as uid=1001
[+] socket(AF_INET6, SOCK_RAW) FAILED: Operation not permitted (errno=1)
[+] expected: SYSCAP_NONET_RAW required (rip6_attach raw_ip6.c:530)
[+] => unprivileged user CANNOT reach rip6_output
[+] => DF-0621 is a root-only hardening gap, not unpriv->root
```

So:
- An unprivileged user **cannot** reach `rip6_output` at all.
- The only credential that experiences the wrong check is a
  capsicum-restricted root, which is a defense-in-depth scenario.

## Fix
Use `caps_priv_check_td(curthread, SYSCAP_RESTRICTEDROOT)` to match
the setsockopt path. See fix.diff.
