# DF-0015 — kern.proc.pathname.<pid> discloses the executable path of any process with no visibility check

## Verdict
**REPRODUCED** — `sysctl_kern_proc_pathname` resolves and returns the executable
path of **any** pid with no `p_trespass`/`ps_argsopen` gate. Confirmed on
DragonFly master DEV `v6.5.0.1712.g89e6a-DEVELOPMENT`: as unprivileged `maxx`
(uid 1001, not in wheel) I read the resolved exe path of root's init (`/sbin/init`)
and every other root daemon, and proved the sibling `args`/`cwd` handlers *are*
gated while `pathname` is not.

## Mechanism (confirmed by source + run)

`sysctl_kern_proc_pathname` (`sys/kern/kern_proc.c:2080-2117`):

```c
pid_t *pidp = (pid_t *)arg1;                 /* the requested pid */
...
p = pfind(*pidp);                            /* :2095 -- finds ANY pid  */
if (p == NULL) return (ESRCH);
...
if (p->p_textnch.ncp) {
    cache_copy(&p->p_textnch, &nch);
    error = cache_fullpath(p, &nch, NULL, &retbuf, &freebuf, 0);  /* :2102 */
}
...
error = SYSCTL_OUT(req, retbuf, strlen(retbuf) + 1);              /* :2110 */
```

There is **no** `p_trespass(cr1, p->p_ucred)` / `ps_argsopen` authorization
check. Its two siblings enforce it:

- `sysctl_kern_proc_args` (`kern_proc.c:1897`): `if ((!ps_argsopen) && p_trespass(cr1, p->p_ucred)) goto done;`
- `sysctl_kern_proc_cwd`  (`kern_proc.c:2052`): same gate.

The node (`kern_proc.c:2212-2214`) is `CTLFLAG_RD | CTLFLAG_NOLOCK`; as shown in
DF-0006, sysctl reads are not privilege-gated by the framework
(`kern_sysctl.c:1446-1450` checks only writes). So reads reach the handler as
any user, and the handler has no internal gate → the exe path of any process is
disclosed.

## The ps_argsopen nuance (and why this is still a real gap)

`ps_argsopen` defaults to **1** (`sys/kern/kern_exec.c:103`), which *disables*
the `args`/`cwd` gate (the condition `(!ps_argsopen) && ...` is false). So in
the default config all three of `args`/`cwd`/`pathname` are open. The
documented way an admin restricts inter-process visibility is
`sysctl kern.ps_argsopen=0`. With that set:

| node                     | as `maxx`, target pid 1 (root init) | gate line |
|--------------------------|-------------------------------------|-----------|
| `kern.proc.pathname.1`   | rc=0, `/sbin/init`  **LEAKED**      | (none)    |
| `kern.proc.args.1`       | rc=0, len=0  **blocked**            | kern_proc.c:1897 |
| `kern.proc.cwd.1`        | rc=0, len=0  **blocked**            | kern_proc.c:2052 |

i.e. the admin's hardening hides args/cwd but **cannot** hide pathname — proving
`pathname` is the lone un-gated sibling. (`run.log` = hardened; `run.default.log`
= ps_argsopen=1 baseline where all three leak.) Every root daemon's exe path was
recovered: init, hammer2, dhclient, devd, syslogd, sshd, cron (see
`leak_sample.txt`).

## Access-pattern note (PoC fix)

These are "node-with-pid-child" sysctls. The original `leak_pathname.sh` used the
dotted `sysctlbyname("kern.proc.pathname.1")` form, which returns `ENOENT` on
DragonFly — that is a sysctl(8)/sysctlbyname resolution quirk, **not** a
privilege control. The pid must be passed as a trailing MIB element:

```c
sysctlnametomib("kern.proc.pathname", mib, &len);   /* mib=[1,14,9] */
mib[len] = pid;
sysctl(mib, len+1, buf, &buflen, NULL, 0);          /* succeeds as any user */
```

The rewritten `leak_pathname.c` uses this form, prints pathname + the gated
args/cwd contrast, reads `kern.ps_argsopen` to label the run, and walks several
root daemons.

## Impact
Low-grade information disclosure to any local unprivileged user: the resolved
executable path (including install path / chroot/jail root) of every process
system-wide, even when the admin has set `kern.ps_argsopen=0` to restrict
inter-process visibility. Useful attacker reconnaissance / target selection;
inconsistent with the explicit args/cwd privilege policy. Rated Low.

## PoC changes
Replaced `leak_pathname.sh` (which used the non-resolving dotted name and
relied on `ps -ax` which needs privileges) with `leak_pathname.c` using the
correct `sysctlnametomib`+MIB form, reading pathname + gated args/cwd for
contrast, reporting `kern.ps_argsopen`, and iterating several root daemons.
Added `build.sh`/`run.sh`.

## Recommended fix
Matches the finding markdown's proposal. Authoritative `fix.diff` in this folder
adds the same `(!ps_argsopen) && p_trespass(...)` gate to
`sysctl_kern_proc_pathname` that the args/cwd siblings use.
