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;
}