COMMAND

    imapd

SYSTEMS AFFECTED

    imapd-4.7c and prior versions then combined with mail.local and probably some another MDAs

PROBLEM

    3APA3A found following.   It's not big,  but interesting  security
    hole.  In fact neither mail.local  nor imap has any bugs, but  the
    problem rises  from the  fact there  is no  clear format  for Unix
    mailbox.  Since mail.local has more suitable mailbox format 3apa3a
    decided to report problem as imap problem.

    Then  delivering  message  to  user's mailbox mail.local believes,
    that messages  are delimited  by empty  line followed  by "From  "
    header.   If this  combination found  in message  text it  will be
    commented  out.   Yet  imap,  while  parsing user's mailbox relies
    only on "From"  header in special  format and doesn't  check empty
    line.   This makes  it possible  to put  faked message  with faked
    RFC-822 header (including all Received: fields) in user's  mailbox
    using properly formatted "From" header after non-empty line.  This
    can be used both for  sending spoofed messages and DoS'ing  user's
    mailbox by emulating a huge number of messages.

    Shell string below  send a single  approx. 2.5 Mb  message.  After
    delivering imap will think there  is more then 70,000 messages  in
    user's mailbox.

        perl -e 'print "1\nFrom user Wed Dec  2 05:53:22 1992\n\n"x70000' |\
        mail -s "Oooops I did it again" victim

    Then this 70,000 messages are received with MUA, MUA usually fails
    (mostly because of memory problems).  Was tested with The Bat! and
    Netscape.   After  that   on  FreeBSD  3.2  PIII  128Mb box author
    noticed next behavior:  ipop3  (imapd  was not tested)  freezes in
    sbwait state, locking 99% CPU and amount of memory (much more then
    message size itself).  After 30 minutes situation has not changed.

SOLUTION

    One patch is  for imapd-4.7c.   It was not  excessively tested and
    may be not complete, but believed to close this very hole.

    *** unix.c.old  Wed Feb 23 03:43:30 2000
    --- unix.c      Thu Aug 10 12:58:19 2000
    ***************
    *** 1048,1053 ****
    --- 1048,1054 ----
        unsigned long i,j,k,m;
        char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
        int ti = 0,pseudoseen = NIL,retain = T;
    +   int wasempty;
        unsigned long nmsgs = stream->nmsgs;
        unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
        unsigned long recent = stream->recent;
    ***************
    *** 1389,1404 ****
            k = m = 0;              /* no previous line size yet */
                                    /* note current position */
            j = LOCAL->filesize + GETPOS (&bs);
            if (i) do {             /* look for next message */
              s = unix_mbxline (stream,&bs,&i);
              if (i) {              /* got new data? */
    !           VALID (s,t,ti,zn);  /* yes, parse line */
                if (!ti) {          /* not a header line, add it to message */
                  elt->rfc822_size +=
                    k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
                                    /* update current position */
                  j = LOCAL->filesize + GETPOS (&bs);
                }
              }
            } while (i && !ti);     /* until found a header */
            elt->private.msg.text.text.size = j -
    --- 1390,1408 ----
            k = m = 0;              /* no previous line size yet */
                                    /* note current position */
            j = LOCAL->filesize + GETPOS (&bs);
    +       wasempty = 1;
            if (i) do {             /* look for next message */
              s = unix_mbxline (stream,&bs,&i);
              if (i) {              /* got new data? */
    !           if (wasempty) VALID (s,t,ti,zn);    /* yes, parse line */
                if (!ti) {          /* not a header line, add it to message */
                  elt->rfc822_size +=
                    k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
                                    /* update current position */
                  j = LOCAL->filesize + GETPOS (&bs);
                }
    +           if ( *s == '\n') wasempty = 1;
    +           else wasempty = 0;
              }
            } while (i && !ti);     /* until found a header */
            elt->private.msg.text.text.size = j -