Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 28 Jun 1998 03:42:25 -0600
From:      "Aaron D. Gifford" <agifford@infowest.com>
To:        <security@FreeBSD.ORG>
Subject:   UIDL overruns in qpopper
Message-ID:  <199806281925.NAA24567@infowest.com>

next in thread | raw e-mail | index | archive | help
When I first saw patches for pop_dropcopy.c that limit the uidl string
length to 128, I couldn't see any overrun potential there.  Then Miquel
van Smoorenburg <miquels@CISTRON.NL> explained that the actual overrun
occur in pop_uidl.c but can be prevented in pop_dropcopy.c.  I looked at
pop_uidl.c and sure enough, Miquel was correct.  Further investigation
revealed that another potential buffer overrun can occur in some cases
where a huge From: header occurs and the EUIDL command is used by a POP
client.

The patch below constitute several changes I use on my FreeBSD boxen.
It includes a fix for the signal 11 problem that some have reported
after patch-ag is applied.  It include a POP parameter limiting patch
which you might want to avoid (it is the patch to the pop_parse.c file)
-- I like it and it has been working on a 5,000-user system with no
known problems yet -- but it might break something I don't know about
or violate POP protocl.  It includes a fix to the UIDL overrun by
limiting the size of the UIDL data in pop_dropcopy.c.  It also fixes
another potentil UIDL overrun by reducing a buffer in pop_uidl.c.
Finally, it also includes 2 cosmetic fixes that I like because my log
files are more readable -- one change to pop_init.c and another to
pop_auth.c.

Whew!

Please be aware that my mail software might wrap a line or two of the
patch when I send this and cause it to break.


Here goes:
==========

diff -p work/qpopper2.41beta1/pop_auth.c work2/qpopper2.41beta1/pop_auth.c
*** work/qpopper2.41beta1/pop_auth.c	Wed Nov 19 14:20:38 1997
--- work2/qpopper2.41beta1/pop_auth.c	Sat Jun 27 23:34:14 1998
*************** int pop_auth (p)
*** 23,29 ****
  POP     *   p;
  {
      /*  Tell the user that this command is not supported */
!     return (pop_msg(p,POP_FAILURE,"This command is not supported yet"));
  }
  
  
--- 23,29 ----
  POP     *   p;
  {
      /*  Tell the user that this command is not supported */
!     return (pop_msg(p,POP_FAILURE,"The auth command is not supported yet"));
  }
  
  
diff -p work/qpopper2.41beta1/pop_dropcopy.c work2/qpopper2.41beta1/pop_dropcopy.c
*** work/qpopper2.41beta1/pop_dropcopy.c	Sun Jun 28 12:58:14 1998
--- work2/qpopper2.41beta1/pop_dropcopy.c	Sun Jun 28 13:07:47 1998
*************** POP *p;
*** 489,495 ****
  		    /* Skip over header string */
  		    cp = &buffer[7];
                      while (*cp && (*cp == ' ' || *cp == '\t')) cp++;
!                     if(strlen(cp) < DIG_SIZE) /* To account for the empty UIDL string */
                      {
                          uidl_found--; /*roll over as though it hasn't seen anything*/
                          continue;
--- 489,501 ----
  		    /* Skip over header string */
  		    cp = &buffer[7];
                      while (*cp && (*cp == ' ' || *cp == '\t')) cp++;
!                     /*
!                      * The UIDL digest SHOULD be approx. 32 chars long,
!                      * so reject/skip any X-UIDL: lines that don't fit
!                      * this profile.  A new X-UIDL: line will be created
!                      * for any messages that don't have a valid one.
!                      */
!                     if(strlen(cp) < DIG_SIZE || strlen(cp) > DIG_SIZE * 3)
                      {
                          uidl_found--; /*roll over as though it hasn't seen anything*/
                          continue;
diff -p work/qpopper2.41beta1/pop_init.c work2/qpopper2.41beta1/pop_init.c
*** work/qpopper2.41beta1/pop_init.c	Wed Nov 19 14:20:38 1997
--- work2/qpopper2.41beta1/pop_init.c	Sat Jun 27 23:38:54 1998
*************** char    **      argmessage;
*** 281,288 ****
      ch = gethostbyaddr((char *) &cs.sin_addr, sizeof(cs.sin_addr), AF_INET);
      if (ch == NULL){
          pop_log(p,POP_PRIORITY,
!             "(v%s) Unable to get canonical name of client, err = %d",
! 	    VERSION, errno);
          p->client = p->ipaddr;
      }
      /*  Save the cannonical name of the client host in 
--- 281,288 ----
      ch = gethostbyaddr((char *) &cs.sin_addr, sizeof(cs.sin_addr), AF_INET);
      if (ch == NULL){
          pop_log(p,POP_PRIORITY,
!             "(v%s) Unable to get canonical name of client [%], err = %d",
! 	    VERSION, p->ipaddr, errno);
          p->client = p->ipaddr;
      }
      /*  Save the cannonical name of the client host in 
diff -p work/qpopper2.41beta1/pop_msg.c work2/qpopper2.41beta1/pop_msg.c
*** work/qpopper2.41beta1/pop_msg.c	Sun Jun 28 12:58:15 1998
--- work2/qpopper2.41beta1/pop_msg.c	Sat Jun 27 23:25:59 1998
*************** va_dcl
*** 61,67 ****
  
      /*  Point past the POP status indicator in the message message */
      l = strlen(mp);
!     len -= l, mp += l;
  
      /*  Append the message (formatted, if necessary) */
      if (format) 
--- 61,74 ----
  
      /*  Point past the POP status indicator in the message message */
      l = strlen(mp);
!     mp += l;
!     /*
!      * Subtract an additional 2 from the remaining buffer length
!      * so that after the vsnprintf()/snprintf() calls there will
!      * still be enough buffer space to append a "\r\n" even in a
!      * worst-case scenario.
!      */
!     len -= l - 2;
  
      /*  Append the message (formatted, if necessary) */
      if (format) 
*************** va_dcl
*** 90,97 ****
                 (p->user ? p->user : "(null)"), p->client, message);
  
      /*  Append the <CR><LF> */
!     len -= strlen(message);
!     (void)strncat(message, len, "\r\n");
          
      /*  Send the message to the client */
      (void)fputs(message,p->output);
--- 97,104 ----
                 (p->user ? p->user : "(null)"), p->client, message);
  
      /*  Append the <CR><LF> */
