open
https://gitlab.synchro.net/main/sbbs/-/issues/1187
Inbound FidoNet netmail addressed to a bare user number is delivered to that number without checking whether the account is actually active, so a remote system can deposit mail into a `DELETED` (or `INACTIVE`) user's slot.
In `import_netmail()` (`src/sbbs3/sbbsecho.c:4862`):
```c
usernumber = atoi(hdr.to);
if (!usernumber && strListFind(cfg.sysop_alias_list, hdr.to, /* case sensitive: */ false) >= 0)
usernumber = 1;
if (!usernumber)
usernumber = lookup_user(&scfg, &user_list, hdr.to);
```
`atoi(hdr.to)` is taken straight from the packet and **short-circuits `lookup_user()`** — which is the one function here that skips deleted users (`src/sbbs3/userdat.c:5051`). A netmail addressed `To: 42` therefore bypasses that check entirely.
Nothing downstream catches it. `fmsgtosmsg()` calls `getuserdat()` (`sbbsecho.c:3576`), which bounds-checks the number but returns `USER_SUCCESS` for a `DELETED` record, and no `user.misc & DELETED` test guards the `smb_hfield_str(&msg, RECIPIENTEXT, str)` at line 3591.
For contrast, the SMTP `RCPT TO` path (`src/sbbs3/mailsrvr.cpp:4976-5036`) is the model: it verifies a by-number recipient via `username()` / `"DELETED USER"`, then `getuserdat()`, then explicitly rejects `DELETED | INACTIVE` with a 550.
## Impact
Mail lands in a deleted user's slot and is invisible until that slot is recycled. Combined with the slot-reuse mail-purge gap (see the companion issue), it then surfaces in an unrelated new user's inbox. It's remote-triggerable and needs no corruption or misconfiguration.
## Suggested fix
Reject an inactive account at the point where the number is resolved, so the message falls through to the existing `lookup_user()` / `default_recipient` / "Unknown user" handling rather than changing `fmsgtosmsg()`'s error semantics (returning `SMB_FAILURE` there risks packet retry behavior):
```c
usernumber = atoi(hdr.to);
if (usernumber) { /* Addressed by user number: don't accept an inactive account */
user_t user = { .number = usernumber };
if (getuserdat(&scfg, &user) != USER_SUCCESS || !user_is_active(&user))
usernumber = 0;
}
```
`user_is_active()` (`src/sbbs3/userdat.c:4108`) is the codebase's canonical `(misc & (DELETED | INACTIVE)) == 0` test — the same pair `total_users()` uses and that SMTP rejects on.
## Related observation
`lookup_user()` skips `DELETED` but not `INACTIVE`, so netmail addressed **by name** to an inactive account is still accepted, where SMTP would reject it. Worth deciding separately whether that's intended.
— *Authored by Claude (Claude Code), on behalf of @rswindell*
--- SBBSecho 3.37-Linux
* Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)