OSEC

Neohapsis is currently accepting applications for employment. For more information, please visit our website www.neohapsis.com or email hr@neohapsis.com
 
Please Review: Sendmail CA-2003-07 workaround

From: Wietse Venema (wietseporcupine.org)
Date: Tue Mar 04 2003 - 16:21:14 CST


This patch for Postfix 1.1 and later is meant to neutralize message
headers that could exploit the Sendmail vulnerability described in
CERT advisory CA-2003-07.

The idea is quite simple: each individual address in a message
header is limited to 250 bytes (including all the comments and
other non-address info), and thus cannot overflow of Sendmail's
256-byte address buffer with data under the attacker's control.

If an address is too long, Postfix first deletes all the comments
and other non-address info. If the result is still too long then
Postfix simply truncates the address without consideration for
unmatched quotes etc. The address is truncated at 250 characters
in order to allow for a little safety margin.

This patch adds Disposition-Notification-To: to the list of headers
whose addresses are rewritten by Postfix, just to be sure that
Postfix neutralizes all the addresses that Sendmail may rewrite.

If no-one sees a problem with this patch then I will release Postfix
2.0.6 tomorrow.

What still needs to be figured out is whether the vulnerability
also aplies to addresses in MAIL FROM and RCPT TO commands. In that
case the SMTP client would simply bounce long recipient addresses
as undeliverable.

        Wietse

*** ./tok822_parse.c- Sat Jun 8 14:46:10 2002
--- ./tok822_parse.c Tue Mar 4 16:35:28 2003
***************
*** 77,82 ****
--- 77,86 ----
  /* Append a line break after each comma token, instead of appending
  /* whitespace. It is up to the caller to concatenate short lines to
  /* produce longer ones.
+ /* .IP TOK822_STR_TRNC
+ /* Truncate addresses to 250 characters, to protect against the
+ /* Sendmail vulnerability described in CERT advisory CA-2003-07.
+ /* This flag has effect with tok822_externalize() only.
  /* .PP
  /* The macro TOK_822_NONE expresses that none of the above features
  /* should be activated.
***************
*** 85,93 ****
  /* TOK822_STR_TERM flags. This is useful for most token to string
  /* conversions.
  /*
! /* The macro TOK822_STR_HEAD combines the TOK822_STR_TERM and
! /* TOK822_STR_LINE flags. This is useful for the special case of
! /* token to mail header conversion.
  /*
  /* tok822_internalize() converts a token list to string form,
  /* without quoting. White space is inserted where appropriate.
--- 89,97 ----
  /* TOK822_STR_TERM flags. This is useful for most token to string
  /* conversions.
  /*
! /* The macro TOK822_STR_HEAD combines the TOK822_STR_TERM,
! /* TOK822_STR_LINE and TOK822_STR_TRNC flags. This is useful for
! /* the special case of token to mail header conversion.
  /*
  /* tok822_internalize() converts a token list to string form,
  /* without quoting. White space is inserted where appropriate.
***************
*** 241,259 ****
  {
      VSTRING *tmp;
      TOK822 *tp;
  
      if (flags & TOK822_STR_WIPE)
          VSTRING_RESET(vp);
  
      for (tp = tree; tp; tp = tp->next) {
          switch (tp->type) {
          case ',':
              VSTRING_ADDCH(vp, tp->type);
! if (flags & TOK822_STR_LINE) {
! VSTRING_ADDCH(vp, '\n');
! continue;
! }
! break;
  
              /*
               * XXX In order to correctly externalize an address, it is not
--- 245,290 ----
  {
      VSTRING *tmp;
      TOK822 *tp;
+ int start;
+ TOK822 *addr;
  
      if (flags & TOK822_STR_WIPE)
          VSTRING_RESET(vp);
  
+ #define RESET_ADDR_LENGTH { \
+ start = VSTRING_LEN(vp); \
+ addr = 0; \
+ }
+
+ #define ENFORCE_ADDR_LENGTH do { \
+ if (VSTRING_LEN(vp) - start > 250) { \
+ msg_warn("truncating address > 250 characters: %.200s...", \
+ vstring_str(vp) + start); \
+ vstring_truncate(vp, start); \
+ if (addr) { \
+ tmp = vstring_alloc(100); \
+ tok822_internalize(tmp, addr, TOK822_STR_TERM); \
+ quote_822_local_flags(vp, vstring_str(tmp), \
+ QUOTE_FLAG_8BITCLEAN | QUOTE_FLAG_APPEND); \
+ vstring_free(tmp); \
+ vstring_truncate(vp, start + 250); \
+ } \
+ } \
+ } while(0)
+
+ if (flags & TOK822_STR_TRNC)
+ RESET_ADDR_LENGTH;
+
      for (tp = tree; tp; tp = tp->next) {
          switch (tp->type) {
          case ',':
+ if (flags & TOK822_STR_TRNC)
+ ENFORCE_ADDR_LENGTH;
              VSTRING_ADDCH(vp, tp->type);
! VSTRING_ADDCH(vp, (flags & TOK822_STR_LINE) ? '\n' : ' ');
! if (flags & TOK822_STR_TRNC)
! RESET_ADDR_LENGTH;
! continue;
  
              /*
               * XXX In order to correctly externalize an address, it is not
***************
*** 263,268 ****
--- 294,300 ----
               * the issue of atoms in the domain part that would need quoting.
               */
          case TOK822_ADDR:
