https://bz.apache.org/SpamAssassin/show_bug.cgi?id=8383

            Bug ID: 8383
           Summary: spamc silently fails on TLS 1.3 connections to spamd —
                    three bugs in SSL read/write handling
           Product: Spamassassin
           Version: 4.0.3
          Hardware: PC
                OS: Mac OS X
            Status: NEW
          Severity: normal
          Priority: P2
         Component: spamc/spamd
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: Undefined

Environment:

FreeBSD (ports build)
SpamAssassin 4.0.2
OpenSSL with TLS 1.3 support
spamc built with ENABLE_SSL=yes
Summary:

spamc hangs or silently fails when connecting to spamd over TLS 1.3. The
connection completes but spamc never successfully sends the SPAMC protocol,
resulting in a 30-second wait followed by spamd closing the connection. Three
bugs were found in spamc/libspamc.c and spamc/utils.c.

Diagnosis:

Using openssl s_client confirmed spamd itself handles TLS 1.3 correctly —
manually sending PING SPAMC/1.5\r\n\r\n after the handshake returns a clean
SPAMD/1.5 0 PONG. The bug is entirely in spamc's SSL handling.

Bug 1: ssl_timeout_read retry loop checks wrong error mechanism (spamc/utils.c)

The retry loop used errno == EWOULDBLOCK to decide whether to retry a failed
SSL_read. OpenSSL does not use errno for this — it uses its own error queue,
accessed via SSL_get_error(). The correct retry condition is SSL_get_error(ssl,
rc) == SSL_ERROR_WANT_READ. As written, the retry loop never fired for any
SSL-level condition.

Bug 2: SSL_write not retried on SSL_ERROR_WANT_READ (spamc/libspamc.c)

In TLS 1.3, the server sends post-handshake NewSessionTicket records after the
handshake completes. When spamc calls SSL_write to send the SPAMC protocol
header, OpenSSL may need to drain these pending records first and returns
SSL_ERROR_WANT_READ. The original code treated any rc <= 0 from SSL_write as a
fatal error with no retry, causing spamc to fail silently. The fix wraps
SSL_write in a retry loop that handles both SSL_ERROR_WANT_READ and
SSL_ERROR_WANT_WRITE.

Bug 3: SSL_write(ssl, buf, 0) treated as fatal error (spamc/libspamc.c)

For commands with no body — specifically PING (-K) — towrite_len is 0. Calling
SSL_write with a length of 0 returns 0, which the existing rc <= 0 error check
treated as a fatal failure. The non-SSL full_write path handles zero-length
writes as a harmless no-op. The fix skips the body SSL_write call entirely when
towrite_len == 0.

Fix:

Pull request in place here: https://github.com/apache/spamassassin/pull/26

Three commits:

4ec853f — Fix spamc hang on TLS 1.3 (bugs 1 & 2), add -D/--ssl-debug flag
18200047 — Fix SSL_write failure when body length is zero (bug 3)
20e98f0 — Document -D/--ssl-debug in spamc.pod
Testing:


spamc -S -K -d localhost -p 784          # PING: returns SPAMD/1.5 0 PONG
spamc -S -d localhost -p 784 < spam.msg  # Full message: returns tagged output
Both confirmed working over TLS 1.3 after the fix.

Note: This report was prepared with assistance from an LLM (Claude). The
debugging, testing, and confirmation were performed by the submitter on live
hardware.

-- 
You are receiving this mail because:
You are the assignee for the bug.

Reply via email to