COMMAND

    PHP

SYSTEMS AFFECTED

    PHP 4.0.4

PROBLEM

    Following is based on  a geekgang Security Advisory  [gsa2001-01].
    PHP 4.0.4 contains a fix for a buffer overflow in the imap module.
    Unfortunately  this  fix  breaks   the  imap  module  under   some
    circumstances due to its interaction with the WU c-client  library
    which PHP uses to implement the imap protocol.

    The imap  module in  PHP contained  a buffer  overflow in versions
    prior to 4.0.4, due to improper use of strcpy().  The fix in 4.0.4
    resolves the strcpy() problem, but causes the imap module to  fail
    under some  circumstances.   For example,  the IMP  WebMail system
    fails to work correctly under  4.0.4, so PHP 4.0.3 is  extensively
    deployed for use with IMP.  A number of WebMail systems are likely
    to be vulnerable to this issue.

    The PHP imap module relies on the WU c-client library to  actually
    perform  imap  (and  POP3,  NNTP  and  local  mailbox)   requests.
    Additionally,  the  c-client  library  uses  callbacks into PHP in
    order to  ascertain the  username and  password for  the requested
    connection.

    The patch  in PHP  4.0.4 changed  the behavior  of the imap module
    such that the username and password is no longer stored beyond the
    initial imap_open() call.  However, the c-client library may still
    call the callback function  to retrieve the username  and password
    outside  of  this  call,  which  then  returns  garbage data.  For
    example, the imap_reopen()  function triggers this  call sequence.
    This issue appears to be fixed in the current CVS version of PHP.

