open
https://gitlab.synchro.net/main/sbbs/-/issues/1192
## Summary
When the Web Server's file-area virtual path is enabled (`FileVPathPrefix`, e.g. `/files/`), an **unauthenticated** request downloads files with **no credit check and no credit charge**. The FTP Server applies the credit check to the same file for the same anonymous user.
The result is that enabling `FileVPathPrefix` silently makes every file in any directory with empty ARS free to the entire internet, regardless of the credit values on those files.
## Why this looks like a defect rather than a policy choice
Issue #446 reported anonymous FTP refusing a download with `550 Insufficient credit (177008 required)`. It was closed with:
This is expected - not every directory on Vertrauen allows unlimited free downloads.
This is not a Synchronet issue.
So credit enforcement for anonymous downloads is the stated intended behavior. The Web Server does not implement it.
## Where the paths diverge
**Web — anonymous requests skip the credit gate.** `check_ars()` handles `AUTHENTICATION_UNKNOWN` in its own branch, and for a file path it returns the result of `user_can_download()` alone (`src/sbbs3/websrvr.cpp:2085`):
```c
if (session->user.number == 0) {
switch (session->parsed_vpath) {
case PARSED_VPATH_FULL:
return user_can_download(&scfg, session->file.dir, &session->user, &session->client, NULL);
```
The credit gate sits further down, *after* the password check, so only authenticated sessions reach it (`websrvr.cpp:2211`):
```c
if (session->parsed_vpath == PARSED_VPATH_FULL) {
if (download_is_free(&scfg, session->file.dir, &session->user, &session->client)
|| session->user.cdt >= session->file.cost)
authorized = user_can_download(&scfg, session->file.dir, &session->user, &session->client, &reason);
```
**FTP — the gate is unconditional.** `ftpsrvr.cpp:4672` applies it to every user, anonymous included:
```c
if (!getsize && !getdate && !delecmd
&& !download_is_free(&scfg, dir, &user, &client)) {
...
if (f.cost > user_available_credits(&user)) {
sockprintf(sock, sess, "550 Insufficient credit (%lu required).", (ulong)f.cost);
```
The charge side has the same asymmetry in effect: `user_downloaded_file()` runs for web downloads (`websrvr.cpp:6541`) and calls `subtract_cdt()`, but with a zeroed user #0 it does nothing, because `adjustuserval()` early-returns on `!VALID_USER_NUMBER(user->number)`.
## Reproduction
On a system with `FileVPathPrefix=/files/`, a file directory whose `ars`, `download_ars` and the library's equivalents are all empty, and which is **not** flagged `FREE`:
```
$ curl -sI
http://<host>/files/<lib>/<dir>/<file.zip> | grep -i content-length Content-Length: 255869
```
Downloads in full with no authentication. The same file's record carries `Cost 255869` (confirmed via `smbutil v`), and the same file over anonymous FTP is refused with `550 Insufficient credit`.
Observed on Synchronet 3.22 for Linux.
## Scope and what is *not* affected
- **Opt-in.** `FileVPathPrefix` is empty in stock `sbbs.ini`, so a default install is unaffected. This only applies to sysops who deliberately published their file areas over HTTP.
- **ARS still applies.** `user_can_download()` continues to enforce library/directory `ar` and `dl_ar`, and an unauthenticated user has level 0, so any ARS referencing `LEVEL`/security/flags correctly excludes it. A sysop who has set `download_ars` is not exposed.
- **No data corruption.** The accounting calls are safe no-ops for user #0 (`adjustuserval()` validates the user number; `subtract_cdt()` returns early on a zero amount).
- **Per-file and system statistics still count** — `times_downloaded` and `inc_download_stats()` run regardless of user number. It is only per-user accounting that is skipped.
## Possible directions
Not proposing a specific fix, since which is right depends on the intended policy:
1. Apply the same `download_is_free() || cdt >= cost` gate in the `user.number == 0` branch, making the Web Server match FTP. An anonymous user has 0 credits, so this would make costed files unavailable anonymously — consistent with #446, but a behavior change for anyone relying on the current behavior.
2. Leave the behavior and document it, so `FileVPathPrefix` carries an explicit warning that it exposes file areas without credit enforcement, with `download_ars` named as the control.
3. Make it configurable — a web-server option for whether anonymous file-vpath downloads honor credits.
Worth noting the current behavior is *convenient* for publishing an intentionally-public archive; the concern is that it is not discoverable from the configuration surface, and it contradicts the policy stated in #446.
— *Authored by Claude (Claude Code), on behalf of @rswindell*
--- SBBSecho 3.37-Linux
* Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)