+ addr = tp->head;
              tmp = vstring_alloc(100);
              tok822_internalize(tmp, tp->head, TOK822_STR_TERM);
              quote_822_local_flags(vp, vstring_str(tmp),
***************
*** 294,299 ****
--- 326,334 ----
          if (tok822_append_space(tp))
              VSTRING_ADDCH(vp, ' ');
      }
+ if (flags & TOK822_STR_TRNC)
+ ENFORCE_ADDR_LENGTH;
+
      if (flags & TOK822_STR_TERM)
          VSTRING_TERMINATE(vp);
      return (vp);
***************
*** 617,626 ****
          vstream_printf("Internalized:\n%s\n\n",
                  vstring_str(tok822_internalize(vp, list, TOK822_STR_DEFL)));
          vstream_printf("Externalized, no newlines inserted:\n%s\n\n",
! vstring_str(tok822_externalize(vp, list, TOK822_STR_DEFL)));
          vstream_printf("Externalized, newlines inserted:\n%s\n\n",
                         vstring_str(tok822_externalize(vp, list,
! TOK822_STR_DEFL | TOK822_STR_LINE)));
          vstream_fflush(VSTREAM_OUT);
          tok822_free_tree(list);
      }
--- 652,662 ----
          vstream_printf("Internalized:\n%s\n\n",
                  vstring_str(tok822_internalize(vp, list, TOK822_STR_DEFL)));
          vstream_printf("Externalized, no newlines inserted:\n%s\n\n",
! vstring_str(tok822_externalize(vp, list,
! TOK822_STR_DEFL | TOK822_STR_TRNC)));
          vstream_printf("Externalized, newlines inserted:\n%s\n\n",
                         vstring_str(tok822_externalize(vp, list,
! TOK822_STR_DEFL | TOK822_STR_LINE | TOK822_STR_TRNC)));
          vstream_fflush(VSTREAM_OUT);
          tok822_free_tree(list);
      }
*** ./tok822.h- Sat Jun 8 14:23:18 2002
--- ./tok822.h Tue Mar 4 16:27:53 2003
***************
*** 87,94 ****
  #define TOK822_STR_WIPE (1<<0)
  #define TOK822_STR_TERM (1<<1)
  #define TOK822_STR_LINE (1<<2)
  #define TOK822_STR_DEFL (TOK822_STR_WIPE | TOK822_STR_TERM)
! #define TOK822_STR_HEAD (TOK822_STR_TERM | TOK822_STR_LINE)
  
   /*
    * tok822_find.c
--- 87,95 ----
  #define TOK822_STR_WIPE (1<<0)
  #define TOK822_STR_TERM (1<<1)
  #define TOK822_STR_LINE (1<<2)
+ #define TOK822_STR_TRNC (1<<3)
  #define TOK822_STR_DEFL (TOK822_STR_WIPE | TOK822_STR_TERM)
! #define TOK822_STR_HEAD (TOK822_STR_TERM | TOK822_STR_LINE | TOK822_STR_TRNC)
  
   /*
    * tok822_find.c
*** ./header_opts.c- Fri May 24 15:55:31 2002
--- ./header_opts.c Tue Mar 4 17:00:57 2003
***************
*** 59,64 ****
--- 59,65 ----
      "Content-Transfer-Encoding", HDR_CONTENT_TRANSFER_ENCODING, HDR_OPT_MIME,
      "Content-Type", HDR_CONTENT_TYPE, HDR_OPT_MIME,
      "Delivered-To", HDR_DELIVERED_TO, 0,
+ "Disposition-Notification-To", HDR_DISP_NOTIFICATION, HDR_OPT_SENDER,
      "Date", HDR_DATE, 0,
      "Errors-To", HDR_ERRORS_TO, HDR_OPT_SENDER,
      "From", HDR_FROM, HDR_OPT_SENDER,
*** ./header_opts.h- Fri May 24 15:55:04 2002
--- ./header_opts.h Tue Mar 4 17:16:21 2003
***************
*** 53,58 ****
--- 53,59 ----
  #define HDR_CONTENT_DISPOSITION 28
  #define HDR_CONTENT_ID 29
  #define HDR_MIME_VERSION 30
+ #define HDR_DISP_NOTIFICATION 31
  
   /*
    * Header flags.