SOLUTION

    The gsa2001-01.diff  patch against  php-4.0.4pl1 reverts  the imap
    module  to  4.0.3  behavior,  without  reintroducing  the   buffer
    overflow.

    diff -ru php-4.0.4pl1/ext/imap/php_imap.c php-4.0.4pl1-patched/ext/imap/php_imap.c
    --- php-4.0.4pl1/ext/imap/php_imap.c	Wed Oct 25 18:43:52 2000
    +++ php-4.0.4pl1-patched/ext/imap/php_imap.c	Mon Feb 26 13:17:08 2001
    @@ -371,8 +371,8 @@
    
     static void php_imap_init_globals(zend_imap_globals *imap_globals)
     {
    -	imap_globals->imap_user=NIL;
    -	imap_globals->imap_password=NIL;
    +	imap_globals->imap_user[0] = 0;
    +	imap_globals->imap_password[0] = 0;
 	    imap_globals->imap_folders=NIL;
 	    imap_globals->imap_sfolders=NIL;
 	    imap_globals->imap_alertstack=NIL;
    @@ -633,8 +633,11 @@
 		    }
 	    }
    
    -	IMAPG(imap_user)     = estrndup(Z_STRVAL_PP(user), Z_STRLEN_PP(user));
    -	IMAPG(imap_password) = estrndup(Z_STRVAL_PP(passwd), Z_STRLEN_PP(passwd));
    +	strncpy(IMAPG(imap_user), Z_STRVAL_PP(user), IMAPSTRLEN);
    +	strncpy(IMAPG(imap_password), Z_STRVAL_PP(passwd), IMAPSTRLEN);
    +
    + 	IMAPG(imap_user)[IMAPSTRLEN-1] = 0;
    +	IMAPG(imap_password)[IMAPSTRLEN-1] = 0;
    
     #ifdef OP_RELOGIN
 	    /* AJS: persistent connection handling */
    @@ -766,8 +769,6 @@
 	    } else {
     #endif
 		    imap_stream = mail_open(NIL, Z_STRVAL_PP(mailbox), flags);
    -		efree(IMAPG(imap_user));
    -		efree(IMAPG(imap_password));
    
 		    if (imap_stream == NIL) {
 			    php_error(E_WARNING, "Couldn't open stream %s\n", (*mailbox)->value.str.val);
    diff -ru php-4.0.4pl1/ext/imap/php_imap.h php-4.0.4pl1-patched/ext/imap/php_imap.h
    --- php-4.0.4pl1/ext/imap/php_imap.h	Tue Oct 17 16:42:05 2000
    +++ php-4.0.4pl1-patched/ext/imap/php_imap.h	Mon Feb 26 13:16:11 2001
    @@ -13,6 +13,7 @@
     extern zend_module_entry imap_module_entry;
     #define imap_module_ptr &imap_module_entry
    
    +#define IMAPSTRLEN 80
    
     /* Data types */
    
    @@ -140,8 +141,8 @@
     PHP_FUNCTION(imap_mime_header_decode);
    
     ZEND_BEGIN_MODULE_GLOBALS(imap)
    -	char *imap_user;
    -	char *imap_password;
    +	char imap_user[IMAPSTRLEN];
    +	char imap_password[IMAPSTRLEN];
 	    STRINGLIST *imap_folders;
 	    STRINGLIST *imap_sfolders;
 	    STRINGLIST *imap_alertstack;

    Below is a patch  against php-4.0.4pl1 (backported from  php-cvs),
    which cures  the problem  without imposing  80-character limits or
    using static buffers.   Just committed it  to the  OpenBSD-current
    port of PHP4:

    --- ext/imap/php_imap.c.orig	Tue Mar  6 09:22:17 2001
    +++ ext/imap/php_imap.c	Tue Mar  6 09:24:10 2001
    @@ -25,7 +25,7 @@
        | PHP 4.0 updates:  Zeev Suraski <zeev@zend.com>                       |
        +----------------------------------------------------------------------+
      */
    -/* $Id: php_imap.c,v 1.50 2000/10/25 17:43:52 andrei Exp $ */
    +/* $Id: php_imap.c,v 1.57 2001/02/21 20:33:46 thies Exp $ */
    
     #define IMAP41
    
    @@ -183,7 +183,19 @@
     void mail_close_it(zend_rsrc_list_entry *rsrc)
     {
 	    pils *imap_le_struct = (pils *)rsrc->ptr;
    +	IMAPLS_FETCH();
    +
 	    mail_close_full(imap_le_struct->imap_stream, imap_le_struct->flags);
    +
    +	if (IMAPG(imap_user)) {
    +		efree(IMAPG(imap_user));
    +		IMAPG(imap_user) = 0;
    +	}
    +	if (IMAPG(imap_password)) {
    +		efree(IMAPG(imap_password));
    +		IMAPG(imap_password) = 0;
    +	}
    +
 	    efree(imap_le_struct);
     }
    
    @@ -633,6 +645,14 @@
 		    }
 	    }
    
    +	if (IMAPG(imap_user)) {
    +		efree(IMAPG(imap_user));
    +	}
    +
    +	if (IMAPG(imap_password)) {
    +		efree(IMAPG(imap_password));
    +	}
    +
 	    IMAPG(imap_user)     = estrndup(Z_STRVAL_PP(user), Z_STRLEN_PP(user));
 	    IMAPG(imap_password) = estrndup(Z_STRVAL_PP(passwd), Z_STRLEN_PP(passwd));
    
    @@ -712,6 +732,8 @@
 				    }
  			    }
 			    efree(hashed_details);
    +			efree(IMAPG(imap_user)); IMAPG(imap_user) = 0;
    +			efree(IMAPG(imap_password)); IMAPG(imap_password) = 0;
 			    RETURN_FALSE;
 		    }
    
    @@ -721,6 +743,8 @@
 			    node = malloc(sizeof(pils));
 			    if (node == NULL) {
 				    efree(hashed_details);
    +				efree(IMAPG(imap_user)); IMAPG(imap_user) = 0;
    +				efree(IMAPG(imap_password)); IMAPG(imap_password) = 0;
 				    RETURN_FALSE;
 			    }
    
    @@ -757,6 +781,8 @@
    
 			    free(headp);
 			    efree(hashed_details);
    +			efree(IMAPG(imap_user)); IMAPG(imap_user) = 0;
    +			efree(IMAPG(imap_password)); IMAPG(imap_password) = 0;
  			    RETURN_FALSE;
 		    }
    
    @@ -766,11 +792,11 @@
 	    } else {
     #endif
 		    imap_stream = mail_open(NIL, Z_STRVAL_PP(mailbox), flags);
    -		efree(IMAPG(imap_user));
    -		efree(IMAPG(imap_password));
    
 		    if (imap_stream == NIL) {
 			    php_error(E_WARNING, "Couldn't open stream %s\n", (*mailbox)->value.str.val);
    +			efree(IMAPG(imap_user)); IMAPG(imap_user) = 0;
    +			efree(IMAPG(imap_password)); IMAPG(imap_password) = 0;
 			    RETURN_FALSE;
 		    }