!     len -= strlen(mp);
!     (void)strncat(message, "\r\n", len);
          
      /*  Send the message to the client */
      (void)fputs(message,p->output);
diff -p work/qpopper2.41beta1/pop_parse.c work2/qpopper2.41beta1/pop_parse.c
*** work/qpopper2.41beta1/pop_parse.c	Wed Nov 19 14:20:38 1997
--- work2/qpopper2.41beta1/pop_parse.c	Sat Jun 27 22:58:17 1998
*************** char        *   buf;        /*  Pointer 
*** 26,31 ****
--- 26,32 ----
  {
      char            *   mp;
      register int        i;
+     register int        parmlen;
      
      /*  Loop through the POP command array */
      for (mp = buf, i = 0; ; i++) {
*************** char        *   buf;        /*  Pointer 
*** 45,52 ****
          /*  Point to the start of the token */
          p->pop_parm[i] = mp;
  
          /*  Search for the first space character (end of the token) */
!         while (!isspace(*mp) && *mp) mp++;
  
          /*  Delimit the token with a null */
          if (*mp) *mp++ = 0;
--- 46,75 ----
          /*  Point to the start of the token */
          p->pop_parm[i] = mp;
  
+         /*  Start counting the length of this token */
+         parmlen = 0;
+ 
          /*  Search for the first space character (end of the token) */
!         while (!isspace(*mp) && *mp) {
!             mp++;
!             parmlen++;
!             if (parmlen > MAXPARMLEN) {
!                 /* Truncate parameter to the max. allowable size */
!                 *mp = '\0';
! 
!                 /* Fail with an appropriate message */
!                 if (i == 0) {
!                     pop_msg(p,POP_FAILURE,
!                             "Command \"%s\" (truncated) exceedes maximum permitted size.",
!                             p->pop_command);
!                 } else {
!                     pop_msg(p,POP_FAILURE,
!                             "Argument %d \"%s\" (truncated) exceeds maximum permitted size.",
!                             i, p->pop_parm[i]);
!                 }
!                 return(-1);
!             }
!         }
  
          /*  Delimit the token with a null */
          if (*mp) *mp++ = 0;
diff -p work/qpopper2.41beta1/pop_uidl.c work2/qpopper2.41beta1/pop_uidl.c
*** work/qpopper2.41beta1/pop_uidl.c	Wed Nov 19 14:20:38 1997
--- work2/qpopper2.41beta1/pop_uidl.c	Sun Jun 28 13:09:56 1998
*************** from_hdr(p, mp)
*** 101,107 ****
       POP         *p;
       MsgInfoList *mp;
  {
!   char buf[MAXLINELEN], *cp;
  
      fseek(p->drop, mp->offset, 0);
      while (fgets(buf, sizeof(buf), p->drop) != NULL) {
--- 101,112 ----
       POP         *p;
       MsgInfoList *mp;
  {
!   /*
!    * Shorten this buffer so that an extra-long From: header
!    * won't overflow the buffers in the pop_euidl() where
!    * this function is called.  128 should be sufficient.
!    */
!   static char buf[MAXLINELEN - 128], *cp;
  
      fseek(p->drop, mp->offset, 0);
      while (fgets(buf, sizeof(buf), p->drop) != NULL) {
diff -p work/qpopper2.41beta1/popper.h work2/qpopper2.41beta1/popper.h
*** work/qpopper2.41beta1/popper.h	Sun Jun 28 12:58:15 1998
--- work2/qpopper2.41beta1/popper.h	Sun Jun 28 11:56:10 1998
***************
*** 59,65 ****
  #define MAXMSGLINELEN   MAXLINELEN
  #define MAXCMDLEN       4
  #define MAXPARMCOUNT    5
! #define MAXPARMLEN      10
  #define ALLOC_MSGS  20
  
  #ifndef OSF1
--- 59,65 ----
  #define MAXMSGLINELEN   MAXLINELEN
  #define MAXCMDLEN       4
  #define MAXPARMCOUNT    5
! #define MAXPARMLEN      32      /*  Large enough for 32-byte APOP parm. */
  #define ALLOC_MSGS  20
  
  #ifndef OSF1





Have fun, but not too much! ;)

Aaron out.


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe security" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199806281925.NAA24567>