Skip to content

Handshake the data connection, so an empty transfer over TLS works - #79

Open
christhomas wants to merge 1 commit into
secsy:masterfrom
antimatter-studios:cth/empty-store-upstream
Open

Handshake the data connection, so an empty transfer over TLS works#79
christhomas wants to merge 1 commit into
secsy:masterfrom
antimatter-studios:cth/empty-store-upstream

Conversation

@christhomas

Copy link
Copy Markdown

Fixes #63.

crypto/tls handshakes lazily, on the first Read or Write. A transfer that moves no bytes never triggers one, so the server is handed a plain TCP connection that opens and closes without ever speaking TLS. It rejects that and drops the control connection, and the caller sees:

error reading response: EOF

from a store that was otherwise perfectly valid.

@ior308 reported this against vsftpd, and noted the same store without TLS worked. It reproduces here against pure-ftpd, with exactly that asymmetry.

Where the handshake goes matters more than that it happens

My first attempt put it beside the tls.Client call in prepareDataConn and deadlocked. That function runs before the transfer command is sent, and the server does not begin the data channel's handshake until it has the command — so handshaking there waits for a server that is itself waiting for us. I assume that is why it was left implicit.

It belongs in the getter prepareDataConn returns, which runs after the command has been sent and acknowledged. That is the first moment it can complete.

An unexpected second fix

proftpd answered 425 Unable to build data connection: Operation not permitted to the same operation, which had looked like an unrelated server or config problem. The eager handshake fixes that too — it was the same cause seen from the other end.

Tests

empty_store_test.go — an empty store over TLS and the same without, so a failure distinguishes "TLS" from "empty". Confirmed red before the fix.

Full suite green three consecutive runs, which matters here: this changes every TLS transfer, not only the empty one.

One note on running them: this branch is off master, so TestMain still requires ./build_test_server.sh. I verified via the container harness in #73.

Fixes secsy#63.

crypto/tls handshakes lazily, on the first Read or Write. A transfer that
moves no bytes never triggers one, so the server is handed a plain TCP
connection that opens and closes without ever speaking TLS. It rejects
that and drops the control connection, and the caller sees:

    error reading response: EOF

from a store that was otherwise perfectly valid. Storing an empty file
over TLS is the case that hits it, which is exactly what was reported
against vsftpd — and reproduces here against pure-ftpd, while the same
store without TLS succeeds.

The handshake is now explicit. Where it goes matters more than that it
happens: it has to be in the getter prepareDataConn returns, not beside
the tls.Client call.

prepareDataConn runs before the transfer command is sent. The server does
not begin the data channel's handshake until it has that command, so
handshaking at connection time waits for a server that is itself waiting
for us — the first attempt at this deadlocked, which is presumably why
the handshake was left implicit in the first place. The getter runs after
the command has been sent and acknowledged, which is the first moment the
handshake can complete.

Also fixes proftpd answering "425 Unable to build data connection" to the
same operation, which had looked like a separate problem.

Red before green: both test servers failed an empty TLS store and passed
the same store without TLS. Full suite green three consecutive runs,
which matters here because this changes every TLS transfer and not only
the empty one.
@christhomas christhomas mentioned this pull request Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

error reading response: unexpected EOF while storing a 0 file size using TLS

1 participant