open
https://gitlab.synchro.net/main/sbbs/-/issues/1221
## Summary
The Mail Server listens on the submission port (587, `USE_SUBMISSION_PORT`) and on the implicit-TLS submission port (465, `TLS_SUBMISSION`), but it does not behave as a Message Submission Agent on them: those listeners run the exact same code path as port 25. Consequently:
* An authenticated user cannot submit an original message to an external
recipient without the sysop enabling `ALLOW_RELAY`, which is a server-wide
option and not scoped to the submission port.
* Once `ALLOW_RELAY` is enabled, an authenticated user may submit a message
with an arbitrary `From:` address, including an address at a domain the
system does not own. Nothing verifies the submitted sender against the
authenticated user.
* The submission port does not require authentication at all.
Raised by Deuce on IRC after a submission from `
deuce@bbsdev.net` to an external recipient, via the submission port, with a valid authenticated session, was refused and logged as `!ILLEGAL RELAY ATTEMPT`.
## What the RFCs say
RFC 6409 (Oct 2011) obsoletes RFC 4409 (Apr 2006), which obsoletes RFC 2476 (Dec 1998). The submission/relay distinction is the *source* of the message, not the destination:
**Message Submission Agent (MSA)**: A process that conforms to this specification. An MSA acts as a submission server to accept messages from MUAs, and it either delivers them or acts as an SMTP client to relay them to an MTA.
**Message Transfer Agent (MTA)**: A process that conforms to [SMTP-MTA]. An MTA acts as an SMTP server to accept messages from an MSA or another MTA...
-- RFC 6409 section 2.1
So an authenticated user handing the server a new message addressed to `
someone@example.com` is a **submission**, regardless of the recipient domain. It is a relay only when the message arrives from another MTA. This is the distinction port 587 exists for (RFC 6409 section 3.1: "Port 587 is reserved for email message submission").
Relevant requirements:
| Ref | Level | Requirement | Synchronet today |
|---|---|---|---|
| 6409 section 4.3 | **MUST** | "The MSA MUST, by default, issue an error response to the MAIL command if the session has not been authenticated using [SMTP-AUTH]", reply code 530 | Not implemented. An unauthenticated client on 587/465 is treated identically to one on 25. |
| 6409 section 6.1 | MAY | "The MSA MAY issue an error response to a MAIL command if the address in MAIL FROM appears to have insufficient submission rights or is not authorized with the authentication used" | Not implemented. |
| 6409 section 6.2 | MAY | "The MSA MAY issue an error response to a RCPT command if inconsistent with the permissions given to the user" | Partially: `ALLOW_RELAY` plus the `G`/`M` restrictions, but all-or-nothing and not per-port. |
| 6409 section 7 | SHOULD | PIPELINING, ENHANCEDSTATUSCODES | Neither advertised (AUTH, which is MUST, is advertised). |
| 6409 section 8.3 | SHOULD | Add or replace `Message-ID` if missing/invalid | Not implemented. |
## What the code actually does
Verified in `src/sbbs3/mailsrvr.cpp` at master (2ff8d37d99):
**1. The submission port is indistinguishable from port 25 after accept().** `servprot_submission` is defined at line 116 and referenced exactly once more, in the `xpms_add_list()` call at line 6523. The per-session struct records only `tls_port` (line 157), set at line 6734 as `(servprot == servprot_submissions)` -- i.e. only implicit-TLS-ness survives into the session. Nothing downstream can tell a submission-port session from a port-25 session, so no policy can be applied per-port even if one were written.
**2. Submission to an external recipient is refused as a relay.** In the
`RCPT TO:` handler (near line 4855):
```c
if (p != alias_buf /* forced relay by alias */ &&
(!(startup->options & MAIL_OPT_ALLOW_RELAY)
|| relay_user.number == 0
|| relay_user.rest & (FLAG('G') | FLAG('M'))) &&
!find2strs(host_name, host_ip, relay_list, NULL)) {
... "!ILLEGAL RELAY ATTEMPT" ...
```
With `ALLOW_RELAY` clear, an authenticated, unrestricted user is refused -- this is the reported symptom. The only lever is `ALLOW_RELAY`, which applies
to every listener including port 25.
**3. No sender authorization on an authenticated session.** Every sender check is gated on `relay_user.number == 0`, so authenticating *disables* them:
* `MAIL FROM:` -- `chk_email_addr()` is skipped (near line 4595).
* `From:` header -- `chk_email_addr()` is skipped (near line 3629).
* The forged-From check (`compare_addrs(sender, sender_addr)`, near line 3730,
which emits `!FORGED mail header 'FROM' field`) is skipped (near line 3729).
Nothing compares the submitted address against the authenticated user's own address(es) or against the configured domain list.
**4. The envelope sender is rewritten, but the `From:` header is not.** For an authenticated sender, `SENDERNETTYPE` is left `NET_NONE` (near line 3749), so `sendmail_thread()` derives the outbound `MAIL FROM:` from
`usermailaddr(&scfg, str, msg.from)` (line 5821) rather than from the client-supplied reverse path. The visible `From:` header, however, is emitted verbatim:
```c
if ((p = smb_get_hfield(msg, RFC822FROM, NULL)) != NULL)
s = sockprintf(socket, prot, sess, "From: %s", p); /* use original RFC822 header field */
```
(line 794). So the SMTP envelope cannot be forged by an authenticated user, but the header that recipients actually see can be.
This has become more consequential now that the server signs outbound mail with DKIM: a message carrying a forged `From:` is signed with the system's key, lending its domain reputation to the forgery. It also means a legitimate submission whose `From:` is one of the system's domains can end up misaligned with an envelope sender derived from `usermailaddr()`, which matters for DMARC.
## Suggested direction
Not a proposal for a specific patch, just the shape of it:
1. **Record submission-ness in the session.** Add a field to `smtp_t` set from
`servprot` at accept time, so per-port policy becomes expressible at all.
This is one field; everything below depends on it.
2. **Require authentication on the submission ports** (RFC 6409 section 4.3),
with `530` on `MAIL` for an unauthenticated session. Default on, with an
option to disable for sysops with existing unauthenticated automation
pointed at 587.
3. **Treat authenticated submission as submission, not relay.** Do not require
`ALLOW_RELAY` for an authenticated session on the submission port.
`ALLOW_RELAY` continues to govern port 25.
4. **Verify the sender** (RFC 6409 section 6.1): reject `MAIL FROM:` and/or
`From:` when the address does not belong to the authenticated user or is not
at one of the system's domains, with `550 5.7.1`. Sysop-selectable strictness
is probably needed -- at minimum: off / domain must be ours / address must be
the user's.
5. Optionally, per RFC 6409 section 8, add a `Message-ID` when the client omits
one, and advertise `PIPELINING` and `ENHANCEDSTATUSCODES`.
An ARS would be a natural way to express item 3/4's authority, which overlaps with issue #107 ("SMTP relay access requirements").
-- *Authored by Claude (Claude Code), on behalf of @rswindell, from an IRC discussion between @rswindell and @Deuce*
--- SBBSecho 3.37-Linux
* Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)