[tpop3d-discuss] tpop3d and incompetently-written virus scanners (was: Re: Tpop3d Bug?)

Chris Lightfoot chris at ex-parrot.com
Tue, 30 Sep 2003 19:55:38 +0100

  [ Cc: to list, in case anybody else is having the same
    kind of trouble; and to Norman ASA, authors of the
    offending product, who *just might* want to fix it. ]

On Tue, Sep 30, 2003 at 06:52:16PM +0200, M. Boelen wrote:
> Yes, we have tried to use 1.4.2. This version doesn't have the "bug". We 
> have a few FreeBSD servers now that use 1.4.2 and some that use 1.5.1 
> (and some 1.5.2).
> The products that have the problems are commercial (ie. Norton 

Well, I've discovered the problem with the Norman product,
`Norman Virus Center'.

It was written by morons.

Did you know that it takes up 70 megabytes of memory just
to be a POP3 proxy? I could sacrifice 70,000 times the
computer memory that got men to the moon and back to the
silly thing and it still wouldn't run.

More germane to this problem... the idiots who wrote it
assumed that a whole POP3 response (+OK/-ERR ... \r\n)
would be received in a single recv call, that is, in a
single TCP packet. This assumption is certainly wrong,
since no protocol can make that guarantee, and is not
present in the RFC, which defines the POP3 protocol. Only
a very lazy programmer would make such an assumption --
hardly the kind of person you'd want working on something
security-critical like a virus scanner.

Between tpop3d 1.4.2 and 1.5.1, I changed the way that
command responses are sent so that they *may* be split
over several write calls. This means that they *may* be
split over several TCP segments -- more likely now than
before, though either case was possible previously. (This
change came with a rewrite of the tpop3d I/O layer to
incorporate TLS support, and avoids an additional buffer

This means that the idiot Norman program never sees
responses from tpop3d. It's not restricted to the +OK
response after the password -- it won't see a -ERR
response, either.

Here is the patch:

diff -u -r1.49 connection.c
--- connection.c        14 Jul 2003 23:31:20 -0000      1.49
+++ connection.c        30 Sep 2003 18:52:19 -0000
@@ -381,9 +381,30 @@
  * Send a +OK... / -ERR... response to a message. Returns 1 on success or 0 on
  * failure. */
 int connection_sendresponse(connection c, const int success, const char *s) {
-    if (connection_send(c, success ? "+OK " : "-ERR ", success ? 4 : 5)
-            && connection_send(c, s, strlen(s))
-            && connection_send(c, "\r\n", 2)) {
+    /*
+     * For efficiency's sake, we should send this bit-by-bit, avoiding another
+     * buffer copy. But unfortunately, there are POP3 clients in the world
+     * so stupid that they assume a whole response will arrive in a single TCP
+     * segment. Particular examples include POP3 virus-scanning proxies, such
+     * as Norman ASA's, which was evidently written by somebody very lazy.
+     * 
+     * Obviously there's no way to guarantee how the packets in a TCP stream
+     * are disposed, in general, but we can increase the probability of success
+     * by trying to ensure here that our response is contained in a single
+     * write call. It still might get split up by the ioabs layer, but we have
+     * to take our chances....
+     */
+    static char *buf;
+    static size_t buflen;
+    size_t l;
+    l = (success ? 6 : 7) + strlen(s);
+    if (!buf || buflen < l + 1)
+        buf = xmalloc(buflen = l + 1);
+    sprintf(buf, "%s %s\r\n", success ? "+OK" : "-ERR", s);
+    if (connection_send(c, buf, l)) {
         if (verbose)
             log_print(LOG_DEBUG, _("connection_sendresponse: client %s: sent `%s %s'"), c->idstr, success ? "+OK" : "-ERR", s);
         return 1;

(This is now in CVS, though I much resent including it,
since it damages my code -- albeit slightly -- in order to
work around the incompetence of the people from Norman.
I'd be interested to see whether Norton made the same

Early [ethernet] developers... objected to a roundoff error that
exceeded the ARPANET's entire bandwidth, but marketing won out.
(Evi Nemeth)