diff -Nura php-5.1.4/Changelog.hphp hardening-patch-5.1.4-0.4.15/Changelog.hphp --- php-5.1.4/Changelog.hphp 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Changelog.hphp 2006-09-07 19:32:29.000000000 +0200 @@ -0,0 +1,61 @@ +Changelog of the Hardening-Patch +-------------------------------- + +0.4.15 - 07. September 2006 + + PHP4: + [+] Fix for potential DOS in handling of include blacklists + + PHP4+5: + [+] Backported a fix for open_basedir problems with insanse PHP scripts + [+] Added a fix for ini_restore() PHP security vulnerability + +0.4.14 - 11. August 2006 + + PHP4: + [+] Remove unecessary call to AC_BROKEN_REALPATH + + PHP5: + [+] Fix Remote URL Include Protection - Thanks to: Bart Vanbrabant + + PHP4+5: + [+] Added a few PHP security fixes / see changelog.secfix for details + [+] Fixed the memory_limit protection for systems with different perdir memory_limits + [+] Fixed a possible memory corruption when foreach() is used with wrong arguments + +0.4.13 - 07. August 2006 + + PHP4+5: + [+] Added a fix for a compile problem on solaris due to missing strcasestr() + +0.4.12 - 19. July 2006 + + PHP4: + [+] Added fixes from sf4 security patch / see changelog.secfix for details + + PHP5: + [+] Added fixes from sf5 security patch / see changelog.secfix for details + + PHP4+5: + [+] Added anti mail spam feature + [+] Speedup of zend_hash canary (clear/destroy) + [+] Added a fix for a DOS in the handling of URL blacklists + +0.4.11 - 13. May 2006 + + PHP5: + [+] tsrm_virtual_cwd.c: close open_basedir, safe_mode hole introduced by realpath() cache + [+] install-pear-nozlib.phar: bundle in full package download of 5.1.4 + + PHP4+5: + [+] tsrm_virtual_cwd.c: realpath() hotfix to solve problems with non existing directories + + +0.4.10 - 11. May 2006 + + PHP4: + [+] info.c: backport from 5.1.4 contained TSRMLS macro that had to be removed + + PHP4+5: + [+] fopen_wrappers.c: fix for a trailing slash problem with open_basedir + diff -Nura php-5.1.4/Changelog.secfix hardening-patch-5.1.4-0.4.15/Changelog.secfix --- php-5.1.4/Changelog.secfix 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Changelog.secfix 2006-09-05 20:31:02.000000000 +0200 @@ -0,0 +1,40 @@ +Changelog of PHP 5.1.4 Security Fixes + +Release 6 - 11. August 2006 + + [+] Added IMAP open_basedir/safe_mode check + [+] Added a fix for previous ext/session fixes + [+] Added upstream fix to ext/socket + [+] Added sscanf() security fix + [+] Added fixes for handling of corrupt .gif files to gdlib + +Release 5 - 13. July 2006 + + [+] Fixed compilation of Security-Patch Release 4 in ZTS mode + +Release 4 - 13. July 2006 + + [+] Added a recursive array printing fix to the phpinfo() XSS fix + [+] Added a fix for stat() on non existing files in safe_mode + +Release 3 - 07. July 2006 + + [+] Added a fix for an integer overflow in str_repeat() + [+] Added a *working* wordwrap() fix + [+] Added code to make memory_limit work on 64bit systems + [+] Added a fix for the error_log() safe_mode/open_basedir vulnerability + [+] Added a fix for overlong tempfilename + [+] Added multiple fixes for new safe_mode/open_basedir problems in ext/curl + [+] Added a high characters fix to ext/wddx + +Release 2 - 16. May 2006 + + [+] Remove install-pear-nozlib.phar from the patchfile, because the official PHP + tarball got updated + +Release 1 - 13. May 2006 + + [+] Bundle install-pear-nozlib.phar which was missing in the official PHP tarball + and is downloaded when make install is called (usually as root -> security risk) + [+] Fixed open_basedir/safe_mode bypass via the realpath() cache + diff -Nura php-5.1.4/configure hardening-patch-5.1.4-0.4.15/configure --- php-5.1.4/configure 2006-05-12 16:41:10.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/configure 2006-09-05 20:31:02.000000000 +0200 @@ -942,6 +942,16 @@ ac_help="$ac_help --with-libdir=NAME Look for libraries in .../NAME rather than .../lib" ac_help="$ac_help + --disable-hardening-patch-mm-protect Disable the Memory Manager protection." +ac_help="$ac_help + --disable-hardening-patch-ll-protect Disable the Linked List protection." +ac_help="$ac_help + --disable-hardening-patch-inc-protect Disable include/require protection." +ac_help="$ac_help + --disable-hardening-patch-fmt-protect Disable format string protection." +ac_help="$ac_help + --disable-hardening-patch-hash-protect Disable Zend HashTable DTOR protection." +ac_help="$ac_help SAPI modules: " @@ -1410,6 +1420,8 @@ ac_help="$ac_help --enable-wddx Enable WDDX support" ac_help="$ac_help + --disable-varfilter Disable Hardening-Patch's variable filter" +ac_help="$ac_help --disable-xml Disable XML support" ac_help="$ac_help --with-libxml-dir=DIR XML: libxml2 install prefix" @@ -3618,6 +3630,157 @@ +# Check whether --enable-hardening-patch-mm-protect or --disable-hardening-patch-mm-protect was given. +if test "${enable_hardening_patch_mm_protect+set}" = set; then + enableval="$enable_hardening_patch_mm_protect" + + DO_HARDENING_PATCH_MM_PROTECT=$enableval + +else + + DO_HARDENING_PATCH_MM_PROTECT=yes + +fi + + +# Check whether --enable-hardening-patch-ll-protect or --disable-hardening-patch-ll-protect was given. +if test "${enable_hardening_patch_ll_protect+set}" = set; then + enableval="$enable_hardening_patch_ll_protect" + + DO_HARDENING_PATCH_LL_PROTECT=$enableval + +else + + DO_HARDENING_PATCH_LL_PROTECT=yes + +fi + + +# Check whether --enable-hardening-patch-inc-protect or --disable-hardening-patch-inc-protect was given. +if test "${enable_hardening_patch_inc_protect+set}" = set; then + enableval="$enable_hardening_patch_inc_protect" + + DO_HARDENING_PATCH_INC_PROTECT=$enableval + +else + + DO_HARDENING_PATCH_INC_PROTECT=yes + +fi + + +# Check whether --enable-hardening-patch-fmt-protect or --disable-hardening-patch-fmt-protect was given. +if test "${enable_hardening_patch_fmt_protect+set}" = set; then + enableval="$enable_hardening_patch_fmt_protect" + + DO_HARDENING_PATCH_FMT_PROTECT=$enableval + +else + + DO_HARDENING_PATCH_FMT_PROTECT=yes + +fi + + +# Check whether --enable-hardening-patch-hash-protect or --disable-hardening-patch-hash-protect was given. +if test "${enable_hardening_patch_hash_protect+set}" = set; then + enableval="$enable_hardening_patch_hash_protect" + + DO_HARDENING_PATCH_HASH_PROTECT=$enableval + +else + + DO_HARDENING_PATCH_HASH_PROTECT=yes + +fi + + +echo $ac_n "checking whether to protect the Zend Memory Manager""... $ac_c" 1>&6 +echo "configure:2725: checking whether to protect the Zend Memory Manager" >&5 +echo "$ac_t""$DO_HARDENING_PATCH_MM_PROTECT" 1>&6 + +echo $ac_n "checking whether to protect the Zend Linked Lists""... $ac_c" 1>&6 +echo "configure:2729: checking whether to protect the Zend Linked Lists" >&5 +echo "$ac_t""$DO_HARDENING_PATCH_LL_PROTECT" 1>&6 + +echo $ac_n "checking whether to protect include/require statements""... $ac_c" 1>&6 +echo "configure:2733: checking whether to protect include/require statements" >&5 +echo "$ac_t""$DO_HARDENING_PATCH_INC_PROTECT" 1>&6 + +echo $ac_n "checking whether to protect PHP Format String functions""... $ac_c" 1>&6 +echo "configure:2737: checking whether to protect PHP Format String functions" >&5 +echo "$ac_t""$DO_HARDENING_PATCH_FMT_PROTECT" 1>&6 + +echo $ac_n "checking whether to protect the Zend HashTable Destructors""... $ac_c" 1>&6 +echo "configure:2737: checking whether to protect the Zend HashTable Destructors" >&5 +echo "$ac_t""$DO_HARDENING_PATCH_HASH_PROTECT" 1>&6 + + +cat >> confdefs.h <<\EOF +#define HARDENING_PATCH 1 +EOF + + + +if test "$DO_HARDENING_PATCH_MM_PROTECT" = "yes"; then + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_MM_PROTECT 1 +EOF + +else + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_MM_PROTECT 0 +EOF + +fi + +if test "$DO_HARDENING_PATCH_LL_PROTECT" = "yes"; then + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_LL_PROTECT 1 +EOF + +else + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_LL_PROTECT 0 +EOF + +fi + +if test "$DO_HARDENING_PATCH_INC_PROTECT" = "yes"; then + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_INC_PROTECT 1 +EOF + +else + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_INC_PROTECT 0 +EOF + +fi + +if test "$DO_HARDENING_PATCH_FMT_PROTECT" = "yes"; then + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_FMT_PROTECT 1 +EOF + +else + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_FMT_PROTECT 0 +EOF + +fi + +if test "$DO_HARDENING_PATCH_HASH_PROTECT" = "yes"; then + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_HASH_PROTECT 1 +EOF + +else + cat >> confdefs.h <<\EOF +#define HARDENING_PATCH_HASH_PROTECT 0 +EOF + +fi @@ -18607,6 +18770,62 @@ fi + echo $ac_n "checking whether realpath is broken""... $ac_c" 1>&6 +echo "configure:14928: checking whether realpath is broken" >&5 +if eval "test \"`echo '$''{'ac_cv_broken_realpath'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + if test "$cross_compiling" = yes; then + + ac_cv_broken_realpath=no + +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + + ac_cv_broken_realpath=no + +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + + ac_cv_broken_realpath=yes + +fi +rm -fr conftest* +fi + + +fi + +echo "$ac_t""$ac_cv_broken_realpath" 1>&6 + if test "$ac_cv_broken_realpath" = "yes"; then + cat >> confdefs.h <<\EOF +#define PHP_BROKEN_REALPATH 1 +EOF + + else + cat >> confdefs.h <<\EOF +#define PHP_BROKEN_REALPATH 0 +EOF + + fi + + echo $ac_n "checking for declared timezone""... $ac_c" 1>&6 echo "configure:18612: checking for declared timezone" >&5 if eval "test \"`echo '$''{'ac_cv_declared_timezone'+set}'`\" = set"; then @@ -89422,7 +89641,7 @@ if test "$ac_cv_crypt_blowfish" = "yes"; then ac_result=1 else - ac_result=0 + ac_result=1 fi cat >> confdefs.h <&6 +echo "configure:82041: checking whether to enable Hardening-Patch's variable filter" >&5 +# Check whether --enable-varfilter or --disable-varfilter was given. +if test "${enable_varfilter+set}" = set; then + enableval="$enable_varfilter" + PHP_VARFILTER=$enableval +else + + PHP_VARFILTER=yes + + if test "$PHP_ENABLE_ALL" && test "yes" = "yes"; then + PHP_VARFILTER=$PHP_ENABLE_ALL + fi + +fi + + + +ext_output="yes, shared" +ext_shared=yes +case $PHP_VARFILTER in +shared,*) + PHP_VARFILTER=`echo "$PHP_VARFILTER"|sed 's/^shared,//'` + ;; +shared) + PHP_VARFILTER=yes + ;; +no) + ext_output=no + ext_shared=no + ;; +*) + ext_output=yes + ext_shared=no + ;; +esac + + + +echo "$ac_t""$ext_output" 1>&6 + + + + +if test "$PHP_VARFILTER" != "no"; then + cat >> confdefs.h <<\EOF +#define HAVE_VARFILTER 1 +EOF + + + ext_builddir=ext/varfilter + ext_srcdir=$abs_srcdir/ext/varfilter + + ac_extra= + + if test "$ext_shared" != "shared" && test "$ext_shared" != "yes" && test "" != "cli"; then + + + + case ext/varfilter in + "") ac_srcdir="$abs_srcdir/"; unset ac_bdir; ac_inc="-I. -I$abs_srcdir" ;; + /*) ac_srcdir=`echo "ext/varfilter"|cut -c 2-`"/"; ac_bdir=$ac_srcdir; ac_inc="-I$ac_bdir -I$abs_srcdir/$ac_bdir" ;; + *) ac_srcdir="$abs_srcdir/ext/varfilter/"; ac_bdir="ext/varfilter/"; ac_inc="-I$ac_bdir -I$ac_srcdir" ;; + esac + + + + b_c_pre=$php_c_pre + b_cxx_pre=$php_cxx_pre + b_c_meta=$php_c_meta + b_cxx_meta=$php_cxx_meta + b_c_post=$php_c_post + b_cxx_post=$php_cxx_post + b_lo=$php_lo + + + old_IFS=$IFS + for ac_src in varfilter.c; do + + IFS=. + set $ac_src + ac_obj=$1 + IFS=$old_IFS + + PHP_GLOBAL_OBJS="$PHP_GLOBAL_OBJS $ac_bdir$ac_obj.lo" + + case $ac_src in + *.c) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.cpp) ac_comp="$b_cxx_pre $ac_extra $ac_inc $b_cxx_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_cxx_post" ;; + esac + + cat >>Makefile.objects<>Makefile.objects<>Makefile.objects<> confdefs.h <>Makefile.objects<>Makefile.objects<&6 @@ -112351,7 +112829,7 @@ php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \ strlcat.c mergesort.c reentrancy.c php_variables.c php_ticks.c \ network.c php_open_temporary_file.c php_logos.c \ - output.c ; do + output.c hardening_patch.c ; do IFS=. set $ac_src @@ -112596,7 +113074,7 @@ zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \ zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \ zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ - zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c; do + zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_canary.c; do IFS=. set $ac_src diff -Nura php-5.1.4/configure.in hardening-patch-5.1.4-0.4.15/configure.in --- php-5.1.4/configure.in 2006-05-04 01:30:02.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/configure.in 2006-09-05 20:31:02.000000000 +0200 @@ -209,7 +209,7 @@ sinclude(Zend/Zend.m4) sinclude(TSRM/tsrm.m4) - +sinclude(main/hardening_patch.m4) divert(2) @@ -1275,7 +1275,7 @@ php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \ strlcat.c mergesort.c reentrancy.c php_variables.c php_ticks.c \ network.c php_open_temporary_file.c php_logos.c \ - output.c ) + output.c hardening_patch.c ) PHP_ADD_SOURCES(main/streams, streams.c cast.c memory.c filter.c \ plain_wrapper.c userspace.c transports.c xp_socket.c mmap.c) @@ -1302,7 +1302,7 @@ zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \ zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \ zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ - zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c) + zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_canary.c ) if test -r "$abs_srcdir/Zend/zend_objects.c"; then PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c zend_mm.c \ diff -Nura php-5.1.4/ext/curl/interface.c hardening-patch-5.1.4-0.4.15/ext/curl/interface.c --- php-5.1.4/ext/curl/interface.c 2006-04-13 13:26:10.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/curl/interface.c 2006-09-05 20:31:02.000000000 +0200 @@ -167,6 +167,11 @@ RETURN_FALSE; \ } \ \ + if (php_memnstr(str, tmp_url->path, strlen(tmp_url->path), str + len)) { \ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Url '%s' contains unencoded control characters.", str); \ + RETURN_FALSE; \ + } \ + \ if (tmp_url->query || tmp_url->fragment || php_check_open_basedir(tmp_url->path TSRMLS_CC) || \ (PG(safe_mode) && !php_checkuid(tmp_url->path, "rb+", CHECKUID_CHECK_MODE_PARAM)) \ ) { \ @@ -1065,7 +1070,6 @@ case CURLOPT_FTPLISTONLY: case CURLOPT_FTPAPPEND: case CURLOPT_NETRC: - case CURLOPT_FOLLOWLOCATION: case CURLOPT_PUT: #if CURLOPT_MUTE != 0 case CURLOPT_MUTE: @@ -1116,6 +1120,16 @@ convert_to_long_ex(zvalue); error = curl_easy_setopt(ch->cp, option, Z_LVAL_PP(zvalue)); break; + case CURLOPT_FOLLOWLOCATION: + convert_to_long_ex(zvalue); + if ((PG(open_basedir) && *PG(open_basedir)) || PG(safe_mode)) { + if (Z_LVAL_PP(zvalue) != 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "CURLOPT_FOLLOWLOCATION cannot be activated when in safe_mode or an open_basedir is set"); + RETURN_FALSE; + } + } + error = curl_easy_setopt(ch->cp, option, Z_LVAL_PP(zvalue)); + break; case CURLOPT_URL: case CURLOPT_PROXY: case CURLOPT_USERPWD: diff -Nura php-5.1.4/ext/curl/streams.c hardening-patch-5.1.4-0.4.15/ext/curl/streams.c --- php-5.1.4/ext/curl/streams.c 2006-01-01 13:50:01.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/curl/streams.c 2006-09-05 20:31:02.000000000 +0200 @@ -289,7 +289,11 @@ curl_easy_setopt(curlstream->curl, CURLOPT_WRITEHEADER, stream); /* currently buggy (bug is in curl) */ - curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 1); + if ((PG(open_basedir) && *PG(open_basedir)) || PG(safe_mode)) { + curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 0); + } else { + curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 1); + } curl_easy_setopt(curlstream->curl, CURLOPT_ERRORBUFFER, curlstream->errstr); curl_easy_setopt(curlstream->curl, CURLOPT_VERBOSE, 0); diff -Nura php-5.1.4/ext/fbsql/php_fbsql.c hardening-patch-5.1.4-0.4.15/ext/fbsql/php_fbsql.c --- php-5.1.4/ext/fbsql/php_fbsql.c 2006-01-01 13:50:06.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/fbsql/php_fbsql.c 2006-09-05 20:31:02.000000000 +0200 @@ -1925,8 +1925,24 @@ } else if (fbcmdErrorsFound(md)) { +#if HARDENING_PATCH + char* query_copy; + int i; +#endif FBCErrorMetaData* emd = fbcdcErrorMetaData(c, md); char* emg = fbcemdAllErrorMessages(emd); +#if HARDENING_PATCH + query_copy=estrdup(query_copy); + for (i=0; query_copy[i]; i++) if (query_copy[i]<32) query_copy[i]='.'; + php_security_log(S_SQL, "fbsql error: %s - query: %s", emg, query_copy); + efree(query_copy); + if (HG(hphp_sql_bailout_on_error)) { + free(emg); + fbcemdRelease(emd); + result = 0; + zend_bailout(); + } +#endif if (FB_SQL_G(generateWarnings)) { if (emg) diff -Nura php-5.1.4/ext/gd/libgd/gd.c hardening-patch-5.1.4-0.4.15/ext/gd/libgd/gd.c --- php-5.1.4/ext/gd/libgd/gd.c 2005-09-30 22:48:05.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/gd/libgd/gd.c 2006-09-05 20:31:02.000000000 +0200 @@ -2161,7 +2161,7 @@ for (x = 0; (x < w); x++) { int c = gdImageGetPixel (src, srcX + x, srcY + y); if (c != src->transparent) { - gdImageSetPixel (dst, dstX + x, dstY + y, gdTrueColor(src->red[c], src->green[c], src->blue[c])); + gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c])); } } } diff -Nura php-5.1.4/ext/gd/libgd/gd_gd2.c hardening-patch-5.1.4-0.4.15/ext/gd/libgd/gd_gd2.c --- php-5.1.4/ext/gd/libgd/gd_gd2.c 2005-08-18 14:54:43.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/gd/libgd/gd_gd2.c 2006-09-05 20:31:02.000000000 +0200 @@ -430,6 +430,10 @@ gdImagePtr im; + if (w<1 || h <1) { + return 0; + } + /* The next few lines are basically copied from gd2CreateFromFile * we change the file size, so don't want to use the code directly. * but we do need to know the file size. diff -Nura php-5.1.4/ext/gd/libgd/gd_gif_in.c hardening-patch-5.1.4-0.4.15/ext/gd/libgd/gd_gif_in.c --- php-5.1.4/ext/gd/libgd/gd_gif_in.c 2005-09-24 16:39:16.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/gd/libgd/gd_gif_in.c 2006-09-05 20:31:02.000000000 +0200 @@ -44,7 +44,7 @@ #define LOCALCOLORMAP 0x80 #define BitSet(byte, bit) (((byte) & (bit)) == (bit)) -#define ReadOK(file,buffer,len) (gdGetBuf(buffer, len, file) != 0) +#define ReadOK(file,buffer,len) (gdGetBuf(buffer, len, file) > 0) #define LM_to_uint(a,b) (((b)<<8)|(a)) @@ -147,6 +147,9 @@ Background = buf[5]; AspectRatio = buf[6]; + imw = LM_to_uint(buf[0],buf[1]); + imh = LM_to_uint(buf[2],buf[3]); + if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */ if (ReadColorMap(fd, BitPixel, ColorMap)) { return 0; @@ -182,14 +185,13 @@ bitPixel = 1<<((buf[8]&0x07)+1); - imw = LM_to_uint(buf[4],buf[5]); - imh = LM_to_uint(buf[6],buf[7]); - if (!(im = gdImageCreate(imw, imh))) { - return 0; - } + if (!(im = gdImageCreate(imw, imh))) { + return 0; + } + im->interlace = BitSet(buf[8], INTERLACE); if (! useGlobalColormap) { - if (ReadColorMap(fd, bitPixel, localColorMap)) { + if (ReadColorMap(fd, bitPixel, localColorMap)) { return 0; } ReadImage(im, fd, imw, imh, localColorMap, @@ -212,6 +214,10 @@ if (!im) { return 0; } + if (!im->colorsTotal) { + gdImageDestroy(im); + return 0; + } /* Check for open colors at the end, so we can reduce colorsTotal and ultimately BitsPerPixel */ @@ -502,6 +508,18 @@ int v; int xpos = 0, ypos = 0, pass = 0; int i; + + /* + ** Initialize the Compression routines + */ + if (! ReadOK(fd,&c,1)) { + return; + } + + if (c > MAX_LWZ_BITS) { + return; + } + /* Stash the color map into the image */ for (i=0; (ired[i] = cmap[CM_RED][i]; @@ -511,12 +529,7 @@ } /* Many (perhaps most) of these colors will remain marked open. */ im->colorsTotal = gdMaxColors; - /* - ** Initialize the Compression routines - */ - if (! ReadOK(fd,&c,1)) { - return; - } + if (LWZReadByte(fd, TRUE, c) < 0) { return; } diff -Nura php-5.1.4/ext/gd/libgd/gd_gif_out.c hardening-patch-5.1.4-0.4.15/ext/gd/libgd/gd_gif_out.c --- php-5.1.4/ext/gd/libgd/gd_gif_out.c 2006-03-13 22:56:38.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/gd/libgd/gd_gif_out.c 2006-09-05 20:31:02.000000000 +0200 @@ -265,9 +265,11 @@ int InitCodeSize; int i; GifCtx ctx; + + memset(&ctx, 0, sizeof(ctx)); ctx.Interlace = GInterlace; ctx.in_count = 1; - memset(&ctx, 0, sizeof(ctx)); + ColorMapSize = 1 << BitsPerPixel; RWidth = ctx.Width = GWidth; diff -Nura php-5.1.4/ext/gd/tests/bug37346.gif hardening-patch-5.1.4-0.4.15/ext/gd/tests/bug37346.gif --- php-5.1.4/ext/gd/tests/bug37346.gif 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/gd/tests/bug37346.gif 2006-09-05 20:31:02.000000000 +0200 @@ -0,0 +1,4 @@ +GIF89a +< + +看吧, 我都说过滤不严了 \ Kein Zeilenumbruch am Dateiende. diff -Nura php-5.1.4/ext/gd/tests/bug37346.phpt hardening-patch-5.1.4-0.4.15/ext/gd/tests/bug37346.phpt --- php-5.1.4/ext/gd/tests/bug37346.phpt 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/gd/tests/bug37346.phpt 2006-09-05 20:31:02.000000000 +0200 @@ -0,0 +1,13 @@ +--TEST-- +Bug #37346 (gdimagecreatefromgif, bad colormap) +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: imagecreatefromgif(): '%sbug37346.gif' is not a valid GIF file in %sbug37346.php on line %d diff -Nura php-5.1.4/ext/gd/tests/bug37360.gif hardening-patch-5.1.4-0.4.15/ext/gd/tests/bug37360.gif --- php-5.1.4/ext/gd/tests/bug37360.gif 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/gd/tests/bug37360.gif 2006-09-05 20:31:02.000000000 +0200 @@ -0,0 +1,469 @@ +GIF89aK痔奈臆栽+w彬渲遮溴葑睒庘颈灬瞩缳cフ蝓檑觏 Bs阉蓊蝤剖犹烫燃呈驮韭索铈艺哿盼郧姬谳鋻悥貂鰸9辶磈摬2壣却搪箷櫒咤耜芪忮肴耨褶忸彶挖揲隣埐Is柟脚Η蓉*兤逗茂乡眄磲後;k呵-f撃哪觋赍邋胍沛轵莺揶驳靳谮跊《鲽粥摸槥掣0徽匠饰刭衷腼拨汩骕ix虑Q劶技拶站畅t嚄箐质唐邻藩桤0劽啑ч瘟媸兼偬咳槊甸膈重蒉噤樗筋镳值﹪Ь镖替桕嘤瞥泊夔眵腴嬲忭赡潬秩氽唛腠醇顺防徙$Qx,\勼篝葆觑哐迤蛊任咤镢轷即矚佩4嵨潘夙秣躞-篚鲰腴4嬎痤2嬏鲺蠖咕坩腌邈$m⒉溷鉁唦珙佳腠镏葭噍谶圬鷾%}姥汞腠耖腭袜镔骜a槰9嵥爱便彗忭隽览坩眚确仠崩沟席圯嘭揣暗2埰狗弗煘崁|灘等粕槠2嵨5徯劯=娒掊樾行1=H徵C斘谉部z【萝颹偂攥牢垜5嵥躞鰛勩珞彘蹂膈羼珉麇膑徵箸媵沌4嬐彗/嚾沌蹯腠徨蠼裔挧拂缳?吂匐黼热塞姞6撜焕畜躞唠糇噻鼢! NETSCAPE2.1! , H盃羶*\劝∶#J淗雹艐3j苋保菑 CI菠蓳(S猏刹ニ0c蕼I肠蜎8s贶沙烜 +J川眩H*]蚀┯滼氮斋X砵菔但庄`脢K冬俪h营]硕穚闶滽樊莼x箨菟矾呖 n汆脠N谈便菒#K濴共逅3k尢钩缦燙M捍橛⊿玘秃惦装c藶M欢碹竤朕突凤呃 N几胥葥+_渭贵缧烴胶蹼爻k呶交黠嗝O炯栌玙暇禁黟闼烵烤脒峡2j("评x0笕0(xLc:話`儅駜>@僀&棎 ;e嗬 彀X4,鈃屸*:涿N$)$ 桯 ,灗2 K躧 衲a1i<忻H幚搑񝏂"炕07wテ爸R闶#涉 i蕈Zs艀闹⒚3甛挵操d賝!k鮆?H敊#鏵琿惱閚寄<瘻4瞨緀0黰5 +]:寙薏'(獋O"#`慲a鳗h狰貞壝ii oSD1G|2绹^$f` 剟fP )$+橮>0"胑H梻糎e2惼鐟&獝$@ 榩~B5!U郂 +0A 蔠搋郷3l TQ$ 襚萡.芇蹄~`盦 葼(!4﹊d){砢!3っ?0 +!t!6(貈燖`&E1S(劆z0褒Q)钾酂d袎g, 皢銩 >悈,lE%D @羉V8錜& 6q 猷A0XCB,lP胰B0厃|餚鍾鲳C8齚Nx?綻H PA瀉JPY'噄硪怨擱#P唸B:鈵媐P >c0 -B舉 汏,8袜W E+d0l相E(;$ +Hl鄥?Xc'B廍0Y(琗5腀3p奜 H +H.賭岶滺]^0DiH 2JU猨U粯A.驯1琄┡饓g腀*皟 +趭? @垏毞甌b~xM7 圛8A &溌a 傈 笮CA0睭馟炑崻@g砕樴0@q僒 礚羷)纏贤&槀m賞覼6a髕摖 2&b鋺Y6@8.D準悛Uz愋裏(擗0a N P衼棒鈇-跏旓J 靉&@8`p 瑈瓚\L瓾癙 +#t閻17谺癅9%yS 4▎]!&b焵Ax)@绹D鐱m*; gū崙踿5啵=鍵*乬 ! 鄡槺剅斆 +@Xμf X# B杚驅$4 h3`f瀫-犘滟5枯瞻 7豩1鶈vP 膒 樫 +8f嬭 n3N@:襚.R L逤独硻;P5愍O`囥N&纼G:% 愶3踞F8V6餖剅戳'廓`凙bⅶL_渶鹎砋矁$嗂拙6纼f{HCw蚮攷o 鎚抔餈吂G9淨 +k宗 d8$n鎟8k-溉 雼kf窰<癷#菪B癙k@芈迴桫лy堻谸-*蔜琳*魅7袀篓徨礷h NO座50陁及諆箘 铯o$緡8詋]x阙.u弡决y馏KU:鄏 帎劰00砪{vz9\鄠ks99+饙6越鉜(3膼钰8↗\ +9X伖l肅d闈z朴)擅/@瀜y' 鯩帙D繟W. }KTP降!不=礽 + m91庌k 3!瓓CmMpE$诎"0nD 皃诅螾7kuk饁\杇~f&x相i+ p蘰吗 豟RF妧WP k 鉦.<@颱Qgi'k磛m輤LX l' 滰w鎳厬smV`P +J匤( +Tc L0X@饌\4揊,恛鶩T鄡B  篅0仐g╱S0[ EP嵃 4鄅r6Abd@,朏崸o0u岪譌T +}m0py @,謖m傪噣美鲃Mg&P +岪 k`H,-P墥H譴晇iP{鵙 +m kP{1讓A"垁' D莙烿!O0婮 +p媃W 孈T 肵t犂c侨wv亶Bum 9yQwN踿缻辷虪 曟m抪繝豍巰\R%崁 ◢骝 d(蕡g.悆009sT ダ匷+騎N 5鄲婄gs`6筐(矵0 儏S~ +v崁9驸wpz3)悆刀0pm胰u N@v 渶擹Dp@P藹Q賾Sw塮俉牁 +勑-0崉 鯲H l 扌4 9P嵭R_NM~9wyIV暡鴳儛 mu娮6r5@ +-嬥n衪 q輕毜FS 寪)@磒 + 碃筰~000曟8 臥yPW塎Z +9~Qy@~衦 +Sa杙y 鱬爢 纻fy +搻'0橤熈O _檋 煥儕 逘5{謷 0恑Q嗀S]&{謇 9k檕⺋{?鴵█]0 檿5Yw}X PL0扖 0sL yp@t尿 Xt3翣篑 Fp兿0k僯貐=PqムG)i幜`蟨LP 恏~k>x -D@k)溃" +3ii涡 衻j貕=鴼帠P 婸 辠~戲2( 悝Y"0-p汎嬭 T袙 睓`q悵葨湴趬鯥u- )焵 !zy@ +i緪 鄬蜙o袃薨~皬牟 uT +V-馉 p氒@0玓尠v︵|pT0p' 戰═貞` |eXS{拫 4w矦i2軃0n\宰唉 k Ay1 Q皠UD 昑朻 1燪洞 擆 T猖>X儼ei.i鴥G祒祎A毉挆o濜桖枓沺 Qw彃榿 S晘垷Ot*z` +閫旹pP`o0s8 +鍾殫`} I鸱甙x`q字 紜隯 gm橂{剋P  圲P +Yh俺汸灣I. 嫲{鎟钡 @ +)Ug6* 椂遈栏劵澃魉qV4UjfK0挔R 圝 欯 滶r0 奱#嚢.烒爃責菠#AV綦 .欨p紋@@ LZp >怜改I}H 酿囦a +攩硽 凘婡燦w獁J(G,⺁n嵃~菋0 +Q 衜粏 +偧杽LX@D@ 5`}z 綳孧姁 喾 藮郍 0,4q腊媭萼Z皜軥Q账膄喨>h)@L 隸扏g{ + оW&@捆藞0 7 繀\x肂 .@<H 饶m縅秄0歚oo婼C M噋腊觙弃氟;u胜p簝,y +潏 囆烉|~xh茖羵p=砳 +虒O q@浶t症 :Ё-Pg姎柝檩 Z嘌鷓Hυ擒悒Q 輈鸾A6J汏栁祀 拔7B匌 B^|m0p嘈Js+隣瘕iPDp遳  婮0?d穲lc牶q-楠禆貺P +0u6m鱌儼撟欒冟輤厕Po H 壃涘皯Q Tpfu埛`爭P<刞茨r苄煂鄩T巴呪推g4劦 瀮矻yp飙怦砣X惧]n>0┪ q辧墽)0A糶K袇9饁鐚p~No +P燒i=婳 萴綅M!U`l雋他だ}気P+/<銆掔@qk ~z瀘GP +元陜痯啴sQ垜绊临猡軪l`Z:啐番葷璎娃}璌fV[倜暴滴嚒=0P鸹 騣`鋂W 巋-k嚖)泂* .搆雀.@.p b 蟙麛J L"]⒊睻Xr3 zmr7鈁L孈砚Yk|m^賦@ 垹:g聩0躽0嬃笥*衇0>_p-墍C?濬 FG饁.6L尷 MKLp沵~VP戰贽媂 杙℡\A9覥無E?'螾蹝`蝬 饈~{鎈M榒瘺缧璩 婶- +`%` 嫚姊諞  7bt蟺ymUk徼M究鸍M犨睳恏g`-} n-偈濝 麟$< ラ 游 vi綍rK歺癛梧A &IIQL鳺馹 +4鴆B廍!E嘃v豳IR8c俨%AF韦Y渔M9u钿儆鏞燗厏L挸R48蠖C7g6澳耫峽B+9"┩B癮鄞0b7BFH_Т謞凃,+]簞IK隷纴&\豴E2F瀜肂e詎4,-礱7 +幋h伦啊:@,DM詣d諺c1U礀黤絏疙us骯嗔'N,T附鍫苍 ,Ki佨亴`$Mì骋莼$+燛棑鄑'睈堨ⅱ丗8%ヘ7")R0)衶ocp@ 4"#竫g98蒩オ(X蕝5l;rX +碭 8鹉[H}瀳H塐(b="馈槱.軠!-缆箹@$揟r蓽 $R愧A柂*0勃^媚酣"VYG顮!$q`’蕭█櫔pMD悾摽6(DD -8亰*N怚Fut8"卬(览g嘿ò牎,毫 y銩)劰 +RD垑鑹V 1躾 1" 碍G柡ab瓈:钓Zl玚噏X&婃2]渊6唳 $鞕Tp:Zm 菮br+$駑蠧!璹f# y4j`b嶟 ?m+G 艂蝉鴆恎個檊Tr茀 t慉\` +灚税*聏5y]Tc;重` 1xa $崋KBY粬纲R稟  鵮5剄騻iLaz* ♂7敏h-慦@y 鑹W3|奦粻)qF驡蹫q嚓薷羆) +$l;G臠噯 r=ュH 疴f嵨港 瀴!#y照蒗&6j罏"%x +J棣爆<梸xaA:堫(\F石酦竤xWm堛 p"頵汑轛氰C 穖r寤< #`痕P(QXJ7.6g48N<伤-x鸳: PC H5揱擅 @`痠$ 攪ˋ@垶}滛BY嗽 l $,4啡蜺ㄇ3餉- EM瀜4`剝d攪 韝菒r=翨):N袪%H钺崤k妬"1&h$<4\P( P矂rT h><︱3汩 - pk(-榨虤D嵙罇绘鯤x怽fc贎'欞$-P狉<醏耹佲皛+{關9`傞zc閚" 牗\砃a唷撞踊`衢鎛詢閙Tg袠 裇鋃嗷﨔0畻訔`犌旴サ:q xaぢ鶦N啾o貍迺郖)錅>⒐7X3龑;詊驭癅l慬`襀虩!匈&@6gj#v鳒=]綨篊+)鎞t匋F猲瑬祌们c攢=}2糭Z盲鑚鱬巺Q寋cSyH"N-*@'Hv嶌伯G Q繬慝@%`|戯褘`/y/) 簯5,>[,q$b77^ 棺G樚K謕o4F辑响+p<5;俴 t煭I\蓣漌Z +?* [指i/l0鉁()╛\:7 +艂@H?l儗?z皐B?恲哬貋p:楌-罀83NH"? .笧p嫚@KP糑$WaXh70矫 槖淬@;z#`. "[ P佔&V.糶P 勬-7hB 4爂貋 +湝K.靟 N@婚I ):X-輞l +X儼 & =梙#39;橞褓賏 +#(G脗徦&桑伆珚(XDF魷 @ {l*x3楣蠨(E愧縬p< 耑吽髽{iC +h:ul( <鑹Hz`?7b奼4轾Hfy2癉-L瀍祭5韫 擨1則gY[(H皦+ +J`M沢皣y +赴叒禞#J歿w@ 㱮刾?:2(+&:枋淂ASL+椥0湌m[税A脖,np[蠬;(垁F馑甽弹1瞆旸F逘ズr塴d摊鶈醭 a`嗠Q娳K羰!妀djD脇6儔TM9刌[.8侫x 癑觊罬+!迭`#M缮迾;c n癒淠枈:◢兑澝N卆P4矽B俽◢%h 貧)N X傯L锌/:洓瀥< 悲0(?0袳鞵濦挶R5%|D傐饠鄉 癅枲牯PE杘谢厚贅 皬 (鸼#X(梽佪擰$艍>'`覞+嬺 (囦 HR.祲串 砰!僫g5鑶8錻r* (.澯 +绖,禆{牎g0 +t箑-U &h9MRfH絪孛悜艙 $啊i(P橠ITNP0`9 >哺V蜓r悹+暭| H鞹潇剦府$F1瀧ㄆX垔 筆Cl 藙U$E墩2?槺B.0|-越鑶#8"Jb鮌箒(椅櫦)躕3U3柿(P皆蘓n4 4 咈"俽HQ煞借衫倃TW]h<[- 0躟施#x*VMO譱搜殮擑唖(暔+ep嘈塃NT樄防G#w@H晲劌 汹轮揺L+蟪餖9 *6訷(N垈幸漊袨518蠭腺窵6呵 o0狡x炸耾85穩_6悆P0+, @X) F/l#=埕旒3競`紤僝h伴R("|m塅凴9\x91 +x虥剚A癥 腬F,(乚輂PV!互6h搔)萐*鵂30貪"B敇򜖹楄p冡eL!@掭佗$薿鴱尜 哱執^g靷;爀y箓橙葷 `_8挷丳饏]2芮?8肨'吳*`丢k&MJ^鎚菸賋蒥Z ~`帝辸瘰艴^ 辧|楗婖j@Tf<;螮競壝 砞堎X辮刿ㄐn=d&=>壇a%帆^瀂(栳#51樥w)1h=R伞瓧吴"珎\聯h紂-;矚抹XJ綻塭ck,"耰% 垬d5貫(呫鬰朴\`挗-/X鉱1-M +XQ I>騍5B瓡瀆藎tae墣免Q畎/;Va^0P#F; 鼚豙,(穁6:t;沒琮!轙嚓7"i>"uWB㧏R"#PJ_匽hY$‥> Hj8玜掤卆8芘畲柸傾貈峧h..Sx惴 8`梕^瀞5Tk+8; +f朄勵1T1唊脛^L+_o-%s樿c6.:氎S*P p霼"鴎o櫶n侕舗剃钫捕殹> hm*嗉0`l*褥寖嗡嶏犼;/;倣(#`j/L 搭&!鄟杁<#~饒z=S@呬灋C`嗛顔聒 様以爒q纅哣x#*e莕# 繇嚯铖 +膬["Wn'籔 X蜉柶7)適f 医鄦RP+l!衻呹 莕侟B Z6,>!6ws割5l 9os:焌袞@= $/t条h墒;tD?踘雄絧G=t50%秚噉価N鑬u鋀G鯄} 序嬱WT莊7ìV?鮓W8臈Z莊`覾]囌g犛鮜^岸屰鬰梔0膶Jo鰸]0恦j8h雔燊m髯gx杉婿p梔7X<`s?w6緡攣/pw鱨跼z縻Hc索,唨Eplx/x?x凮x卂x唎x ! , H盃羶*\劝∶#J淗雹艐3j苋保菑 CI菠蓳(S猏刹ニ0c蕼I肠蜎8s贶沙烜 +J川眩H*]蚀┯滼氮斋X砵菔但庄`脢K冬俪h营]硕穚闶滽樊莼x箨菟矾呖 L赴崦>K谈便菒#K濴共逅3k尢钩缦燙M捍橛⊿玘秃惦装c藶M欢碹竤朕突凤呃 N几胥葥+_渭贵缧烴胶蹼爻k呶交黠嗝O炯栌玙暇禁黟闼烵烤脒峡(鄝=3饘蚵(x莱:唷+K茸甃3R郟8p(艊薜馨;3&ヾ&Rl窭P颌啅>馪I$A7*=sCK,爦抎趫 0N>犳鱌筍<<#?<(馏舌P&櫁 '灗ゝs屻&N-!纾s騚D\幠O翔犮烰^襠唵 +息6}#え>髰Q镚Kc,,篱抏j哃@*MB磹臧A0硟零3$#姯& (部呼崳胿D莚鬔 蠘#3 m捜Z瓪甦  <美r3$扡澳7⿸罫 !Q娶詈$S氾鯬蛿虿俩Kむ佄`)tj@鶰杕^t!弾'肐B﹌弹t B$顄-KD +XL kDq 4 袙 腨77@:%8@#7nj/p祄/B鋅豃 +劶3滧1` 菁m呌廧I頓V慃+ P2B掚HL1Vq$何搐綈? +;访屛岟禪 T郚穜俙裥礍渟尥凷@ 囵 +FP鄖帚$J&Bd枔9F T蠦薨@僐.厑8蟈# -#ri怶 @ 喋'惭0X糎FF昋Q藸T踉↗侥川8偆<[D$褝Rh!f皢0m绒@颁格晇宀h &^xF謊=!*Hc2霵3鋌#怉5 傲vU瀫╓嵀惙袪摧└'ZN淅猺TU嶳r聁C9]虝s4A猭 jUS`攚Z基 !轝0 4[藞I谷4zH@> 娎6EX>"坄 +>TM. +╘,OQJX孄=婺V辣 103幬9N 串錈7健劽厡蛤hi uN`續醏B萯FS揉aP%0休鉳;=籜'(徇吶惍> 9懬硸b鮮破続凢溧c\譸执p7@踤袪凡汪)璑' 5%@ 爗篏%Psw&pp0|0qW/7I鄔 5yp97%}p槯?咝62PF2?ok0Syn#Z悆Sj錌U p62 [ /pv`v  z踦瞒E闸>5 q  ,2 ="竓#煺 Z 銗鍫*o蟨饱jk癬伟C?Q爚7鷵痃P傛T怶X@0叢-YgC闲=@ 踐& |"303=^1-栋啯u9@倁(RTPkmD,嬸偡Q垏圎匜6C9@\EoLy? +v剝N0 喰? +Y v 皗呃 盚 Y 虑&P Iq*pQ@屳(nJ尠 琏 oDc豿VnUCDw擶 +G郆湓19JP-鮊+ +3f`媉0* Y(h0w"P% 蕋(P"蘌^▊勦`斎j萖=唭槰j媝)D拹)@甙麉V9e4'@怤稳Jhty4<瓺P)@Dp⿶ T0 #P乩*h@ 0wl獝 鑀e8痵咝(I狍(q癛md=cyx(n敹餿K缿遬 !甙u剨▔凚8p洙<%eN4 'H\tT}捺> 绦\`@ 牂h鱲l1詹& 兀@裒($儼00Kd櫁L)Z涋O穝m檔q鱻湱鋝靺V&Rn□栧幠ey.4蝋W炎 0赤 `G)0"`_pk悁悋2 謍遬dp6(傽2爒xT犖pR +啈Q@@還顳(闎zj4燿'Rk爚岚碋摫鮼迶h)Z堇爛 +麡d  ,pho鯝Z +( 8y7鯳)@y=&O(}僚/1D錚馉格 E鐱0@鈫Ry9桑R 揋7.pe湏爤閐 @(爴甜 欼* 悁" ]0 :38xcU,*Hp飃NH袦嬛VV7t猤dWV ?栨.朲[⒅1c*s蹳N矹鮢.擿壻L饄硶N0v`i皬 +夝eGM L^+{g鬷P綈uH湊uH闎L@"篽"萯!萾鳵Ks诖箨R豤座R榩硯⒄V'L牣N?C硺喦 禤9盰;皈vGv籊鯼窀g[Sc鯬-捫乻.]*OJT餿7Z9繼6V`/ 呚涱GΚg5G +p敨悒皽c堾C摐@x湊 I`{*Pp0簕謵烶|O( +冎唘p4禘 3洡墇{x嵃憼堓燾%3K获 飬8冘eFx鉴3C儛z鴇#佛R~L +T呃^簵^p8w`k *嶓铰粉 蘌l8,珸みVn胜鍮@恏嚒色zx┰ 倇j勑#鵕ZN.G &s3膐 +ky踷 }yF;R乳 s*15 P澳,p7f3爂觶猺VRA` 兎z4穞湴x袅 s糺L!|9鹇Q丐.機s娲鍾跗辮箊-誳y协x4葱磈t螑 +  绨.js0j `0?{3頟独 Mu繡0狌1t籬唽8鰚 3蘻ólDk佳n拓9R,e牣58J.熁~kx?p)癤翨'錚Vpv@@l石职咢A +} BR u9PwT甦/ 嘠兆coI崞h閂脺褔 e埃痑 n漑偑智/魷溊蛓賻o G顆`j?vJ` 蠍Up蟐@陦l \ 粢-囸嫲uU2H苹,偋腃塘敇糥p}wk嘗蟹嵺2$埣!Q,nG纃茾8濤CXq衃n絏樚柊D蟦ガ凋悶鳯魀榾*) vH倡擔争爣鱆點帜尲4[鰖x輴n 认6一悋傓熊缨扏j犽_伃M5;刐b镘aiv髮 0% 頟鯼 iDZ`J陛cu8朖*nk溄=vR糊.7杅尦sザ溯Jl剴S溶箂`Jy04`2 +FD6嚢npOWF稅騏mp}牂 '颁孝溳訯Lc `Ε缼轅垡L黏3族A8纁v坢圂戢/Lol乐g 釜v籷I叭st@1T嘁iny犘恺 還4P冦| +⺁]粑遴煤忯悶P幭t6@@袵栤航,粶pX~驿ф更谅锑*ZV&'詪σ6gUQ偑V"锅#@q轕 +P赶qUPh3滎坧N} fu浫尃kpG讠$(@xn獩魹驾5P讜ЕIZ榞4厚 I<蛵萮拽Ns,u40C $3d刴C伍 珗瞀E`@劍00甪.R&4関u漱5疰"U迳6亚n▏_猈缎E5謠洁y8駯?醦藖1鑟彃@p*捓殚黹/~闢J歔Z4CT鏡y~}=累!各p繺扩Svh篒Z~蘦揋'm4LP腹伃 :G餯g.腇 叓惵韩鰷謇 沒.p砦 #瀛穂4悀轞T谢i-RH +?鞷UV,胲k烇B壧霺vD10Pi=Z@P!肰U_;T事"nFn&泃騃'煩n pv娐フ(剜进D(檨@?磺:4癎 肰绷L刪#@燷蹾-案hf磆 +hp.j剨佗妰夾濋唺@醾p(G礰(ˉC=x唺鮽 +噦渹B(R蟥缊g8鸂評W^{祳噊!h$6帬" 秙拠 &(s 瀥 倯@PPE\ |酈攏01!,`x + 腋8鉅軠醖扫麬!N./猳躳 *SM半7H= 抉gwj-栱1 +瀐醔鄢罻榗桖 y茒$嚼秸▓+@W=櫪痥薸闾mk I\a戛ξ蓬, *陘 % 脆姊(x f*7 + |U讪H裨4謤g犙gF$ Z1団藵隆僥S樌9l憑f蠧}狢刞瑼LF+襖垈-餹r1*@d2鶕D[荐$%鷥!訳軺 4▋8<+ T觵秅挢`蠵Ш + %ot* 墬& 8蓛壖⒙豰坄X!*Z0p齗e$ +0 +B3p杗t@q`a鰨"'D6VA9翞`BQ8G蚀;m#0鷌B饎穽H1t@ 4!"&t HA74p弬m@P岪 ひ +类哗 +騟5宱*擗C墁鈴鈙8P\`EQ丠 荔憂燿MYB盍R焥`(h9K``A !堺&颈玱槀a +(`吕磥τ$# *勦 (犣犂0緢繻陘仐曄エ旤A +0j撌=hq8\燘d*遆劸B嘶G !(洫#4'H@枓p陰垆&wZ鄰g岹XfH-釔堨3iNkC*塯ā 癇4 樞 +哉? ` + 周0eD鯌鈢f-镠8h慖哓繭\@馆v)濹窏H馨Yn喞题⑤M徧帘4癇 鈘楖J|h@傲K蚯 +3侤 +D鈂濴爿 +摪=.<媂g墆袀* 0墘l譬j rq3%湩5X燤|脷K h,&P&(氘DH猂酸哹a'咓fD9 p昽T*3r@知8 aB}羷3"膗滆3剱箛x#2lqij譯HpcT皢eID獋& ]黚烖侑鄟攋 +(QN粮Yi║^鶒瘠$E#牜咰T!9t", @翧 (')0!d怈駟椄V恧圿h繽罼.癅l"g D)栶 +圊<迗BX;b驤"聐袄Zh7砃怉*ˋ7*|-HMxU歍S39&$┘醙*0霬獳逛r泏%p"勍  4朩﹁ l鲃L橋訮繳╄疲!芗'"n愂36p堟 █g軄=Z訬 +j3:顳4窢/k s搭"駝)?A Ty苃$+坸訵y7R滥 幈>又=u*〾Tb赜&衢9=滠}艑oH@禓"躊k釻LQS 孉禠m^/ @co浺尼<8悤呩[醓倭>皚 倓0饍3澃琍 @鋘?翑蹗傂R}鄡(3"p嗥 ̄X= 癎 Auz鞕 佂 '鶹躠謔:!>$`i). +Z纔>%鳤豐&b@袳\1!戝 昏{肥袿1 +奞‰&%湥榾$虽ut讻&焋H猔 +|茞?C僠p塓波< +H萗l0鄊厩踞蠍3BJq剫tc 3H埪:差4n~槵>B饓审*ㄐ锆{A蟱YD 矌 +|D5{蔑禇HT嚝機 XA愷+Db O恢 "`惊墏茪Sx梉!XT=m眾C鄼僅+[ 爞%H?S9± +墻K鴨/;纠0 溪镵<蕗PJ"H +f(嘋皡o▊Cx6∝ +B俱x +* 鉎煓剄垽r鄤'. +c晆$攰I8単8;芇;B猉> )諧 p595ZQ.∪乪眢=87﨣*`pb(阜hr门冸鸆晙Q8!* X P5褅骡5 隂 ,GdX盔澫x0%)8D创TI饻趽 +巌舏儬9DYB +鹈^羵7P僝L欶|3湚&P鵤胏\ 労摞 鞂03'富峚 +(∮艷粐Hg(50漅`HG姅 悐兪鴥+凕 &3 抚 枞1椾≧m1嘫▉#0葍桙缩拻n#H獥瑩P偳$KFT穩z俉悐*郒z1<MmI俶 iSM'9侳簛uC坝俿X烃阃臭╄P!h3螑I轖aH倲搬[の +偺!( 把瀰2D覾媱俇D僑R竷*葍C垜3堀Zfh爛(萖:1@G`t倭-'`獔0P屍蠵(.累,磔迿A53.L:[(鬗+袊,皞窊m HS( 俌龈苐鋉欩唎2裮簢.^纮 ,A仟鑲4pT2襣鄠pt 癨謌蕫█8猝i.錆 卦M艸(傝匯"-摹仠韆;=f +3pN鄤2v".倸HU8拭祳FC"(%(硅#鯾m<參e宧X(pE唫K*棤皪诺 `畩{dLF<鍸(5偯 .d渼Y c乚(苩 .(5]p)xe8 ;=Qwh疂p丄纮@F? -v翆) y "癴汤c=秺桎D缹兊鹰4姞0eh轙1h^ 齠`Z酓H╫1p蓣 %(g@叴f寗趨念奛恜禨74埜呟Uo儗榨荄[hK邌狸点o唹餠c]Pp袴悋bdW@('楡& 灼-[癵鱒+璎5ea憵Hnqm瑐0炩>逳;IP~队7xr!臝癱鬓p忙bx&榩;u&绺+椕q&-嶖韗秬皥1閂滘g4k茽恇薛_鑳盃sm祜馏<嚶^揰悁褑蟏H省謤R须筌E*(p .c027i7xn!勇N桟=豴H靿瀈骣b Pu瀫璙胦序麂U&-*C熩g杈爛丼辵滯鮜 橌傐v茷_fG*鄐y_ 8頹_藮-h躭O?P(鋘zxiE魈~奐w9魢啂皁鞼.乑玎oh葷鄤e趋豄荀舐巊Xv鹚厐VA JCx($儮嶊~w轜tog躘婳V侖&競c .da瑨嘊Sz a珉辀焁!拕轾欑2M(d侚唭;>&:z钧╒(80l"ゾ嘔=┻籓o8x梬搤F汚禢P4 :▽恭踘葋葕焮臬c8H?(鹋╫遇鈕劀{胈竜8僼h丁鰅撮犹徑.皟>P砾>怾 y/鍼搎 +N?齾螟9O)6镃yI9側穧|(三庵j1Nd釭?/m`伙v瀾G?槄t▇ 卐&Wh7桡貈忸':L俫a亸鱞}KF8Фo傳 +鼉 +2l桊!膱'R琱疋胓89茞"G,i$蕯*I騛,傽 8彬&螠凯菱瑳:-j(R嬏P翑9餖IC7g).<+匕H橝h*搥1牟5zHK7,Lz淡k.迏b H圛0憟+<.I婮#,n禅笔-8U@G L7Gt盲[f淕.蚉( Na偙橥6贰E燔后觊②)"^卷~黰3g\3o^礒蔳E缺嗉0#y鎊镱=&桗. yr;稔紿'~鴅焆()~ +盒4踿S輷僲;48a鱴1轅ha匰侫#4!夀}銋Sh]袵塇崖劥8cs哞"f幮Xl2鈳A歷俖 +f y7菕Q巉乢瑝`.巋"eJ-丄W\奩2 Q 橉搶IR/50&漧Prど,郪显爡娬灳 俴,`Δ鵶G槕Z歍墭y 岊brp)DB*狥e篿!G`潳:+璆櫌倷淮徶++u捚xt|*,惩妕 +J婈,顿JdJ5&屃( 嗸Lペ"槅 E爕&@弘陒(詟q@{0砞;P壺0瞌&笁0糯 + 濴X 豟I! +JP"`篓"籰)碕纻/隲g:uF;戒2%X嬟板裃ygeg"',,5-R碓"枋[w}鰟9SW頡/詇>*罞p07啁曺硽dmv邊_ %fEn蠄倍嫃!遣恔緵-M 鵰>zl摨鯣§r罍せ⺋*豈G6場:類慳囕  &%;暼9縷^h2?}[咴}|蛟k蟅2裶E蹕兆澅)惬? 椯e嬒)R傘螂菣 +笒斧0乫ɡ揇*H9纻{-皞1N亓G.馦 +8p喰$蠄C驹/x 澬C隯抺R*r–<:摠%2眽N|"()R眾V"敞-r眿^"(1挶宖<#莹5脖峮|#(9冶巚#蟥=虮弤# )華矏<$"箼! , H盃羶*\劝∶#J淗雹艐3j苋保菑 CI菠蓳(S猏刹ニ0c蕼I肠蜎8s贶沙烜 +J川眩H*]蚀┯滼氮斋X砵菔但庄`脢K冬俪h营]硕穚闶滽阻砱u箨e墂縺 L赴崦+^谈便菒#K濴共逅3k尢钩缦燙M捍橛⊿玘秃惦装c藶M欢碹竤朕突凤呃 N几胥葥+_渭贵缧烴胶蹼爻k呶交黠嗝O炯栌玙暇禁黟 咪儈!(f腯 >B/碻鄟攣 f (F蹸a匲d(鈃$嵛噾A`剤,Hd刎7U|余!瓌揋#8# B 跇0xh盇纮3D,憽墼班e +酦 桫]B0菮愇Dr5丄峘1亷 栻 +4碄3$翆;慏U$D捹枢)8肏p侲8)01H溤倨犳ANG銨媎$撩=#~覀櫾}@ 9l 浕bAEt肕 +阮晏 `兴剫QE#f琁鞇_t@芣 D陯4&X +D酈8繼壛 ~,韆橡繡 "xDIH鲕祫3)t肈李 +\P-G|踻p=cD .!$,佺]H0D80"掲鲘-:迌E +X0 WYtLt油昑中牙哠Jk蠤 +b蟃唁]鞔侅豖H吕讼 1)笎B;gD埾8)4$涋t燛AvC伭0榌8'9悆=Wて0 `q{鑰{ A7|CG0 鬊w繏皾7蒈BoPyoP憙慊2袮覓n繦愞0链727攇睦 # 8郟器邨!~ 纁:T0卯成H),3冦溶秋灐i V .`.,踀 +:@隄燖@0@.q 槓%, 'U宍O笹荀5玞蠇X#n羷ET`R兲茘FLlu鑐m灹镴X緤X +粥俿堂獯e Z`漏(矅H8讀l[貍!F元a膴膐W(* ┩( 2TB嫈3巰舩庛鋈韆建沴Z@裲Z鑖暮mZ纰BW儊摾騿S" 餒9t ~痡勀鷍糮(嚚@ +袕.稑 @/,\劖皱簯 +,@伮 聫E臔亝@痡 珨覛呏a%4qQ#  >J  IHRX}.t粦T枓8卸P4"鑵T_ f堀{肠隸pV`M_D-k7y 聥硒礃d齢5*`$n︸=婣這G5棹 ダ8vA*芈鼈噶#蠦墸)躁紌概r死r:#╇-幚鎢Q癇 q垈 奼慳98㈣I稰3D姷淰岂籟)6"薎喟 衊#^M氫`6錊岂渷鸥 u-椗(D睿A樞燱鄝蠿4 +鱖K%銇'逪B2YG4,P鸬2#P +t臤↙鍟(凚螂I纶剰U 廲埪s繗mgp=髤u7藁X翿JsF渞鄠l{儴 W$內绤腲 軆ぺ& H]冎o 洈购乸竁Ii~:藂X崊j4皊y1懵蛪劆雂>+   R8q胝纏珽N!ca逹劳匜垃歠揂Q鋶{瞐啢湣B牂3B悙迋詧>W摽,o谓:'鏁W +$|S:謘a5摒A:8恫O |:&楀艈現櫨舶rP 幹 婀s髃Z岳:K宇g糩拰燽黗1譨嘀.泿5x摼鐁佮騹A"+7疐削碘6yDm-ac"O}針@ +\P/Y奔褔y 輤lK paJ揳H,尷s"H#,評y朿筒)@,V癄 +0u苶$P鰙3PG3Q"5p c衤 ZF:嶐0p Dpp +籅k鄗lB譚R=荶k@V蝨%峱/T鈠Xхx皝彴 U\*凚:U 0-怸枾7煻 晘.窯LU萖溞~嬪@嬂_q7鋚v74 RV劤1YFT/4k粋m杪Z nZ & 叙 R纝繮6鍈i1Q淝6膠-X囘史Gt3葁牝SaU堺AQ,莅跗& Z衤v35{辚C譾^_ + + 癙啺$epBe k4 e( n碨0m &烩Q矉碒囩冾'a轥< n▋qyP恄泚#4wZ咂U嬽Uε&錬sV<犯QK儘A" 估:@R0%T$筶R燘&蓭'雸偱鮨.慭z僥悜荟4&,T鋸efr堢钒嘆QD窺|鰉Lp}xJ}梞鸉]卧Qx葔Q悋V v50Y?pB0't &Y0iB  纮0頧`s x0I;0燪磋 g7T(N4)9猄-違曾敋`枅閞鍬[1E鐁噹Hk %NQW奿T瀫Bn檼(4p7"話R`唨+0氦咝 贂+銎 @i讟罛 媀09z屽礨f +f怋/ss\(桟蝮o啦嘕养P倾 籅70p9靾0 f濦S棤 +3`骮>靝 0G|eK?* H郟Z膐轞LQ饎t葔爥逨峕4減麗!鳡% T6杢勥餦Uo 嘫4%gqlP璨# Om$櫊 +糮2pl03馥ie050 0R0驪O=衛i(Q"牥q醺漊觫0妔80Z@﹙字.蓴+Dp'jpw宎$ 蓋羷 〦+楐+_钟s釥R堢/ +汨 悁# ]0 >eM > 豟c +玃3P :C&域喩7孖魪5pV樽 淧K鱃鸉7虪攚)v'K麙;/S筼煩i囂&1溞E蓅鍬蘎.L04 ? `諤汤  +耟0# _" l` 聽S=@#!靖:DPz垾r/J4@ ]拁E3Mze纊T皺4Dp6iCB"-发XTa邡@Aㄅ嗌餽ex壽J儉廦H鯋!)掟 憷 ~b95 噰焝 塃群Ex訰*夊5鹦0觍鹥 + P寂%胺W`0+ +鄏%L鄔;t)@~@.P箙S +Q繞 係xc骔 +\`{膛0Dj']儰钡1X?荴0繥p.L髐a4RWd燭2 [  '`v 奝,貝 c嗉 l 叻O暥$ T8硣5@孠P鑹x""v銳儬X? 讬膽b琡bBQKxQZ爬U蒟`繟9圲眵T0戄纙鑂 +絧缋=鴇T- 跕ng蟺 擛 _`技30⒎T $*h儃檙 錆U劯幩pb炦8Q@櫟茲芫茬 F犏 璙^蒺'+B^3获W泙Q:郟s2鍽綽] 蘘 弪 +s =0螾f桶0=@ =E胺![ 靲唟猽 d敃6m蓍1烊砐8擊ZZ ,烁(蠶p術w0{9 恄蠣詤\1岸夃 噔;`6g 悹k螾"溮/扤%鏖袝0栻 F珄婸-=0x{剅 5F;穑▋半?宲亹yuPy`4G8T@)橡[.U芀*棱 犃`  #0S猔`馉习Up?)L飁铹$>,@澀卻v!WL燓@戋;{eグ +镳G"靠 郁d豕G靃筞狆N n^Z> 0攲郊dL杯蠼皶躊l @"t蓝 t歋  ;`鴳!K._閻|A\%?.謆焻(鯠月廪硹b 碽PAG5v侓む耷悮T帉1 磯=螥+ 姛嗬︷ + 8@ +銲 膶=L"赀艐勷h裉闱%绋` g)U甦檼'N蜳禩墺[ 4湦樘d夷R*揽ox膣3J窞屩p欼摢)酹鹧=,)磈亐鸯Y磇债e壑韀竡鍤 I薋:@ド瓈沷T傅於嵝NR夡 晓=玸栀7蓅1>雲A儉*红4h惏;6l @p&醟$Tqb廞康 S纂35 Q袯卝譃4K1!I+m鈂3合軘馑撁宪ha艩蜣tH.揝,S碼f澉曑`臆凨坓k.疸簲`膏Bc +h-毫Ha"L{瀜"atQA吗0!豝G0獃@*i藒籫N( loBg8塪岺榩A脼Γ亞褻皜8z緳L5篬 顮@K暫┽6悉"3S"聻幖氞繤答GD@t術@y唩圮咑8汆0檒F骸酁o苄 嶢1驀噂2癱@9 +(l窧筑╭V 鎳垽%(@ - +jIb蟭 +H朅,)  x殣  梆"@巿H@B r壥亰:y+8悡7B调^|"Q(鴃欎\燗瞦陈倝z┿K7$逳:j @$q罊bTX]涻ck迄(8"`hn賐{r!&iル啈08o瓹?k) 緛@煡坂仙$:HyF(歕疣 l夆$ +揤J! +,(E:_菜斃=綃鋬琲b$1拎4蠱 NV0襋A )g +TH:鵻q&嘊)袏+绤榌n﹢(<髷lra(苞謾(盢:駡枬X=3i蓞z憱靶罒 %寠(xo~阕欀33鹹5#銢触 `琝^#稠S 咵0"鋒;';LYBp+盄梍k槈" N╜$5G9 俿罀R7赇瑏`B +學孝槖tJQ噵IUG儢4 桜p%u9`S7憚]伤崰B垻wC 濴榾 +0%薒%蚀扲T`33憥 p灏犂丏腥4! 栏ZY` ,鬅J奣4 迵 Q奷EI濧 P85a? p/q皬qC蹨憘8伬t +喟B"'a&X尭C>6鎖@9$%肼胠U`w=h穲鴦畁9炷!纏侰饊聽E$" a8纴0券g(9嗜歈A毼)3蛐8鉢媝C笐8o 毚\@娚葬x$3髰7do員腨Nx爟夐髫@铃@膾&嶝aW 愬aJ斔0攀 8睋 vCL凾 橡囙卩H4a8AK90d4颍l窭bul< \ +(I碳7軆淟J矦 鷵I +垁憑窤((棕褭p"2黾U胎 Q +~) 45?衻) C堡@錵賯:建&M`怮孌蕏菪 oK@\1 4魳辄饑"榾sm 婓槣涩(~槄Aㄑ􅾉H男太Q荖&<-衖0!S逘Z脎郎杽 [ 修1a騎9!厖RP韐y锏&8驹鋸Y麬僃h晔9鼏#煦1棧 (F櫷\ 5&(#`@ 鯏骏 鼝瞎 F:饋u碰浹韐≯B uyF9]F嗯 +U瀀n裫Q荹3叐'DPi@撪昱Y瀬F溽獭0本\飹锑#CI3胐p靣ph!刋姖黋辬蠂|<l 9w檱 +H+ B@迺-0偨螪Pa翑锣YQl论d肼nR+vh滭8$5c88P&ha匤A6剂??- @鯇C"忈/P +[|`sF搒脦^符䎬'!-蕆廻堡 沭钱緍 =X嗅 8.{E h荓゛un@ 蟻A油驅{(鵖鼣㏄|儌.)藁<0C'l y萻&:嵗@荛o  +K蕂簟臾f屚D 偘?璎貱爛*@藓璄貈瑟E>J!79你備褤$a銚狼F鴻D7窔 缡>@樱?]K>^{1P  匽x=;t:衻釦6;緡!A塀[剺*饑:9k獉机梡)<7X5皼:壌(樍蒨静閭/彉鹲,X@@%@4 @悆N槍{*XC硥d塕~妿甜 +y晝臅Kh 7r蹌+tn(.5S5鷖5窩搄嘏]|(附"掣嘕 匫挪逊栲J*{  mQ茣3K梞 `A,S5=鑳 +<< 鴪),+%嗯P雷@(7腇桉 籷/溧(憹#侰%~5291 捐 x%泟Nl[硊$3 俋圙y)P﹦l磪h湊0A孶 {0!Jy翖(w啜旇嘂:> (吜 縟\睶q貑'幵5"鑴彔C鳦^|Gh8豍10災!IP 誓M鋾(K:<7繞姗薖儉Sp Y痰E`M(<寗xl0涩lSH兾6髓>恟搿p[饸忚9g爞}刺礪 N@C8劎85T<l鄤3+衻`hD'@伷銭%竷_S9-幽桜H蜁1!.l70泉L*借摹H&LC俍縋{.p鍰犆劤"衻b俌绬@斍<吷9损$沷X!銊爰嚬L喌,编劊(PZ觽( 楛@(埻x0Pz}愊劤?+p&P卿E?袆8-R:(偸葱| Y壷彼TD╜退癄蜒zY茾8?*槃影_@延sD嘞]T俙KXNP刄郔$绞8肖pX铘鈩%(S鑩欴QP蹈@H艅;\珻(厃銸▊9+:禇:袀驖wb:埿瘁螊T|y貍瞡凞f餝y纻芗:I軪?皙N兾-bUAyXZB覕H?燜<し訳{壣n鯏Bf怰 8+–I袀'@Tf8 鑳蜫Se 擛'<R胤啙摜,W劼鈩E脮fx喴矧k=6X噿擥z40蘎9Ty恵繮U计暮練[鴯r樞J堌e +MO)R7嗟7圝蕭5Xl@乶=婥~E!F +é僘縢@ q&翞漌琀噶芐'0喙+X @椵%彳x'0譛儱B鑁4:(,> +鸴嫉恱 4ed唎2`[7/]J8,]膛D'圙拜爻Pp_ 屑潒{X僷(A岉(h槒 娮蛵 ZX嶸砒C@赓キu鴧_饍XA丱刂'皠砲麁=日諘 7 尮趷Q/忴啺8杇m^範钄翴`7剆 纮V砞 "`&踍<T0 儨M!X咞鮚竴鑵 GN胴c!Y`f嘤17'盂5=j_1#+??@yD丏 爲D(坿`珰╗廆%栕SXI"p伃戃5h莬侻@+L羞,H悩,|"~a紏釮55抃TX9c8註D憬=硙 E-鰪r鼑壈諐c$ +x 6X乽竀I黔 煁0賕h銊8僓豤>N Z栃瞤w8轄艟Iτ[剱珻伣`4I%h5T3GF砞 郞V8'J$(Y幬"Rf+貈m錏 7Shd-]匦^N媨妇pⅢ +輌.P5婭裛蔚WuL%岃俼僥杻4蝔Z曡锰0偟d`(Uv%竵@Y訣T岃@缱鴥1醷^ E~ d眬乨Mh~h諄&癛A&X$9Tx 4 K奮 莏矎鑄JJ闉紖埵68嘳d68瀛t}!E昳锤r @{ 漢耾垊V癷捾諬 幘簽.豝0P槱`3戛F:埊 =杚LkH-鑚+袊荕e桕\sp 崊U䥽I朁,灂,䥽$繼~)Ul 靽朽Jb@0C逑'輅 x|蚮*#嫐),汌6鴧b嶨D桁rX}缊 +Xf` 栱嫧 8諶7凯5'緁 .x紻`貒鏝婥燜-吀o竛*襐去個鮦煐'鑻槃敽GXa跷"C菘荹#鄊\%x俖 ~v2A11 蘀X 咏嚩旉 W256`侶8g-U俆垁瞬1鑲O毌hK{Pc po鰬~I f-迱P颪焆銴 l謖y麊(f.癋T+L豲援qB 5(^杛% +竢,/騉4磤脝 S洚僊>>鵛570&鄊R>鋌嫦Tv駉8 7gF85o這燆U+s-".鑢硛q峪+鄟﹖,I昔Rk鮰羴p3犴> S_1鹣.敻WY彿(p凴p骮F"'哻鲺/ +F&'饆(c/捲N鲨;4鄠g鱰鑊修兇8槱鎸給鸫wQ觿yP仼皞 w_p鑰喏亇靷+X~fO+趋RB]PH +(t嫨u鐾UA8虺`阶譧痲脤坺 ,僽))@v覵q菖~p癯鑴;u輯w僀P茒t'%覷饌YG| 熛冹顎(X)(M浗1Pw8竾F爜銕zR陝805oR盶.(皡`()0妊0098 7 蠯簢(0皊貈0 6枃3f课_@y;:5拔薟个剤8线e&物|PX)aI%餈05堯亠+d 0D儠鉃劦襊[E"8f5柁缦!=竰乩G蟱啒漞 圵M.Li(BPつ +2l桊!膱'R琱"茀*0狆,#葠"G,i騞F(h┼"l恲謤藴 )'夥>聽`',牯┯7U4磤j*脂Z+濰3Q虣B炲, 9"多牋揃 +80邈旨z洅簱镛纻K梡d0B謑KwU匵EE ++癝WL嵝>x4暝)b !'<鳣,A墷O剚T&L`Zm8蜾虱~撠vRP╦+[tDK[JC鄝呌饲/o供4蟔Fw2L_]0N +余優篾黧餆u択9` ΩYC 繠euF"7!Z8Q锕D 1eQp朽)"y鰘hc悜\埱"=(?'糷1h("W蘷*'爸廞J9}x昐b 賭6燞昬歽鍱晸 +}dP[""@E鳒`&h旯'熯弱晄笛Q E !焿*:鲥!鳵泞昛Z)伜d罖 瞏丧zfW* v1&9*蜩bK鑲榊眣1晋跓?笧jA 4D,崇A:le1啿蚙{璹}ɡR霋d6b;.箙U—莓)EpP.捡j 疘p魯泩蝥I蠝袙韩& 0u輣垡,L0C め弄躹(泑%"虘-龄钌1c< 貭d喬=毵@1功ハIN,寔eIV膷J[嵀)$A樬R堬^=6硓亍.1$蚯 " +痙=j恑谺|o97摺F`-6惮k7鈻蚶 +屡%>y@x)鍧膣0醗古灈n <3啛敔虙毋e翑0旃麒.倹 芎 痓KK躔蒣3哕D*?@<抍@=鼷U湿諌 輿_^搫_ _㳠]緜6( ?慳YS棸c8K鸠,q捗Oɡ耟]癆.惗 陞^苧墏X 9 +rp^裷I堟粤b@T涝 +E>^8∪斈%'寭 @掍B偼Lp8"甙纙`bX鄜E||笧!屃傲橊*(r!蟈WK嗋垁 兒‵咊,C貐 D0+鄁ok,"4腝胊@uG(2-'{專A0> 拞UH!噑洏臬慐靷 +`衬I%仏$b2I癉`Q&2貳> 蕀, 皝 Gl0*e&Qλ3眮貈衍2梂籐Y牥 屷o嫑1IV厄E穑R罳.A/赨棆m搮cK摵馉,尷譖X"蠕屫q Y柵月%>'i臘` P~2T藚 +蠴醿5笅塕4_ ZDp9垹陟h䴕s聳牓J(E%bdL献}鞸せ@牂芋5IfY5wQ怠︳囈j7萒 2.!冤v螶f鵆Bq閝1X3uq也诬臯黋衻c痄\咦$磼5:CY饑+从{F鱅>寐o`j9k>]H 暚鸺 +.抋鰘VJLH売蝔R +!抳z <糮 渶剞m熘d皠儷苠2饭蝳.t+蓍R泛纸.v倡蓓r坊摭.x+揆挿兼=/z荧搋卜筋}/|+啭曳决/~螳啐蚍傀/l迉! ,K H盃羶*\劝∶#J淗雹艐3j苋保菑 CI菠蓳(S猏刹ニ0c蕼I肠蜎8s贶沙烜 +J川眩H*鷮 (K滼uf*$!釿寨` +零俪oq1騇穚a>c嫦o犾駂矾呖!T癒樚Z瘈+^躳聟窩谈瞖-C秌勝∷燙胢a镛f楚膀hV措譌僷诂 B>#ぁ-hN<Э +uk?玃?BT辑k褌G珛k呶偨8薀ㄐ鈅苴1G@受銉m眢=2釤 (=7v 懩=H3U<83乚$駷跆h癇P$QE~F 媎噃i燛7Th 癛"70~ 筜|驧w昐J^刲 輤-亨魩z緍瀷碮 揅 霃Q揤;w#昏皝鎋}硤 K译50裷叚惑瀎違罬"l嚔Q艭_吋膛竀 +趢瞘鑑D3g<,pF)'@讄值埈nP羴}9螀0A&浫`嫲惂$礌 +0A厐曅2hD0舶BX貈旝旵` sF胩 蛩H晋凘 だ 髰 h((5菻1(X煑(0"~v9-`~羛峱AE躳&啂#譅F菩+钟D儫.A瞵慴y? +70崢#$X M1籕6) 璾寪E*旾笹 X蓛 Q柍#6p伂]F夌k S丒4m噛欵柺 #傽 0 鋱&悅妦鵉@7+>楧6鸵侫Z頫X犂1裏悺c 圥,y嶺 +`h镍8喗Ch嫀晞苘媂(鷵o鞝 &`4V寓懩傶丱跑c洃絗wa苹P叢t'PI%础E87 +悆^,T4Y>涾c媹@TRSo驆#&@Qf礯辮A鼆稠@~EWY9s佶 +傒爔裙轰 獨 Vb +舗pb Q *鬹傮珋Z杺ゆ鋽T鈆贃冈BW6磆搐臖7`皢l溹瓉@`-lzx槓碮行S飙IT0漉4H嚯MI;+` 儬 糀寞T0轕 Z乻牜 f3BX蘙姉#+3畵溚軆8.{[鑼 " +畊槹R"恐m8滩@煲6&昖{`嵲l橧h紮oW{#D 譐k魡躉爜d卂 f R诓f%B^P` 樳i邗鎄R)堇hP忾醗晌f輥%) 俶.胁敒r 焨@o拡p睈沑o0"c"6!鱐;闲繯Cc輀ˋ铆7桡毥宖B衑矪岪3УOR梓`挔!8諎岮q穢曝*\蔵2遆8@,Vγ髬濃 匉4蒦v砄=a濡 Q餋(燶燼硯!粥DM]愝繯q伡1,㭎壤僞斧倫Q儾檓阦鳙乩r饨航衻敚糤煁赡BA$(@j▌W芰$7 8矐P躽{['爡5袪g89&暗uy鴮儻uk4#碻鮶}囆Z噻餻?簬N圧Pa4XCM(r曁h\0蓿x框@僂糉 x鄮]蚤5顮錨Z妨  h鸸3婘謤/v#s瞞璪臨DA冏 凜洄芀軡8L抢帎 +=龣)鲽&臕徶澙0槟ba (,2趔剁'絤--'F3(1 ha0HA7b9K鄈D 1掄这D軥e! 铖{w'Qu]餔'}懩 +WB5q的 劙* +U`E穣 镧丩pwZw4J(<Gmf哠圣俷``k嬓QS懶10.@-H#?4咖qpU@M減w +(XP+韫 K炤#叻攜i搜v )a灣堗仴sXV:垏速5JGq褸Vǎ >2孭澘v{1(磨tQ蠿q Y鵯藭剙檸 镉B@3婳xK癨*謼z 鴫ba鮖9 媽怲 ~1L餭嶊ㄉ 揨藻p>+悓bq@郰遞TeFfP矚p襘/{岏 独d诗嚘琍6W浥 諛橸Pi+d$'圳A溹z遅F尪捀銫1T黻甇4%K饝艉郀j%);鎦婧_䅟賏 R琱 ~T}0p-拉 5+?阫)鮴 0# @;紁 0 #0  +結Q賾Ei剥v歀嶗1+嘆gP腬顇昘棣珸0櫚秌獉j 霵#0 +暊F 埅G皷R玞亚 牀⒙X剼 鄪Zp6 Н "D$ $惞毣箼  灎 +28a洷p]塟殙驍('餡#梬%8氠a姼A2鲓.@蚯/0K脆8肮潍箺 [=[ 9K@GTTU +&KrC鴣劼oRgdf辅m }a c鄺悁胬枷{纄P爨 欲滉嚴 o鄅Q屶埌;p匎 +X-雡t鸕飘u鄈鬱盤F>溞 纁鎼 藳{ 菮 溓=`,!F.I0捞" [豌鯎鎖橖2)]CU綥$綘[拿蘻s剄95@:4如X嬒)qX鎣ZS + #v,卿| e愇密箰${蔜齫JwoP |萄Q.r拃獡R"唙 +矏 : +叫啇1観y溣`F,焪饔@m爑.jeH:@ 犰-荢=7X渇LP?-:wh|rz嫏 滒 ;/ X攢>懊$ 尚 借夹=% @@觵] +(p丣Gp5q葈ダ w卣品跎1)闼0仑4!Bfr侾9 弨贆 =0呃袻 ]杈@落瓈)縉嚔o疔纝陰7髆 +訞型M且嬊F<嬱鱜 x輚バ8f柫X墪垾S娕 &詢岋0C挗迱個j +蠵 歼 蚂邌 +!裠#踰&Nj瓖  嚴o払湴# 鏍楌汆死鈋 殯 R訹 +奰`([謕;~D 瞼- NN濸*=nR0 e览[p ▕`纜埇會惂_犗X缿fn;鐥  鑠袚^闈 鑠 鏈*a*1Q}悫渪刞諊No鹯?p 鐧 r| ?0樲隦篱殯 e秙 皃邱鰼&愥G8-:uZ|贝tT!直.A`d88N碗> }缋竟w>灋箺 hk訩届彀I` >宍񐷳4旰/䱷tQL,侹q叭2钋燬# KN3諣 +構搤行 ? R 賽P誯渣宇Yj@Q} +蝼翇籇V汈馟幣S汈Hぎ/N 0$犫K2@鸲Ae镄;唨 #[0.U訧爀K恮{7陙#,饐#X鳮HⅥ(竜O5凊7qV愹 锠 +#@ @ 2p 苕p硕eP c 詪鬆0X瀂喑MX< _X煃P意黒. 3秭3   G +鴢_P 髇;~ Y0_噙= A噅拹瓿o峲bw麲9 覡 +灝 +震 f  +銲癋o十1艪駜蚅.3翵0&==雁G %蛱"翠q忠錕1e螌 #&TH} 儆鏞燗%Z澡Q.e诖h +鏳喔劊]_緶 犂k4,畧@@磇賵 +"K 鵎 +A@湏&=D♀嶆_2mb徭F*o怵t谪馽葢%O鴻>皟4@k裈ゑ獉1d水f澈l驰嗥 硂y0槔`g& 媼hxC闶靴O:鋑赲洤 +羌:橐◥ +4e前8%6癢)R男="# 3磋揊s & +(劇罵D 抹 地弘:麴CC屘$駙T1N信kJ0J毴b8啖竮0脺H?+"8)厜H4躟)ボ@C-礌 +K!o ED24驦3 )\烂 #灩Cf1OT颁i(·怋vQM>柛0*鋹帷詟! 榣28,洪2礗SM9% o8癣64SUuUV漽s Z!濨(眬圔zh0敐/§!/6 .钓%8灌篫貊[p谬鐩ERh 竴 堪C呉T疴("恅F砎t豎秶\俲AN(ZkIu +躧#q+而愀`酕4&亙^縥@H,6剳.A4QC 痖橿x 塈)鄶峥副A僂臧cvz7 +愧%q;9!懸忽je|貴奓b鲣_ zj!墘MRc(`峥駫蛑蹍R;F8仭(2(h狃iw(r9欄竫 T4 ;鎑請鈳驴搻(g R0F爛,sv 7Xc -(o +苵黿鋼 崞橣閯0-錎JV Y坒U2笵 +e12 }`犃鏚mr︼緧o6譙,騹Pd N0#鐹p<"8 0孈 +8`%垘 '箛(ph馽4按芇$ #@"瞁歿UPtP=柩蘡  黙潩d 尷 \@0淍 )P7C0唓U鬣R8R O^揁`(燝:饏 3 (媃宊8羷 _c#I\特A ,`4X飀槜饑窝 z 7靤傾褼8掺T酁 +葬媨d/}!滪' 鴢#爞0X@!;W殏"X3琍(`繰)E兞丼渻+6 箒椏dg;ャ2<鄠 \r,皯3"kDКF6&勩R.&( *DT疰7SP +*奥録tx鄰t(乸& █"h ?賖 .dㄔ焘)湌\:鶶:朠6Xa \鶷k4 (嘙 閽l 0 jW絡擭,8j9(ɡ隗Z庶 P;肻匋訫灓@,娅{瀚P:q坱袅 (嘫`當騂YL矽SS( +)鐐-T犚p&G x鞥 仲W寓V.D6y︸J襚淍8鍿窤眯锂d8*a` 4敢$9:02涤.&v 讝 僢美0'n}0GX6"銶oS({啕N;` +kVf劻z_y薣毾櫪 V蚷q淎匛x烆卲坓8嗫m钱\? 莗l拏l@qf1j! *縡膌?煲莹槽T]崩$ +膇[|錈!N34吒k(6% + 鎣坊"4"Y?e$虽耑bp擼j`琇亩 {搒Rs燋Z卭Hu樍拦k犆啺h鋒7R 刈蠜鲫7*饘6 z胢矅樖匾$ "C(X悎(F9}氘~C 胆7虁@ b懊x0刎PD?'俔肌#膗祪%h刈卽Ip70樧B!貭 +Pk╙嗔櫡颓 V7 旔B<萚#0乛Z'al+ 凘W0鶬矞 '9-Q +l +とd+hA 坐8P韨%琦 5q +'p伳#q+J橓茋Q('BW繢檄>CT亃劾"^6瑑`&7涄% 3尅 WЩQ >Hf伓g@丌 +#攧\ 埓瘆銅餑 2塦萿<凭 TL4毉皫b貍F爞辣?!@ 爤+XⅡ硂\0ah◇hN9`滯 4):!; 磄~雍=eu 塅t襆戣TG~蛲'獂袀 +V6叵嫤齿丂隟篾w梍2&瑮5雀8鑴弜划;?\昐?傍R`縨愿@厪h駤@4*袆摫.X:4}>欳伖(鼉芰A匫s,a@f 4x|H倫鑴0瘈1x@珺胄$*X 悇 `_搫8@8={塯>葊1癇8湈*8>Zp.8DpA僟惲xl:愯L儺@忺婥H攲*7.攧1`凙L紮K'鼒▊愹1X綡\牌>x$`隱5x僉0"}.*x9怷沢埖7dEe\ +L8 偛' ?87 *@F02'6魥?悆IXFt<.0pBㄆQs2d啅鄟=G(4HG +S!(+忽+X嘓`:庴Sx(皟倘 +竾軰H&儹捼A忚襽P峾蓮鑲肾sn吭[綆槃㧏D< J2pwど0A$唟鑸o1$+袆爠3蔄L:饒.笢挕獩蕳鋪l=籸郎A忇p偘 K峀$鹈s惽msAD堨LT(?僑八4僼縓`俒l?Dp噹餉>槻1萭`L&郛6潦蒬;#饒Sh1鑳蜭撬+J D-()d;7K鑲;"H肛\FOM4鸈%"(耺 礯埧'狼庰1rD(蝒 +hLnSN圠&跠b牴'pK彣gA頫EX剰l2;嗓$d蠋0H5B俟聓燨#t仴D}∣螩<酩亸#6`D,8D5 |%箐6N 嘅8亗;+0> 嵜gj粜葦梯孭詙1K_ 匔X句7鏛%鄲*隘y 岰@D汅儱LA形憢引 侲綥萗 L=P洋PS爛<8臆萄韠傓]嫴!`乶∮X[咐35颅>5閮^i:繮C萘gX(/35 酪鞢丅龂蠎蚑鶂侫h僃%40QH8匾帹饗3箂( X働M葮[S踯tR有Na%A=h倂*窲|THE(8罋 +絇铸/@(竴d5dV`Tf .x<觍5DW/繟E +熬nUSFHJ"ˋ->鳸KE繢磦Q8`+泄熻(+螠仳33池hN#hC倓)9皜傐衁Y杕>L(亗{=砐( 2L?嘣漝 蠾楿塎Z贑G饸3` 鑲0Y1( 誜1▊煾廉懗`%坻鲾[吽憞S檝s h#儫@><R緷=S 抂( ~dc ^瀷广=挧J1h諓шW7耘^ypys)л 壏 儱7俓鼋:0 诩磙挑'頴侂徖氂皝 `Q韖ZH~)偖韷汽@;/S +^窷剌=秱<4ah竞{87S8貐*怟0  +佰X8鸔 ~&顏俀$s\夂隦7(4尧xふ5y曥溩蹍, +T +%Vc簨h +垁80萣4鑇=:Mx呒崁 皈晛傰訩譋鰱o皓洎 ~Y疘"停0叡弯oY灞. 術__T蘖@U,L赼8e8靷~拃h錏籔L鲥*Z╠d=z +ff谖 榝+XffN1o*G^4E(=bP蹱曠杸k^a(*謌靸馴鐊1h籢儲=K腹3v,烢禯櫤絠甡1(8鋡`爾4H]瞙┋槕h^陯2 3檮c工'系jw玛2H[8盼k]碑<傅卌|a&夥=协g萛E倦簊(恔峻h罏=(皽聨3B)饽91槄溅肭巐孰刌H嚻甼屍l瀮讙] 1(hm1橂吾m抺 躄卅;蹋8澛劬 +]柡]p噢罟*]甼M+n挮=绣骀920]FI[殚4=8:9x眄6.鰵蟟疡V輖 +誉祠频誹娲ei~((谧+ 皁咉(p3愛R鰋k徫!彿o8/碜/ +G茬%釶飌雰浠gL51曛輩]8R`3鯼媇06痣G2`n]皝HKr銇~#讟S&ОNxr:堯箷伯颚1'O*嬞况/s9'P69`嫸fs +sr挪H+`儶秙s膨 鲬"爎@煯.@w +鱜遱_饁wGwo+d踫 顉铷m G韣o*.侾8b,鴰陝菾5 +w鴁"儃N9爓媑燶夛驇o顭y廁鰠:Fyw鲆鄀~wyvJ儍fw铲鬆Z座隈蟾<z_ZtMR6鷂僁H4Be鷂陚鴊vD氟F/8參鼹F鷨袱. KH磸{篃{函{豢{枷{竭{撅{{||/|?|腛|臺|苚||葟|蔁|石|丝|滔|童鼆! ,K H盃羶*\劝∶#J淗雹艐3j苋保菑 CI菠蓳(S猏刹ニ0c蕼I肠蜎8s贶沙烜 +J川眩H*]蚀┯滼氮斋X砵菔但庄`脢K冬俪h营]硕穚闶滽樊莼x箨菟矾呖S}鯨搬 #^谈便菒#K濴共逅3k尢钩缦燙M捍橛⊿玘秃uC庐c颰,欢碹竤朕突凤呃 N几胥葥+_渭贵缧烴胶蹼爻k呶交黠嗝O炯栌玙暇禁黟闼烵烤銄酨A煨骼e宪覤 @酄? 袀F$蓉3a ?AA2秲"惰"掆a细!."<佲寕=s-瑷#I痧#悁=CJ棰?U悜!搣jH閎鴥e摯Pq]z 7裢A轑剼 扗 p -軅w怡 I苡g_ l7XHR 'Z 紃忙7饟)鋈觨' I躷觤(w.繩j @F侾'55d粧4铠懹菝'雋7=s%蘪d 踏=T-D秶(N箕-$杼弹垲) 腓R廾D9:0< 纗"'盻菝4裇[膜R&a剹 +羷1 釹炘笲寓}9[p hy﹑^ruH獝Z7>t0,})衇齒$%L'K!l|$x誛峜崘4萛 +$&. ?貱 @x$"罞If5屶啿絼*Ra92R绹y3q PqK俢 鸭=;Z朽m萩嬋癢-獉-/柤唷$Tk 罄*刬H椠 L饏j萡蔢騕3騱 XC驙±!睧慏T叀Fb<劕)x0f(儃  t!紣@宩焓_皧.5鴤F愓L 蟨@b7@,.lp啅!xx~爄8^ 翔r#a乾f!埪- +D{龂;注o琜;H3@ +@!8l21廟XH5P霥P榔贪1孞C["reG鎷9H仺Ct(}cl!"y埅P阦Ta猾Q蜔闙{ = + 矅yx8 P噈|@D'"L駥/P谷B秾靷 !襡鮼牧刐Q鄉n〈 闑谏0媬( 鲐鏃`忁5倢 +矅樒3v悑Bda衅卫3RJ鬇裱 だ猥沟9D憏麓牬3 Sk梗躷6婲挖)?悑W2嚡A`l# Y0i(麫 +;= 蠫楃L9 3r蹢q "#b髆S"UPoVP鰫嚑f@q唨 0艶vPht ilP;-4KP褩u #<爖's r葃.o9 F餕 I 禓毨!M0豟0 vpgl x +扸a椑br<楃 ?"APW:2L"圔4&評(` +up@ @`,hl8僎 7 $;仔Qk q"2#鰼嚢_拠団噏<;鬻 1Pp塒pa +p"`624%=q9yPI唫'孎bF <饎柪|`pa`Wl` &ph` 9恆剷>01呢YZ∨岊鴰 +("81Pt膄pt纏E@ + U`X8P3便 +娜YV鶒^<+D庐l稜 p*峰隸v 揮洊A籃瓷82}+"鱬pW8mb矃{U q侀梟 a w藣塪* 夿@爜㈢I鱒!絇$+l +穖`嗬 '鸶>K簒c癳E`G9甜睰肫]岭04奴盖 +鄉 g 唃胺k0滈帟$#6独\列丅0w邒侠 j樸嫷P珆P6I枴P╖t嵡D康k}賓);秊聅!嘝 \$ `m+媭(((p驄r +渦糐0@ :&4L` 门k=8{眒p讥l唐O<P @堗@顇S睭+!w& 刏3 ;衘庫搄 肓} 0 l*敔r+ 戰mJ駅l妧% 礏 戇惗1Lj?< +r!0 -[8獯抵#錤I*+翃z4@,穦湼鍊北019,0A'%0蛮 彏c 呤T瑙#㤘"矄/'仪 牋/燚 N`垧蛠IH肷Hmt趥0黴9L@(痂Hj碆%逿]矛jF 褽2}%$銑X8蓟q艅搻N@聙0@普L瓏!K TO -` +* 咰P餍 4p!拀设B Lz&荗6  &A2$$优%M$Ld@<爦 59(]毨 =-(B]禶碍偘胸唐Jp蠣 v 咶 p9碚蓌<`獺J訖#燩&'+ 9圛戺PsI唽厉q@1髯詠 +c=綱S豟膽推(p +-k`&萨庿耭N&K) 躒譌"6饊(L妒 L.oL姢W f斫蚜&-59;厩蹢洼苸 q +ⅹP@黟4绨萆 +r贙 +s#這L号cAx% 饴B(l;汹S2懚p逞`悗p9|0愅na 椵俵 +呃%彌g@傦匚禶麕+"弼;P奘 dp硌賓0 ahQp卅 寿(%竫W % 鋕"坤3鎥⒀ $跜哙叫興*y螆呱Q摖(P;纹~0脿暗浦盛&P畞<畭'е&曗獢zR$S$ 硁墷 @n`.蔕=粛踊 +◥`砣-Po.庤蓌遅乲蝫0 4]甀s掐7 莵T|皥嶡娥噻p汀<师`鱔lh | -I[忳綘奥`&⑨齄"g摣胊痴氚堗鎞F 1 a粓 +P窄r燯騌22枳}L盄喹麷麴$狅翾a`扨V嫲}涸扌OPG`0媍(F嚚慆 P拰oK锃絗轓"岛1 K`扏DP.涝LM呢L`d +N皪C @DN_4珰辺/P[X春%!夓炔礋萣 霞芟p +q m@@瘐嬰筐 ~P !佬Hl qg闲痼nO +k\ 怾a匿82\ 吿 嚴樾.捽墹穵諥n;`#6R/谈|唼 $X朽A .d匦酑%NT芈TZK蕰涏鎮儘!E)wA@9L< 馍1憏謤CVA狓館 +K %Z澡Qはx劂/㏎Ac芁慍O祅輟颬!U0!$H骶 8鲁瀜逦.. +@Y鏗$嘉亙P 厼<<藀SA" "|Fk )噡 rO U笔EZ驯缔A茍!;蚍v髈嗔 +$"楇K8$6+閏衮&-熏寭0Bp鉌鳄-x`f疊! 邢鐾_#鰝莨c雧iw(0 b8v亍@硉#x兝緛M<;粜*YB怐掓溮堷E墫硠 =寈&@;c)j仚^v 氟wx藮f筩*镟=2KP0死vH艾餉郊- **壽岘氪笪垬筦W<0趆蠥Qtb` {d:迤嚇"& $烞2愈 倗 +=.傖 [l"CN缬侰0$^胹W^駎 嶛\癜孆/I鬛T&bB錛qR鄰+x蝠!x4柱h#餠*讝SB!*<銱n= +傸尠禬z顣CHv_卢(ケ>a`爂L乀Zj[俓蒎>w抙O7s;d莘8禖0俁{Cy宊斲 "E熻嚑.#G:錁盐ge恽朶{%圕L泫h鷨Lb迲泇鸂gJ 0Qv.訇$;Pv1:馏2n8(t{CXwɡ僻雫 &墛舻z#I"┊# ',k鄰1 嗾v垥 +57 旨]w*涝n许u烬Z9 +*uC跍屁⒆渕偸箞莒@b%榒C#by鏃鲖劂`` $g %'t顆 _%*圚q_I榒%:pj`垭mA 蟽尙代fxHBe ` + x@:X` 褕nt t-`P奟L8X@<<@Z`N蹅!霢擃虐C N+l Jx/\7纔J垍覀HaE郠 P!鸽K楡&x@& G簯繞燘$礌 琣 4犃68V鳸/B<獗o 嵭 7埮靬菮Ocl 箼~EV(啦埩+爩u 10恛0#尧C,z=#U0}纑8 tF&l Q鐯辢抆袀聬Z4T焧ぃ.C駯;劺&{憞`a抃鑾Gf.Z@涅6B抇d-L&肪'8 牂X敖=鑩0K2蝷AQ(`簇嵺PY痝&Nj彨*4 說E#驆 粹"IZ A硭 +訟 b +'塦 1瞣$ +=S犈n4bぢ7犈7幀摬$麂奭鏨,鶷嗆#@t窗C"" 诵侺J X檻a 齂&@p茉佄郉)榾 浮+刯BA虪 +鶖CT }elB訞卹>銂B,汗疌1D4?緌傴徜0庞&"A麂F#班&P D *a毱B-B/<稜j<爃n鹺嶎KP=G&內蓶 @BK1t县Az +,睱'^B>芣3黙鲐sH!C蒦嬾V琡吶*g|雼Q狺 衏桕7h H +缌Z鶎C(肞]7~9 "稷Ap9e鲙2棍橵 耑9[498Q'I迸.1锧Pq*,pj惌軗勵燤lq:"詟E0酡% 韱`厎Y蝢q锃$/腺i遆E +倎f &$墣v=G"哓A鄶gI跭 AJ鄨 pB(v &ev3齤荻;跤腜鋤狛a機鈥B燏 Ox雹絗訸 ┫睪謪4 笷*顮pY:$0醼儨B%JPeg蓥"琿8P硤:抲x1Oh':k.[P泻n'|W殌B旈 @  g9%押廷鄀 +堾验*\#缧7紣yQ[S``偝e?b 剉q`0蓕釾WZ墵6乩ha&I3;俱袳p炦鑋:E 夤 圗P8 P饎Yp虹 1Ⅻ负雥琨B&釁(際6犐毯P 袪~樑芧剱#V'z薜踳蘥蕹燷N/9蓛@*p]t僺\鐥甚5! a瘄甿0项 ";514@壈n頁 {P磎 X;濭>6' 楬馏\?w鲞?S6榧窗^>(d c *v +'a}蔏?歨?i!8悆;圕z沀S?t;;卼 @飥竇3劏4H冞?  |Ae ?埜TA諧翱80 0X,LBI 欣翶;緞皠NK8 +TB03q<舫!怋珹_讣嚇畸 C9床I1!c0P垨鯧9 A囪;H汣C谋g凍>l3(/嗫儴@垁Z魩柌9垁P亙鸈g&A訞▊Q"@+V<'銆|Fo敗o晵jl緿僑僱麱xP鹉侂t46原w孏~3~垇D傌L拱碝醸K +4N鍸樑ddlN4*fH囪[Dw聞 O) +x嗩旒嘽 O馜aL*d両`O駎[埾藾甏橡.蠴敎Nt笙谠{埼鯟苦4墟fP形嬆聇P纥1 8饏牬嗅d唎`?烡@m>H \╋,Q鎑0HQHeQ伸=Q魶D蜸H)1皜蜰0t乚ㄐ!EM=HJ贗蟐\胰墓荔<鐵)鞮琑,E袇#1P/峀<邬+惹3=L'-籾|用, (x睸命啳敄24楺?uJ<3`,=袁|獢2_8;](侜擳泊GE輩F吢+郃濒詮`N`^`n`~`巂 瀈 +甡 綻 蝋 轥頯㤘aa.a>aF踿!,K H盃羶*\劝∶#J淗雹艐3j苋保菑 CI菠蓳(S猏刹ニ0c蕼I肠蜎8s贶沙烜 +J川眩H*]蚀┯滼氮斋X砵菔但庄`脢K冬俪h营]硕穚鉬|鯨5殁菟矾呖 L赴崦+^谈便菒#K濴共逅3k尢钩缦燙M捍橛⊿玘秃惦装c藶M欢碹竤朕突凤呃 N几胥葥+_渭贵缧烴胶蹼爻k呶交黠嗝O炯栌玙暇禁黟闼烵烤脒峡(鄝O=#+7 釆x 訣燻H8 RHQe斞8t841L=ㄆ=~鋸 恆褜f7芰w寊 A˙!`=sA爛?俻^#锐夻S{. .∟5擙1谹'HDr2宋$梿iz|r訁瑺惓@ZPkF麅xF * #疤~扃_磬墟6%kb皜0糮1 峵璳])H H幚掐乷7緅鞱s 0媉 r苭︰沎誾|悌惏L圶?鳣  +m8G0pL#`Oz琣/b6+8鍸褘T @λ岧 勆Χ5䁖仕=髿O .(█ \@翽敚 V癊"襊:D<镮玺Rq堽0 h悇碿侥囙鈵U 库镖酮Z!;颠H0I戳X00 +竵ゐG 饋B饆9臅硤O:\9爥鷛8BBq}鴼9緽俴{a搪炯j9_^FW"畎層肻 怐9靶,堍Z啬"阷鬣bP0pxUeU 恗u%D畋jT pu硢Td \0 5p喍s +5`v%砢y侠 kKp7XT嚢[ a +違 } 8|蘞vpS*癨`'骚Q飦_殫z馇^B癡`B罘q驠n%6喞r扏螑嵿d  |@U%U悎餪|u%a Y亹^湩乚榝哢_cH&鑒\╖-`ǚv识抪)7o恀a '|m@b郳≦鬬UUJC P@k\H-l檲u`F鄠錰姪%渊l餑珗堇 T`wf榾 I汔垊鄨(qC,&(鲂 碢H佢羖蠘夨`zg嵷HF0冨v卫媨h歚 +堩貛@弨鋂c库U 稰 +袃軎-懛;P-:6愭d.杒`婒謔& s 郒G鞫u0m饝wb $墄濽%puˊ蘌X淧@碢壯 -悡q悡;`}c嫷>蒦抴^樔^;P渊楈) 萼 T饠遬 |X+`65捔8E ,鄬緽kk鶌儼}輹gv唟Yz绦 菃y0泴奕娢@9恈 靾y[ 屔讋賤W'蒬 u卫 T<燵n這gq0nd瘔z%}╢H檉j哓 L袠睩 x尷 '臲@瀠"pS夃HP衭%諾婨澻 gpHvGfmV炏p`*xy 憟Y459鴫晙 堾蛐C餻裡tpShEp&剏<VP +k)Py贊菅'櫁d畽闤X诪珮峎 ,冖〨 b穰"ti0T哢Eoz+0 t {;悜A}瓥汤ユ$ +烥R}脡﹀t! `p.悘J pU" 柪gD囬 +帉p漬0撻醓A牎貐d`}Zb馥)悽(!}w昲m协捓!%@許cp +裧杍H@潿H萼 +褐< 儝猯vi錺樲皹UP藹o注+G貛挔O ! m撙Aj咝[s鲓@LP蝰 ⺁棤p)~ 0囕兝 魴爛 ]Z@貖BhiJ 甁0 hpU笕畊Y俕Ho w+)Q@n蝰 t&訄鐬梧 0ゐN蹱nP窜胾仃(p便ZHW*F[鍐V798遬跺5薶#輕 僷U- +.穀iU ` x灏!悘禈mgPX迸h薧K酁絲冚惓^騠f沯 o䅟 +N@簚DK溳z羆_M( @z罆Zt燬pa熛{涚P +0Y0X徉@潽騀魉^H鄭針谐. +讷i 猉) +@5 瘖钡K Se笀 牋獿蔡*T`)訨騠颗^0L0 AP黸\ PT - +x↗倠盝Pu5'兪茀 卫0搆@T G諓\拃冝P +爩7!1蘑j 箩0坷$SP肋 p鳡斘 跚 Lp@矿醓脆.缞)9 9摖QpV M+(p繷hp撪侠y潾續皧口 窗嘉刾噧pm}p属(琄湌 S,5@乚纮Z\踴泍箲 T0Y鸄痘e5, 賊魂J揚竄擴S檍捛K士牄j姑* 喊栋鯇酿蕛*\Uy黒r愃^囙4) a +B纒 +虐鼞V 澅痡 +a a [,u糹啯iQPZp8熽v 伆掟嫲諀暱嘌O  n袣 询麀訷 HIV@D犙M嬀O写(`U` `怮gp,!;惟啗t J + +螾-@判-ヰ 苦5p縋(=<` |H塒P闲r&() 睏漬嗂` dA |0詉慲倠鞬юX丧Eh旎<惱嵺L y 扩逸颶湌P缋?' (啻OQ鞫轝u丱漥A朮IP:屭伆i蕩軡o镶艀迯E走妠dP醝汩pL4陮覷@嗲龛乏适 别蕗J 4Hp丱 sK嵗潎g抈莪甅蛜扨 +L;,:m逦0炎卝 奢J,M@ +拤焟疰L捂m䎱,窸@ 搖 +玴Q棛h鈬y儢嬶7)问梳M' +郸镃@羘)鈬頑琟戾把=葃0ю 服羥&曏b渑 暘昌嵿cJIjP&n瞳帵*懤 痨 +p焕 +歫; P 'Kiy筈P铐構戉J^鞉S, 瘑 F愘rpQgDP +IF@I尷嫗曖甮戾 Z0臤+鸘饚c犲n貐#郟 +MZ d犼.Zp炱~2OzpSPaj>I熬 +罭c愹艍蠋-嘈- +踱俙萞`垱>]9緱=0洁殡愨M胴崷0熗 rc戌! +蹩飓L 绪x +PGQ!鏽V` #}餕Vp湺賲y珳u`, 郗N耷ⅹ諫 %擴, +"!毓/~ 炭Z(2K夛历?檶按+ 﨤芵郀嵂腒勣厅K|H*W朽6鍉=y騥]聝$呐O釀!习忻唁I)U鱉刧1e螠笴HqZF") u啗{,.e谠镾≦贞U(熭駹"懘渟5ⅳHO0^|忆橧B6鴖TV樰箝鬀=fB$1 A怿9癣)蹳繹衮e虣5o捃oEY跦:t箘,甎K嵣S*B*3鐡*翦M鬀d猅悇菐@寽y6擺乷弗O蔸u 0幢r诨W*甧;$0 B&*:藉w垖f@ 奞稦y ; 4餈I胡4+""0蓲廈"@琸亣鵅 4爜t馝c膉"谅 +尐島T0#FL偫怐*!宏 CD&gɡ 勮IF,吃RFJ"-罳浅XmB01fS`&鳖vh蛭q&愧, +爛 ha- 5粜蘌a亶"J榚$硩6~9s'\;笲鷥K3!苜aP鶘j螰\餾 7!Q\s諘)]乁钚倯襱渉<蛟>檷+f`毌>Z +業" X鮅6剅g榲u鱙] E~-釒H渋;Q嫷'1*淽庇砦 奮刪邻&p栖■Lw 芄^怌v@J{g癣滎B賷旔AM"鑄丏D窧﹤a烜p噰$¨榾聵=]`怿Xdm"醀((@页9S',\塎7锧鄪P h轟觎枂 >蹈繬2>tQo3~ar4G务1粿R%S8\伳0ダg榓l緊Z佲揞 傝$c]撒銎5h╟炅s捉邑鼜_a7()晞 +%葉躘 胪@cm +D铥2P镫w@僐鴰瞤\饹草銞*<戉E0乀7瞭#(xDP勠$緵 vAM驀P鹄V(*0v隱'r蘋咉肈4T/_閺T1 @*t0牤銨啎<騪 嘚摼!tM渫!. +pB}朊B7P冹璓寖麊8浤龏兪溲燼E豘 w!'晐 +hz8i@[L*X7埾' 6P嵭3.7FL喬閕`餢P塯1mhAEP怈亦;!` ;lQH$Uo惏盇D堇满毫#撍刉8P=" +儶辧@,眩T`K╜C饏^剹2&B1 F÷6僴xJ#1堷0S牳j E+"nb0昡hI僧gsI鐴杸5專 HB贄{#X`: @嬻J8凨T2z K貭|h5氲嫸﹦q)3腵 樌簣3*P(0 "!zA榐oB#碢僲t`k燘 +;瀚岤览僭K6 n菩 2)酺怐乩佷3 7l@ p邎 i骾A猔%欻耚Gh纼:DJ眽U饬34_  噓-粬X %釄癭瘑z?x@呁簚OE21c肵s春' F偻n輯Mg2AhXTo e 標慡$"儺,+ 衻 +眉3邬苊&枂,Bj哙"≒ 鹘#芁 咶憎 mp瘣魔tB楞疄俕lb赌L丳黓辮I`晠cg ゐqJb(k,XP74q,X,SL坙葽圔簒13`磣I +He<_#拯 x恊啟a }J$p傞>浶3殐f,Hb !C岎%愋崁&鴱g*希螉)Ny 6T@數7z +貢(i^s& +=憪}b啳H}飓`偸砳樍j /㎝懔97v"L 捡GI=I.: +/Av互 0v5埢濻鎡)Z椿g疣p穱襝}嵒/#骄FpB 穝w艣 +绬$ }hcQ闋通T7赅<P窸0A50槃X簂q 蓗nJ娫?7*咝EWp鋃錌a9J憙2岺@攕嬖夎~叽 BE6恟若摝8軍腠r Ha謘迋漦 /鈚靜蚂[<3>$tk ;,W穥艧7 瞚h螤@rd?e﹂櫪ˋJ籉0;▅#爞5 :+ y堓J'鷺渫8BJA|.蔣@礕%./漭Mg-+~}踃`@L盍崟6傰k慘埍S穐 @弳 +XF:@bJh:#s4Nx)* <伣惪Q僋94N<C:X#p尖s嗐S⿲駟R垈芐箤q H4$笨R廋烁怭~8-x絻+糿垊兙豕CXA(銇潛 7A攉紀█Ah柯#!o鄤(8Xn貈#B黩怈 D18. 8?` 4棦俻(ω蓙8鹇鬶脨:2魢;dCg&▉p5,& 鐱;+7擢2+脛\(菳Cd歯爛<唰⒗ɑH+1`0偀 $"|!h5<m, 蟹U\皝!h()埧D禣$*D}'&h痓`%0XF.P泵珌0A6錄x綽*d鴪q魰?@,Z#翪*x糺鴢,繠t閱 |d* 8#釨凍(苚( p莣l(貑5衻茐\&P0x6:釰済,苂璎j$<[0羯睍膜p *@>7鄽b$B&IMk  +扒 (1b;H倕b1:拤蓪貰w 曚H侾7膲 8-怌搐$# $/Qb?安+:p粳F/趰C$L8吅:(+狮,$(匛鴨o-虒(`單tH嘋刷p!lM傒H踢藾厴蔈C錄A豃捃L垙j&$L:┇F,|\N翙耗/A{5 5P楞膫(p鴻F_ 倻屘O糔贙蟌嫃瀒鉓燨C鋯 F麯' 藷b "挫@8P 0K醠P袧膫 `M +1葊:冭腛褼K樞;腞1乜屠v苌nU|裖M貐 裲X劑躀MuSHy&} 妊O\禁l襖1;(.牠j<逡9e $4.%昔㎡*5艜Nk狜覧郟6L⒓SC+枚;厐4腇;B5娕<;嬙鳆D窽L0d!傏|8葋<睧h蘋E怭E"鑳S QZ 膚,羭U.延怈_68aW 验,M,X4跽锧6崳] "8+@}[沆'!刅Y;(%覻@M; +q}=8燯t6H厐MHS: +刚x晭;ㄗs%9袩薚S-赝5`(88]#&L極 INp褗-悐-嶅X粈$X隳 貟諏窰8Sb{U7勡4 +h貦N 鄝燯::バ|%:蓉)e陔=湑(↖q}.(Y诿"6貐@mXFu伣羳g-[(Zf炶"藖m5x8Z煌 ]H刯b:拑浪仴叧, +*惾屡峂獮P\&<犡x}?暶坝逝 -嵧蝬}X貋滓鍖éf魰18YZ8纮t圿丸-煤 蠵擸鄷 + 4:*6`豲`<耺^珢諼 敛乚茸d跽Q圚罱孹鸮郱8漎(& 蒗 hR淽俨%`蔹 +,樲讚雲茓趰遳澷g貏 堗瑯,蹸0 +4_繷剞妽 +T@`憪働(坅v姰k!饏P8E輆"N 嬕冦-怩>摊+a&瀊撪+-鎴I郷贺掴炨飬據1鎋0嘄A)&c +4v沲-jI勡ャ""咼8c=>岙; 樸@n皟4H皟Fa轉.]1皜E鎬綢>.饌癲 @掬1袇=绖%&eW~eX巈Y瀍Z甧[緀\蝒]辝^頴_㧐`fafb.fc>fdNfe^ffnfg~fh巉i瀎j甪k緁l蝔m辠n頵opgqgr.gs>gtNgu^gvngw~gt; \ Kein Zeilenumbruch am Dateiende. diff -Nura php-5.1.4/ext/gd/tests/bug37360.phpt hardening-patch-5.1.4-0.4.15/ext/gd/tests/bug37360.phpt --- php-5.1.4/ext/gd/tests/bug37360.phpt 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/gd/tests/bug37360.phpt 2006-09-05 20:31:02.000000000 +0200 @@ -0,0 +1,14 @@ +--TEST-- +Bug #37360 (gdimagecreatefromgif, bad image sizes) +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +resource(%d) of type (gd) diff -Nura php-5.1.4/ext/imap/php_imap.c hardening-patch-5.1.4-0.4.15/ext/imap/php_imap.c --- php-5.1.4/ext/imap/php_imap.c 2006-01-28 09:07:20.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/imap/php_imap.c 2006-09-05 20:31:02.000000000 +0200 @@ -26,7 +26,7 @@ | PHP 4.0 updates: Zeev Suraski | +----------------------------------------------------------------------+ */ -/* $Id: php_imap.c,v 1.208.2.7 2006/01/28 08:07:20 mike Exp $ */ +/* $Id: php_imap.c,v 1.208.2.8 2006/08/04 20:31:41 iliaa Exp $ */ #define IMAP41 @@ -761,6 +761,13 @@ efree(IMAPG(imap_password)); } + /* local filename, need to perform open_basedir and safe_mode checks */ + if (Z_STRVAL_PP(mailbox)[0] != '{' && + (php_check_open_basedir(Z_STRVAL_PP(mailbox) TSRMLS_CC) || + (PG(safe_mode) && !php_checkuid(Z_STRVAL_PP(mailbox), NULL, CHECKUID_CHECK_FILE_AND_DIR)))) { + RETURN_FALSE; + } + IMAPG(imap_user) = estrndup(Z_STRVAL_PP(user), Z_STRLEN_PP(user)); IMAPG(imap_password) = estrndup(Z_STRVAL_PP(passwd), Z_STRLEN_PP(passwd)); diff -Nura php-5.1.4/ext/mysql/php_mysql.c hardening-patch-5.1.4-0.4.15/ext/mysql/php_mysql.c --- php-5.1.4/ext/mysql/php_mysql.c 2006-01-01 13:50:09.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/mysql/php_mysql.c 2006-09-05 20:31:02.000000000 +0200 @@ -1231,6 +1231,8 @@ { php_mysql_conn *mysql; MYSQL_RES *mysql_result; + char *copy_query; + int i; ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, link_id, "MySQL-Link", le_link, le_plink); @@ -1281,6 +1283,13 @@ php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn)); } } + copy_query = estrdup(Z_STRVAL_PP(query)); + for (i=0; copy_query[i]; i++) if (copy_query[i] < 32) copy_query[i]='.'; + php_security_log(S_SQL, "MySQL error: %s - query: %s", mysql_error(&mysql->conn), copy_query); + efree(copy_query); + if (HG(hphp_sql_bailout_on_error)) { + zend_bailout(); + } RETURN_FALSE; } #else @@ -1291,6 +1300,13 @@ php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn)); } } + copy_query = estrdup(Z_STRVAL_PP(query)); + for (i=0; copy_query[i]; i++) if (copy_query[i] < 32) copy_query[i]='.'; + php_security_log(S_SQL, "MySQL error: %s - query: %s", mysql_error(&mysql->conn), copy_query); + efree(copy_query); + if (HG(hphp_sql_bailout_on_error)) { + zend_bailout(); + } RETURN_FALSE; } #endif diff -Nura php-5.1.4/ext/mysqli/mysqli_nonapi.c hardening-patch-5.1.4-0.4.15/ext/mysqli/mysqli_nonapi.c --- php-5.1.4/ext/mysqli/mysqli_nonapi.c 2006-03-24 10:32:24.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/mysqli/mysqli_nonapi.c 2006-09-05 20:31:02.000000000 +0200 @@ -184,6 +184,17 @@ if (mysql_real_query(mysql->mysql, query, query_len)) { char s_error[MYSQL_ERRMSG_SIZE], s_sqlstate[SQLSTATE_LENGTH+1]; unsigned int s_errno; +#if HARDENING_PATCH + char *query_copy = estrdup(query); + int i; + + for (i=0; query_copy[i]; i++) if (query_copy[i]<32) query_copy[i]='.'; + php_security_log(S_SQL, "MySQLi error: %s - query: %s", mysql->mysql->net.last_error, query_copy); + efree(query_copy); + if (HG(hphp_sql_bailout_on_error)) { + zend_bailout(); + } +#endif MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); /* we have to save error information, cause @@ -234,6 +245,17 @@ MYSQLI_DISABLE_MQ; if (mysql_real_query(mysql->mysql, query, query_len)) { +#if HARDENING_PATCH + char *query_copy = estrdup(query); + int i; + + for (i=0; query_copy[i]; i++) if (query_copy[i]<32) query_copy[i]='.'; + php_security_log(S_SQL, "MySQLi error: %s - query: %s", mysql->mysql->net.last_error, query_copy); + efree(query_copy); + if (HG(hphp_sql_bailout_on_error)) { + zend_bailout(); + } +#endif MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } diff -Nura php-5.1.4/ext/pgsql/pgsql.c hardening-patch-5.1.4-0.4.15/ext/pgsql/pgsql.c --- php-5.1.4/ext/pgsql/pgsql.c 2006-04-10 21:51:55.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/pgsql/pgsql.c 2006-09-05 20:31:04.000000000 +0200 @@ -1152,10 +1152,28 @@ case PGRES_EMPTY_QUERY: case PGRES_BAD_RESPONSE: case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - PHP_PQ_ERROR("Query failed: %s", pgsql); - PQclear(pgsql_result); - RETURN_FALSE; + case PGRES_FATAL_ERROR: + { +#if HARDENING_PATCH + int i; + char *query_copy; +#endif + char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); + PQclear(pgsql_result); +#if HARDENING_PATCH + query_copy = estrdup(Z_STRVAL_PP(query)); + for (i=0; query_copy[i]; i++) if (query_copy[i]<32) query_copy[i]='.'; + php_security_log(S_SQL, "PgSQL error: %s - query: %s", msgbuf, query_copy); + efree(query_copy); + if (HG(hphp_sql_bailout_on_error)) { + efree(msgbuf); + zend_bailout(); + } +#endif + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Query failed: %s", msgbuf); + efree(msgbuf); + RETURN_FALSE; + } break; case PGRES_COMMAND_OK: /* successful command that did not return rows */ default: diff -Nura php-5.1.4/ext/session/mod_files.c hardening-patch-5.1.4-0.4.15/ext/session/mod_files.c --- php-5.1.4/ext/session/mod_files.c 2006-04-18 02:31:45.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/session/mod_files.c 2006-09-05 20:31:04.000000000 +0200 @@ -152,6 +152,7 @@ if (!ps_files_valid_key(key)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,'"); + PS(invalid_session_id) = 1; return; } if (!ps_files_path_create(buf, sizeof(buf), data, key)) @@ -401,7 +402,12 @@ ps_files_close(data); if (VCWD_UNLINK(buf) == -1) { - return FAILURE; + /* This is a little safety check for instances when we are dealing with a regenerated session + * that was not yet written to disk + */ + if (!VCWD_ACCESS(buf, F_OK)) { + return FAILURE; + } } } @@ -422,6 +428,35 @@ return SUCCESS; } +PS_VALIDATE_SID_FUNC(files) +{ + char buf[MAXPATHLEN]; + int fd; + PS_FILES_DATA; + + if (!ps_files_valid_key(key)) { + return FAILURE; + } + + if (!PS(use_strict_mode)) { + return SUCCESS; + } + + if (!ps_files_path_create(buf, sizeof(buf), data, key)) { + return FAILURE; + } + + fd = VCWD_OPEN_MODE(buf, O_RDWR | O_BINARY, + data->filemode); + + if (fd != -1) { + close(fd); + return SUCCESS; + } + + return FAILURE; +} + /* * Local variables: * tab-width: 4 diff -Nura php-5.1.4/ext/session/mod_mm.c hardening-patch-5.1.4-0.4.15/ext/session/mod_mm.c --- php-5.1.4/ext/session/mod_mm.c 2006-01-01 13:50:12.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/session/mod_mm.c 2006-09-05 20:31:04.000000000 +0200 @@ -425,6 +425,42 @@ return SUCCESS; } +PS_VALIDATE_SID_FUNC(mm) +{ + PS_MM_DATA; + ps_sd *sd; + const char *p; + char c; + int ret = SUCCESS; + + for (p = key; (c = *p); p++) { + /* valid characters are a..z,A..Z,0..9 */ + if (!((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == ',' + || c == '-')) { + return FAILURE; + } + } + + if (!PS(use_strict_mode)) { + return SUCCESS; + } + + mm_lock(data->mm, MM_LOCK_RD); + + sd = ps_sd_lookup(data, key, 0); + if (sd) { + mm_unlock(data->mm); + return SUCCESS; + } + + mm_unlock(data->mm); + + return FAILURE; +} + #endif /* diff -Nura php-5.1.4/ext/session/mod_user.c hardening-patch-5.1.4-0.4.15/ext/session/mod_user.c --- php-5.1.4/ext/session/mod_user.c 2006-01-01 13:50:12.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/session/mod_user.c 2006-09-05 20:31:04.000000000 +0200 @@ -23,7 +23,7 @@ #include "mod_user.h" ps_module ps_mod_user = { - PS_MOD(user) + PS_MOD_SID(user) }; #define SESS_ZVAL_LONG(val, a) \ @@ -174,6 +174,83 @@ FINISH; } +PS_CREATE_SID_FUNC(user) +{ + int i; + char *val = NULL; + zval *retval; + ps_user *mdata = PS_GET_MOD_DATA(); + + if (!mdata) + return estrndup("", 0); + + if (PSF(create) == NULL || ZVAL_IS_NULL(PSF(create))) { + return php_session_create_id(mod_data, newlen TSRMLS_CC); + } + retval = ps_call_handler(PSF(create), 0, NULL TSRMLS_CC); + + if (retval) { + if (Z_TYPE_P(retval) == IS_STRING) { + val = estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval)); + } else { + val = estrndup("", 0); + } + zval_ptr_dtor(&retval); + } else { + val = estrndup("", 0); + } + + return val; +} + +static int ps_user_valid_key(const char *key TSRMLS_DC) +{ + size_t len; + const char *p; + char c; + int ret = SUCCESS; + + for (p = key; (c = *p); p++) { + /* valid characters are a..z,A..Z,0..9 */ + if (!((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == ',' + || c == '-')) { + ret = FAILURE; + break; + } + } + + len = p - key; + + if (len == 0) + ret = FAILURE; + + return ret; +} + +PS_VALIDATE_SID_FUNC(user) +{ + zval *args[1]; + STDVARS; + + if (PSF(validate) == NULL || ZVAL_IS_NULL(PSF(validate))) { + return ps_user_valid_key(key TSRMLS_CC); + } + SESS_ZVAL_STRING(key, args[0]); + + retval = ps_call_handler(PSF(validate), 1, args TSRMLS_CC); + + if (retval) { + convert_to_long(retval); + ret = Z_LVAL_P(retval) ? SUCCESS : FAILURE; + zval_ptr_dtor(&retval); + } + + return ret; +} + /* * Local variables: * tab-width: 4 diff -Nura php-5.1.4/ext/session/mod_user.h hardening-patch-5.1.4-0.4.15/ext/session/mod_user.h --- php-5.1.4/ext/session/mod_user.h 2006-01-01 13:50:12.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/session/mod_user.h 2006-09-05 20:31:04.000000000 +0200 @@ -22,7 +22,7 @@ #define MOD_USER_H typedef union { - zval *names[6]; + zval *names[8]; struct { zval *ps_open; zval *ps_close; @@ -30,6 +30,8 @@ zval *ps_write; zval *ps_destroy; zval *ps_gc; + zval *ps_create; + zval *ps_validate; } name; } ps_user; diff -Nura php-5.1.4/ext/session/php_session.h hardening-patch-5.1.4-0.4.15/ext/session/php_session.h --- php-5.1.4/ext/session/php_session.h 2006-01-28 07:14:49.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/session/php_session.h 2006-09-05 20:31:04.000000000 +0200 @@ -23,7 +23,7 @@ #include "ext/standard/php_var.h" -#define PHP_SESSION_API 20020330 +#define PHP_SESSION_API 20051121 #define PS_OPEN_ARGS void **mod_data, const char *save_path, const char *session_name TSRMLS_DC #define PS_CLOSE_ARGS void **mod_data TSRMLS_DC @@ -32,6 +32,7 @@ #define PS_DESTROY_ARGS void **mod_data, const char *key TSRMLS_DC #define PS_GC_ARGS void **mod_data, int maxlifetime, int *nrdels TSRMLS_DC #define PS_CREATE_SID_ARGS void **mod_data, int *newlen TSRMLS_DC +#define PS_VALIDATE_SID_ARGS void **mod_data, const char *key TSRMLS_DC /* default create id function */ PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS); @@ -45,6 +46,7 @@ int (*s_destroy)(PS_DESTROY_ARGS); int (*s_gc)(PS_GC_ARGS); char *(*s_create_sid)(PS_CREATE_SID_ARGS); + int (*s_validate_sid)(PS_VALIDATE_SID_ARGS); } ps_module; #define PS_GET_MOD_DATA() *mod_data @@ -57,6 +59,7 @@ #define PS_DESTROY_FUNC(x) int ps_delete_##x(PS_DESTROY_ARGS) #define PS_GC_FUNC(x) int ps_gc_##x(PS_GC_ARGS) #define PS_CREATE_SID_FUNC(x) char *ps_create_sid_##x(PS_CREATE_SID_ARGS) +#define PS_VALIDATE_SID_FUNC(x) int ps_validate_sid_##x(PS_VALIDATE_SID_ARGS) #define PS_FUNCS(x) \ PS_OPEN_FUNC(x); \ @@ -65,11 +68,12 @@ PS_WRITE_FUNC(x); \ PS_DESTROY_FUNC(x); \ PS_GC_FUNC(x); \ - PS_CREATE_SID_FUNC(x) + PS_CREATE_SID_FUNC(x); \ + PS_VALIDATE_SID_FUNC(x) #define PS_MOD(x) \ #x, ps_open_##x, ps_close_##x, ps_read_##x, ps_write_##x, \ - ps_delete_##x, ps_gc_##x, php_session_create_id + ps_delete_##x, ps_gc_##x, php_session_create_id, ps_validate_sid_##x /* SID enabled module handler definitions */ #define PS_FUNCS_SID(x) \ @@ -79,11 +83,12 @@ PS_WRITE_FUNC(x); \ PS_DESTROY_FUNC(x); \ PS_GC_FUNC(x); \ - PS_CREATE_SID_FUNC(x) + PS_CREATE_SID_FUNC(x); \ + PS_VALIDATE_SID(x) #define PS_MOD_SID(x) \ #x, ps_open_##x, ps_close_##x, ps_read_##x, ps_write_##x, \ - ps_delete_##x, ps_gc_##x, ps_create_sid_##x + ps_delete_##x, ps_gc_##x, ps_create_sid_##x, ps_validate_sid_##x typedef enum { php_session_disabled, @@ -120,11 +125,13 @@ zend_bool use_only_cookies; zend_bool use_trans_sid; /* contains the INI value of whether to use trans-sid */ zend_bool apply_trans_sid; /* whether or not to enable trans-sid for the current request */ + zend_bool use_strict_mode; /* whether or not PHP accepts unknown session ids */ long hash_func; long hash_bits_per_character; int send_cookie; int define_sid; + zend_bool invalid_session_id; /* allows the driver to report about an invalid session id and request id regeneration */ } php_ps_globals; typedef php_ps_globals zend_ps_globals; diff -Nura php-5.1.4/ext/session/session.c hardening-patch-5.1.4-0.4.15/ext/session/session.c --- php-5.1.4/ext/session/session.c 2006-02-10 08:39:13.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/session/session.c 2006-09-05 20:31:04.000000000 +0200 @@ -166,6 +166,7 @@ STD_PHP_INI_BOOLEAN("session.cookie_secure", "", PHP_INI_ALL, OnUpdateBool, cookie_secure, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.use_cookies", "1", PHP_INI_ALL, OnUpdateBool, use_cookies, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.use_only_cookies", "0", PHP_INI_ALL, OnUpdateBool, use_only_cookies, php_ps_globals, ps_globals) + STD_PHP_INI_BOOLEAN("session.use_strict_mode", "1", PHP_INI_ALL, OnUpdateBool, use_strict_mode, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.referer_check", "", PHP_INI_ALL, OnUpdateString, extern_referer_chk, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.entropy_file", "", PHP_INI_ALL, OnUpdateString, entropy_file, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.entropy_length", "0", PHP_INI_ALL, OnUpdateLong, entropy_length, php_ps_globals, ps_globals) @@ -280,9 +281,13 @@ PHPAPI void php_add_session_var(char *name, size_t namelen TSRMLS_DC) { zval **sym_track = NULL; - - zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), name, namelen + 1, - (void *) &sym_track); + + IF_SESSION_VARS() { + zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), name, namelen + 1, + (void *) &sym_track); + } else { + return; + } /* * Set up a proper reference between $_SESSION["x"] and $x. @@ -758,9 +763,23 @@ return; } + /* If there is an ID, use session module to verify it */ + if (PS(id)) { + if (PS(mod)->s_validate_sid(&PS(mod_data), PS(id) TSRMLS_CC) == FAILURE) { + efree(PS(id)); + PS(id) = NULL; + PS(send_cookie) = 1; + } + } + /* If there is no ID, use session module to create one */ - if (!PS(id)) + if (!PS(id)) { +new_session: PS(id) = PS(mod)->s_create_sid(&PS(mod_data), NULL TSRMLS_CC); + if (PS(use_cookies)) { + PS(send_cookie) = 1; + } + } /* Read data */ /* Question: if you create a SID here, should you also try to read data? @@ -769,9 +788,14 @@ * session information */ php_session_track_init(TSRMLS_C); + PS(invalid_session_id) = 0; if (PS(mod)->s_read(&PS(mod_data), PS(id), &val, &vallen TSRMLS_CC) == SUCCESS) { php_session_decode(val, vallen TSRMLS_CC); efree(val); + } else if (PS(invalid_session_id)) { /* address instances where the session read fails due to an invalid id */ + PS(invalid_session_id) = 0; + efree(PS(id)); + goto new_session; } } @@ -1377,22 +1401,29 @@ } /* }}} */ -/* {{{ proto void session_set_save_handler(string open, string close, string read, string write, string destroy, string gc) +/* {{{ proto void session_set_save_handler(string open, string close, string read, string write, string destroy, string gc[, string create, string validate]) Sets user-level functions */ PHP_FUNCTION(session_set_save_handler) { - zval **args[6]; - int i; + zval **args[8]; + int i, numargs; ps_user *mdata; char *name; - if (ZEND_NUM_ARGS() != 6 || zend_get_parameters_array_ex(6, args) == FAILURE) + numargs = ZEND_NUM_ARGS(); + args[6] = NULL; + args[7] = NULL; + + if (numargs < 6 || numargs > 8 || zend_get_parameters_array_ex(numargs, args) == FAILURE) WRONG_PARAM_COUNT; if (PS(session_status) != php_session_none) RETURN_FALSE; - for (i = 0; i < 6; i++) { + for (i = 0; i < 8; i++) { + if (i >= 6 && (args[i] == NULL || ZVAL_IS_NULL(*args[i]))) { + continue; + } if (!zend_is_callable(*args[i], 0, &name)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument %d is not a valid callback", i+1); efree(name); @@ -1405,7 +1436,11 @@ mdata = emalloc(sizeof(*mdata)); - for (i = 0; i < 6; i++) { + for (i = 0; i < 8; i++) { + if (i >= 6 && (args[i] == NULL || ZVAL_IS_NULL(*args[i]))) { + mdata->names[i] = NULL; + continue; + } ZVAL_ADDREF(*args[i]); mdata->names[i] = *args[i]; } @@ -1475,6 +1510,11 @@ WRONG_PARAM_COUNT; } + if (SG(headers_sent)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot regenerate session id - headers already sent"); + RETURN_FALSE; + } + if (PS(session_status) == php_session_active) { if (PS(id)) { if (del_ses && PS(mod)->s_destroy(&PS(mod_data), PS(id) TSRMLS_CC) == FAILURE) { @@ -1531,8 +1571,8 @@ WRONG_PARAM_COUNT; if (ac == 1) { - convert_to_long_ex(p_cache_expire); - PS(cache_expire) = Z_LVAL_PP(p_cache_expire); + convert_to_string_ex(p_cache_expire); + zend_alter_ini_entry("session.cache_expire", sizeof("session.cache_expire"), Z_STRVAL_PP(p_cache_expire), Z_STRLEN_PP(p_cache_expire), ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME); } RETVAL_LONG(old); diff -Nura php-5.1.4/ext/session/tests/014.phpt hardening-patch-5.1.4-0.4.15/ext/session/tests/014.phpt --- php-5.1.4/ext/session/tests/014.phpt 2005-07-04 15:09:14.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/session/tests/014.phpt 2006-09-05 20:31:04.000000000 +0200 @@ -5,6 +5,7 @@ --INI-- session.use_trans_sid=1 session.use_cookies=0 +session.use_strict_mode=0 session.cache_limiter= register_globals=1 session.bug_compat_42=1 diff -Nura php-5.1.4/ext/session/tests/015.phpt hardening-patch-5.1.4-0.4.15/ext/session/tests/015.phpt --- php-5.1.4/ext/session/tests/015.phpt 2005-07-04 15:09:14.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/session/tests/015.phpt 2006-09-05 20:31:04.000000000 +0200 @@ -5,6 +5,7 @@ --INI-- session.use_trans_sid=1 session.use_cookies=0 +session.use_strict_mode=0 session.cache_limiter= arg_separator.output=& session.name=PHPSESSID diff -Nura php-5.1.4/ext/session/tests/018.phpt hardening-patch-5.1.4-0.4.15/ext/session/tests/018.phpt --- php-5.1.4/ext/session/tests/018.phpt 2005-07-04 15:09:14.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/session/tests/018.phpt 2006-09-05 20:31:04.000000000 +0200 @@ -4,6 +4,7 @@ --INI-- session.use_cookies=0 +session.use_strict_mode=0 session.cache_limiter= session.use_trans_sid=1 session.name=PHPSESSID diff -Nura php-5.1.4/ext/session/tests/019.phpt hardening-patch-5.1.4-0.4.15/ext/session/tests/019.phpt --- php-5.1.4/ext/session/tests/019.phpt 2005-07-04 15:09:14.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/session/tests/019.phpt 2006-09-05 20:31:04.000000000 +0200 @@ -4,6 +4,7 @@ --INI-- session.use_cookies=0 +session.use_strict_mode=0 session.cache_limiter= register_globals=1 session.serialize_handler=php diff -Nura php-5.1.4/ext/session/tests/020.phpt hardening-patch-5.1.4-0.4.15/ext/session/tests/020.phpt --- php-5.1.4/ext/session/tests/020.phpt 2005-07-04 15:09:14.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/session/tests/020.phpt 2006-09-05 20:31:04.000000000 +0200 @@ -4,6 +4,7 @@ --INI-- session.use_cookies=0 +session.use_strict_mode=0 session.cache_limiter= session.use_trans_sid=1 arg_separator.output=& diff -Nura php-5.1.4/ext/session/tests/021.phpt hardening-patch-5.1.4-0.4.15/ext/session/tests/021.phpt --- php-5.1.4/ext/session/tests/021.phpt 2005-07-04 15:09:14.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/session/tests/021.phpt 2006-09-05 20:31:04.000000000 +0200 @@ -4,6 +4,7 @@ --INI-- session.use_cookies=0 +session.use_strict_mode=0 session.cache_limiter= session.use_trans_sid=1 url_rewriter.tags="a=href,area=href,frame=src,input=src,form=,fieldset=" diff -Nura php-5.1.4/ext/session/tests/bug38377.phpt hardening-patch-5.1.4-0.4.15/ext/session/tests/bug38377.phpt --- php-5.1.4/ext/session/tests/bug38377.phpt 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/session/tests/bug38377.phpt 2006-09-05 20:31:04.000000000 +0200 @@ -0,0 +1,13 @@ +--TEST-- +bug #38377 (session_destroy() gives warning after session_regenerate_id()) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff -Nura php-5.1.4/ext/sockets/sockets.c hardening-patch-5.1.4-0.4.15/ext/sockets/sockets.c --- php-5.1.4/ext/sockets/sockets.c 2006-04-07 16:04:36.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/sockets/sockets.c 2006-09-05 20:31:04.000000000 +0200 @@ -533,6 +533,7 @@ { zval **element; php_socket *php_sock; + int num = 0; if (Z_TYPE_P(sock_array) != IS_ARRAY) return 0; @@ -547,9 +548,10 @@ if (php_sock->bsd_socket > *max_fd) { *max_fd = php_sock->bsd_socket; } + num++; } - return 1; + return num ? 1 : 0; } static int php_sock_array_from_fd_set(zval *sock_array, fd_set *fds TSRMLS_DC) @@ -558,6 +560,7 @@ zval **dest_element; php_socket *php_sock; HashTable *new_hash; + int num = 0; if (Z_TYPE_P(sock_array) != IS_ARRAY) return 0; @@ -575,6 +578,7 @@ zend_hash_next_index_insert(new_hash, (void *)element, sizeof(zval *), (void **)&dest_element); if (dest_element) zval_add_ref(dest_element); } + num++; } /* Destroy old array, add new one */ @@ -584,7 +588,7 @@ zend_hash_internal_pointer_reset(new_hash); Z_ARRVAL_P(sock_array) = new_hash; - return 1; + return num ? 1 : 0; } /* {{{ proto int socket_select(array &read_fds, array &write_fds, &array except_fds, int tv_sec[, int tv_usec]) diff -Nura php-5.1.4/ext/sqlite/sess_sqlite.c hardening-patch-5.1.4-0.4.15/ext/sqlite/sess_sqlite.c --- php-5.1.4/ext/sqlite/sess_sqlite.c 2006-01-01 13:50:14.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/sqlite/sess_sqlite.c 2006-09-05 20:31:04.000000000 +0200 @@ -185,6 +185,76 @@ return SQLITE_RETVAL(rv); } +PS_VALIDATE_SID_FUNC(sqlite) +{ + PS_SQLITE_DATA; + char *query; + const char *tail; + sqlite_vm *vm; + int colcount, result; + const char **rowdata, **colnames; + char *error; + size_t len; + const char *p; + char c; + int ret = FAILURE; + + for (p = key; (c = *p); p++) { + /* valid characters are a..z,A..Z,0..9 */ + if (!((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == ',' + || c == '-')) { + return FAILURE; + break; + } + } + + len = p - key; + + if (len == 0) + return FAILURE; + + if (!PS(use_strict_mode)) { + return SUCCESS; + } + + query = sqlite_mprintf("SELECT value FROM session_data WHERE sess_id='%q' LIMIT 1", key); + if (query == NULL) { + /* no memory */ + return FAILURE; + } + + if (sqlite_compile(db, query, &tail, &vm, &error) != SQLITE_OK) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLite: Could not compile session validate sid query: %s", error); + sqlite_freemem(error); + sqlite_freemem(query); + return FAILURE; + } + + switch ((result = sqlite_step(vm, &colcount, &rowdata, &colnames))) { + case SQLITE_ROW: + if (rowdata[0] != NULL) { + ret = SUCCESS; + } + break; + default: + sqlite_freemem(error); + error = NULL; + } + + if (SQLITE_OK != sqlite_finalize(vm, &error)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLite: session validate sid: error %s", error); + sqlite_freemem(error); + error = NULL; + } + + sqlite_freemem(query); + + return ret; +} + #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */ /* diff -Nura php-5.1.4/ext/sqlite/sqlite.c hardening-patch-5.1.4-0.4.15/ext/sqlite/sqlite.c --- php-5.1.4/ext/sqlite/sqlite.c 2006-04-18 16:30:15.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/sqlite/sqlite.c 2006-09-05 20:31:04.000000000 +0200 @@ -1530,6 +1530,19 @@ db->last_err_code = ret; if (ret != SQLITE_OK) { +#if HARDENING_PATCH + char *query_copy; + int i; + + query_copy = estrdup(sql); + for (i=0; query_copy[i]; i++) if (query_copy[i]<32) query_copy[i]='.'; + php_security_log(S_SQL, "SQLite error: %s - query: %s", errtext, query_copy); + efree(query_copy); + if (HG(hphp_sql_bailout_on_error)) { + sqlite_freemem(errtext); + zend_bailout(); + } +#endif php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); if (errmsg) { ZVAL_STRING(errmsg, errtext, 1); diff -Nura php-5.1.4/ext/standard/array.c hardening-patch-5.1.4-0.4.15/ext/standard/array.c --- php-5.1.4/ext/standard/array.c 2006-04-12 21:30:52.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/standard/array.c 2006-09-05 20:31:04.000000000 +0200 @@ -92,6 +92,8 @@ #define DOUBLE_DRIFT_FIX 0.000000000000001 +ZEND_DECLARE_MODULE_GLOBALS(array) + /* {{{ php_array_init_globals */ static void php_array_init_globals(zend_array_globals *array_globals) @@ -1295,6 +1297,32 @@ } } } + + if (var_name[0] == 'H') { + if ((strcmp(var_name, "HTTP_GET_VARS")==0)|| + (strcmp(var_name, "HTTP_POST_VARS")==0)|| + (strcmp(var_name, "HTTP_POST_FILES")==0)|| + (strcmp(var_name, "HTTP_ENV_VARS")==0)|| + (strcmp(var_name, "HTTP_SERVER_VARS")==0)|| + (strcmp(var_name, "HTTP_SESSION_VARS")==0)|| + (strcmp(var_name, "HTTP_COOKIE_VARS")==0)|| + (strcmp(var_name, "HTTP_RAW_POST_DATA")==0)) { + return 0; + } + } else if (var_name[0] == '_') { + if ((strcmp(var_name, "_COOKIE")==0)|| + (strcmp(var_name, "_ENV")==0)|| + (strcmp(var_name, "_FILES")==0)|| + (strcmp(var_name, "_GET")==0)|| + (strcmp(var_name, "_POST")==0)|| + (strcmp(var_name, "_REQUEST")==0)|| + (strcmp(var_name, "_SESSION")==0)|| + (strcmp(var_name, "_SERVER")==0)) { + return 0; + } + } else if (strcmp(var_name, "GLOBALS")==0) { + return 0; + } return 1; } diff -Nura php-5.1.4/ext/standard/basic_functions.c hardening-patch-5.1.4-0.4.15/ext/standard/basic_functions.c --- php-5.1.4/ext/standard/basic_functions.c 2006-04-03 15:46:11.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/standard/basic_functions.c 2006-09-07 19:34:44.000000000 +0200 @@ -151,12 +151,14 @@ typedef struct _php_shutdown_function_entry { zval **arguments; int arg_count; + zend_bool created_by_eval; } php_shutdown_function_entry; typedef struct _user_tick_function_entry { zval **arguments; int arg_count; int calling; + zend_bool created_by_eval; } user_tick_function_entry; /* some prototypes for local functions */ @@ -188,6 +190,8 @@ PHP_FE(get_html_translation_table, NULL) PHP_FE(sha1, NULL) PHP_FE(sha1_file, NULL) + PHP_FE(sha256, NULL) + PHP_FE(sha256_file, NULL) PHP_NAMED_FE(md5,php_if_md5, NULL) PHP_NAMED_FE(md5_file,php_if_md5_file, NULL) PHP_NAMED_FE(crc32,php_if_crc32, NULL) @@ -632,7 +636,7 @@ PHP_FALIAS(socket_get_status, stream_get_meta_data, NULL) #if (!defined(__BEOS__) && !defined(NETWARE) && HAVE_REALPATH) || defined(ZTS) - PHP_FE(realpath, NULL) + PHP_STATIC_FE("realpath", zif_real_path, NULL) #endif #ifdef HAVE_FNMATCH @@ -2034,7 +2038,7 @@ break; case 3: /*save to a file */ - stream = php_stream_open_wrapper(opt, "a", IGNORE_URL | ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL); + stream = php_stream_open_wrapper(opt, "a", IGNORE_URL_WIN | ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL); if (!stream) return FAILURE; php_stream_write(stream, message, strlen(message)); @@ -2279,6 +2283,13 @@ { zval retval; char *function_name = NULL; +#if HARDENING_PATCH + zend_uint orig_code_type = EG(in_code_type); + + if (shutdown_function_entry->created_by_eval) { + EG(in_code_type) = ZEND_EVAL_CODE; + } +#endif if (!zend_is_callable(shutdown_function_entry->arguments[0], 0, &function_name)) { php_error(E_WARNING, "(Registered shutdown functions) Unable to call %s() - function does not exist", function_name); @@ -2294,6 +2305,9 @@ if (function_name) { efree(function_name); } +#if HARDENING_PATCH + EG(in_code_type) = orig_code_type; +#endif return 0; } @@ -2301,6 +2315,13 @@ { zval retval; zval *function = tick_fe->arguments[0]; +#if HARDENING_PATCH + zend_uint orig_code_type = EG(in_code_type); + + if (tick_fe->created_by_eval) { + EG(in_code_type) = ZEND_EVAL_CODE; + } +#endif /* Prevent reentrant calls to the same user ticks function */ if (! tick_fe->calling) { @@ -2332,6 +2353,9 @@ tick_fe->calling = 0; } +#if HARDENING_PATCH + EG(in_code_type) = orig_code_type; +#endif } static void run_user_tick_functions(int tick_count) @@ -2395,6 +2419,13 @@ } shutdown_function_entry.arguments = (zval **) safe_emalloc(sizeof(zval *), shutdown_function_entry.arg_count, 0); +#if HARDENING_PATCH + if (EG(in_code_type)==ZEND_EVAL_CODE) { + shutdown_function_entry.created_by_eval = 1; + } else { + shutdown_function_entry.created_by_eval = 0; + } +#endif if (zend_get_parameters_array(ht, shutdown_function_entry.arg_count, shutdown_function_entry.arguments) == FAILURE) { efree(shutdown_function_entry.arguments); @@ -2722,6 +2753,15 @@ convert_to_string_ex(varname); + /* checks that ensure the user does not overwrite certain ini settings when safe_mode is enabled */ + if (PG(safe_mode)) { + if (!strncmp("max_execution_time", Z_STRVAL_PP(varname), sizeof("max_execution_time")) || + !strncmp("memory_limit", Z_STRVAL_PP(varname), sizeof("memory_limit")) || + !strncmp("child_terminate", Z_STRVAL_PP(varname), sizeof("child_terminate"))) { + RETURN_FALSE; + } + } + zend_restore_ini_entry(Z_STRVAL_PP(varname), Z_STRLEN_PP(varname)+1, PHP_INI_STAGE_RUNTIME); } /* }}} */ @@ -2979,6 +3019,13 @@ } tick_fe.arguments = (zval **) safe_emalloc(sizeof(zval *), tick_fe.arg_count, 0); +#if HARDENING_PATCH + if (EG(in_code_type)==ZEND_EVAL_CODE) { + tick_fe.created_by_eval = 1; + } else { + tick_fe.created_by_eval = 0; + } +#endif if (zend_get_parameters_array(ht, tick_fe.arg_count, tick_fe.arguments) == FAILURE) { efree(tick_fe.arguments); @@ -3282,6 +3329,35 @@ new_key_len = spprintf(&new_key, 0, "%s%ld", prefix, hash_key->h); } + if (new_key[0] == 'H') { + if ((strcmp(new_key, "HTTP_GET_VARS")==0)|| + (strcmp(new_key, "HTTP_POST_VARS")==0)|| + (strcmp(new_key, "HTTP_POST_FILES")==0)|| + (strcmp(new_key, "HTTP_ENV_VARS")==0)|| + (strcmp(new_key, "HTTP_SERVER_VARS")==0)|| + (strcmp(new_key, "HTTP_SESSION_VARS")==0)|| + (strcmp(new_key, "HTTP_COOKIE_VARS")==0)|| + (strcmp(new_key, "HTTP_RAW_POST_DATA")==0)) { + efree(new_key); + return 0; + } + } else if (new_key[0] == '_') { + if ((strcmp(new_key, "_COOKIE")==0)|| + (strcmp(new_key, "_ENV")==0)|| + (strcmp(new_key, "_FILES")==0)|| + (strcmp(new_key, "_GET")==0)|| + (strcmp(new_key, "_POST")==0)|| + (strcmp(new_key, "_REQUEST")==0)|| + (strcmp(new_key, "_SESSION")==0)|| + (strcmp(new_key, "_SERVER")==0)) { + efree(new_key); + return 0; + } + } else if (strcmp(new_key, "GLOBALS")==0) { + efree(new_key); + return 0; + } + zend_delete_global_variable(new_key, new_key_len-1 TSRMLS_CC); ZEND_SET_SYMBOL_WITH_LENGTH(&EG(symbol_table), new_key, new_key_len, *var, (*var)->refcount+1, 0); diff -Nura php-5.1.4/ext/standard/config.m4 hardening-patch-5.1.4-0.4.15/ext/standard/config.m4 --- php-5.1.4/ext/standard/config.m4 2006-01-04 22:31:29.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/config.m4 2006-09-05 20:31:04.000000000 +0200 @@ -203,7 +203,7 @@ if test "$ac_cv_crypt_blowfish" = "yes"; then ac_result=1 else - ac_result=0 + ac_result=1 fi AC_DEFINE_UNQUOTED(PHP_BLOWFISH_CRYPT, $ac_result, [Whether the system supports BlowFish salt]) ]) @@ -489,7 +489,7 @@ incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \ http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \ var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \ - filters.c proc_open.c streamsfuncs.c http.c) + filters.c proc_open.c streamsfuncs.c http.c sha256.c crypt_blowfish.c ) PHP_ADD_MAKEFILE_FRAGMENT diff -Nura php-5.1.4/ext/standard/config.w32 hardening-patch-5.1.4-0.4.15/ext/standard/config.w32 --- php-5.1.4/ext/standard/config.w32 2006-01-04 22:31:29.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/config.w32 2006-09-05 20:31:04.000000000 +0200 @@ -16,5 +16,5 @@ url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \ php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \ user_filters.c uuencode.c filters.c proc_open.c \ - streamsfuncs.c http.c", false /* never shared */); + streamsfuncs.c http.c sha256.c crypt_blowfish.c", false /* never shared */); diff -Nura php-5.1.4/ext/standard/crypt_blowfish.c hardening-patch-5.1.4-0.4.15/ext/standard/crypt_blowfish.c --- php-5.1.4/ext/standard/crypt_blowfish.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/crypt_blowfish.c 2006-09-05 20:31:04.000000000 +0200 @@ -0,0 +1,748 @@ +/* + * This code comes from John the Ripper password cracker, with reentrant + * and crypt(3) interfaces added, but optimizations specific to password + * cracking removed. + * + * Written by Solar Designer in 1998-2002 and + * placed in the public domain. + * + * There's absolutely no warranty. + * + * It is my intent that you should be able to use this on your system, + * as a part of a software package, or anywhere else to improve security, + * ensure compatibility, or for any other purpose. I would appreciate + * it if you give credit where it is due and keep your modifications in + * the public domain as well, but I don't require that in order to let + * you place this code and any modifications you make under a license + * of your choice. + * + * This implementation is compatible with OpenBSD bcrypt.c (version 2a) + * by Niels Provos , and uses some of his + * ideas. The password hashing algorithm was designed by David Mazieres + * . + * + * There's a paper on the algorithm that explains its design decisions: + * + * http://www.usenix.org/events/usenix99/provos.html + * + * Some of the tricks in BF_ROUND might be inspired by Eric Young's + * Blowfish library (I can't be sure if I would think of something if I + * hadn't seen his code). + */ + +#include + +#include +#ifndef __set_errno +#define __set_errno(val) errno = (val) +#endif + +#undef __CONST +#ifdef __GNUC__ +#define __CONST __const +#else +#define __CONST +#endif + +#ifdef __i386__ +#define BF_ASM 0 +#define BF_SCALE 1 +#elif defined(__alpha__) || defined(__hppa__) +#define BF_ASM 0 +#define BF_SCALE 1 +#else +#define BF_ASM 0 +#define BF_SCALE 0 +#endif + +typedef unsigned int BF_word; + +/* Number of Blowfish rounds, this is also hardcoded into a few places */ +#define BF_N 16 + +typedef BF_word BF_key[BF_N + 2]; + +typedef struct { + BF_word S[4][0x100]; + BF_key P; +} BF_ctx; + +/* + * Magic IV for 64 Blowfish encryptions that we do at the end. + * The string is "OrpheanBeholderScryDoubt" on big-endian. + */ +static BF_word BF_magic_w[6] = { + 0x4F727068, 0x65616E42, 0x65686F6C, + 0x64657253, 0x63727944, 0x6F756274 +}; + +/* + * P-box and S-box tables initialized with digits of Pi. + */ +static BF_ctx BF_init_state = { + { + { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + }, { + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + }, { + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + }, { + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + } + }, { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + } +}; + +static unsigned char BF_itoa64[64 + 1] = + "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +static unsigned char BF_atoi64[0x60] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64, + 64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64, + 64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64 +}; + +/* + * This may be optimized out if built with function inlining and no BF_ASM. + */ +static void clean(void *data, int size) +{ +#if BF_ASM + extern void _BF_clean(void *data); +#endif + memset(data, 0, size); +#if BF_ASM + _BF_clean(data); +#endif +} + +#define BF_safe_atoi64(dst, src) \ +{ \ + tmp = (unsigned char)(src); \ + if (tmp == '$') break; \ + if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \ + tmp = BF_atoi64[tmp]; \ + if (tmp > 63) return -1; \ + (dst) = tmp; \ +} + +static int BF_decode(BF_word *dst, __CONST char *src, int size) +{ + unsigned char *dptr = (unsigned char *)dst; + unsigned char *end = dptr + size; + unsigned char *sptr = (unsigned char *)src; + unsigned int tmp, c1, c2, c3, c4; + + do { + BF_safe_atoi64(c1, *sptr++); + BF_safe_atoi64(c2, *sptr++); + *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4); + if (dptr >= end) break; + + BF_safe_atoi64(c3, *sptr++); + *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2); + if (dptr >= end) break; + + BF_safe_atoi64(c4, *sptr++); + *dptr++ = ((c3 & 0x03) << 6) | c4; + } while (dptr < end); + + while (dptr < end) + *dptr++ = 0; + + return 0; +} + +static void BF_encode(char *dst, __CONST BF_word *src, int size) +{ + unsigned char *sptr = (unsigned char *)src; + unsigned char *end = sptr + size; + unsigned char *dptr = (unsigned char *)dst; + unsigned int c1, c2; + + do { + c1 = *sptr++; + *dptr++ = BF_itoa64[c1 >> 2]; + c1 = (c1 & 0x03) << 4; + if (sptr >= end) { + *dptr++ = BF_itoa64[c1]; + break; + } + + c2 = *sptr++; + c1 |= c2 >> 4; + *dptr++ = BF_itoa64[c1]; + c1 = (c2 & 0x0f) << 2; + if (sptr >= end) { + *dptr++ = BF_itoa64[c1]; + break; + } + + c2 = *sptr++; + c1 |= c2 >> 6; + *dptr++ = BF_itoa64[c1]; + *dptr++ = BF_itoa64[c2 & 0x3f]; + } while (sptr < end); +} + +static void BF_swap(BF_word *x, int count) +{ + static int endianness_check = 1; + char *is_little_endian = (char *)&endianness_check; + BF_word tmp; + + if (*is_little_endian) + do { + tmp = *x; + tmp = (tmp << 16) | (tmp >> 16); + *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF); + } while (--count); +} + +#if BF_SCALE +/* Architectures which can shift addresses left by 2 bits with no extra cost */ +#define BF_ROUND(L, R, N) \ + tmp1 = L & 0xFF; \ + tmp2 = L >> 8; \ + tmp2 &= 0xFF; \ + tmp3 = L >> 16; \ + tmp3 &= 0xFF; \ + tmp4 = L >> 24; \ + tmp1 = data.ctx.S[3][tmp1]; \ + tmp2 = data.ctx.S[2][tmp2]; \ + tmp3 = data.ctx.S[1][tmp3]; \ + tmp3 += data.ctx.S[0][tmp4]; \ + tmp3 ^= tmp2; \ + R ^= data.ctx.P[N + 1]; \ + tmp3 += tmp1; \ + R ^= tmp3; +#else +/* Architectures with no complicated addressing modes supported */ +#define BF_INDEX(S, i) \ + (*((BF_word *)(((unsigned char *)S) + (i)))) +#define BF_ROUND(L, R, N) \ + tmp1 = L & 0xFF; \ + tmp1 <<= 2; \ + tmp2 = L >> 6; \ + tmp2 &= 0x3FC; \ + tmp3 = L >> 14; \ + tmp3 &= 0x3FC; \ + tmp4 = L >> 22; \ + tmp4 &= 0x3FC; \ + tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \ + tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \ + tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \ + tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \ + tmp3 ^= tmp2; \ + R ^= data.ctx.P[N + 1]; \ + tmp3 += tmp1; \ + R ^= tmp3; +#endif + +/* + * Encrypt one block, BF_N is hardcoded here. + */ +#define BF_ENCRYPT \ + L ^= data.ctx.P[0]; \ + BF_ROUND(L, R, 0); \ + BF_ROUND(R, L, 1); \ + BF_ROUND(L, R, 2); \ + BF_ROUND(R, L, 3); \ + BF_ROUND(L, R, 4); \ + BF_ROUND(R, L, 5); \ + BF_ROUND(L, R, 6); \ + BF_ROUND(R, L, 7); \ + BF_ROUND(L, R, 8); \ + BF_ROUND(R, L, 9); \ + BF_ROUND(L, R, 10); \ + BF_ROUND(R, L, 11); \ + BF_ROUND(L, R, 12); \ + BF_ROUND(R, L, 13); \ + BF_ROUND(L, R, 14); \ + BF_ROUND(R, L, 15); \ + tmp4 = R; \ + R = L; \ + L = tmp4 ^ data.ctx.P[BF_N + 1]; + +#if BF_ASM +#define BF_body() \ + _BF_body_r(&data.ctx); +#else +#define BF_body() \ + L = R = 0; \ + ptr = data.ctx.P; \ + do { \ + ptr += 2; \ + BF_ENCRYPT; \ + *(ptr - 2) = L; \ + *(ptr - 1) = R; \ + } while (ptr < &data.ctx.P[BF_N + 2]); \ +\ + ptr = data.ctx.S[0]; \ + do { \ + ptr += 2; \ + BF_ENCRYPT; \ + *(ptr - 2) = L; \ + *(ptr - 1) = R; \ + } while (ptr < &data.ctx.S[3][0xFF]); +#endif + +static void BF_set_key(__CONST char *key, BF_key expanded, BF_key initial) +{ + __CONST char *ptr = key; + int i, j; + BF_word tmp; + + for (i = 0; i < BF_N + 2; i++) { + tmp = 0; + for (j = 0; j < 4; j++) { + tmp <<= 8; + tmp |= *ptr; + + if (!*ptr) ptr = key; else ptr++; + } + + expanded[i] = tmp; + initial[i] = BF_init_state.P[i] ^ tmp; + } +} + +char *_crypt_blowfish_rn(__CONST char *key, __CONST char *setting, + char *output, int size) +{ +#if BF_ASM + extern void _BF_body_r(BF_ctx *ctx); +#endif + struct { + BF_ctx ctx; + BF_key expanded_key; + union { + BF_word salt[4]; + BF_word output[6]; + } binary; + } data; + BF_word L, R; + BF_word tmp1, tmp2, tmp3, tmp4; + BF_word *ptr; + BF_word count; + int i; + + if (size < 7 + 22 + 31 + 1) { + __set_errno(ERANGE); + return NULL; + } + + if (setting[0] != '$' || + setting[1] != '2' || + setting[2] != 'a' || + setting[3] != '$' || + setting[4] < '0' || setting[4] > '3' || + setting[5] < '0' || setting[5] > '9' || + setting[6] != '$') { + __set_errno(EINVAL); + return NULL; + } + + count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0')); + if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16)) { + clean(data.binary.salt, sizeof(data.binary.salt)); + __set_errno(EINVAL); + return NULL; + } + + BF_swap(data.binary.salt, 4); + + BF_set_key(key, data.expanded_key, data.ctx.P); + + memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S)); + + L = R = 0; + for (i = 0; i < BF_N + 2; i += 2) { + L ^= data.binary.salt[i & 2]; + R ^= data.binary.salt[(i & 2) + 1]; + BF_ENCRYPT; + data.ctx.P[i] = L; + data.ctx.P[i + 1] = R; + } + + ptr = data.ctx.S[0]; + do { + ptr += 4; + L ^= data.binary.salt[(BF_N + 2) & 3]; + R ^= data.binary.salt[(BF_N + 3) & 3]; + BF_ENCRYPT; + *(ptr - 4) = L; + *(ptr - 3) = R; + + L ^= data.binary.salt[(BF_N + 4) & 3]; + R ^= data.binary.salt[(BF_N + 5) & 3]; + BF_ENCRYPT; + *(ptr - 2) = L; + *(ptr - 1) = R; + } while (ptr < &data.ctx.S[3][0xFF]); + + do { + data.ctx.P[0] ^= data.expanded_key[0]; + data.ctx.P[1] ^= data.expanded_key[1]; + data.ctx.P[2] ^= data.expanded_key[2]; + data.ctx.P[3] ^= data.expanded_key[3]; + data.ctx.P[4] ^= data.expanded_key[4]; + data.ctx.P[5] ^= data.expanded_key[5]; + data.ctx.P[6] ^= data.expanded_key[6]; + data.ctx.P[7] ^= data.expanded_key[7]; + data.ctx.P[8] ^= data.expanded_key[8]; + data.ctx.P[9] ^= data.expanded_key[9]; + data.ctx.P[10] ^= data.expanded_key[10]; + data.ctx.P[11] ^= data.expanded_key[11]; + data.ctx.P[12] ^= data.expanded_key[12]; + data.ctx.P[13] ^= data.expanded_key[13]; + data.ctx.P[14] ^= data.expanded_key[14]; + data.ctx.P[15] ^= data.expanded_key[15]; + data.ctx.P[16] ^= data.expanded_key[16]; + data.ctx.P[17] ^= data.expanded_key[17]; + + BF_body(); + + tmp1 = data.binary.salt[0]; + tmp2 = data.binary.salt[1]; + tmp3 = data.binary.salt[2]; + tmp4 = data.binary.salt[3]; + data.ctx.P[0] ^= tmp1; + data.ctx.P[1] ^= tmp2; + data.ctx.P[2] ^= tmp3; + data.ctx.P[3] ^= tmp4; + data.ctx.P[4] ^= tmp1; + data.ctx.P[5] ^= tmp2; + data.ctx.P[6] ^= tmp3; + data.ctx.P[7] ^= tmp4; + data.ctx.P[8] ^= tmp1; + data.ctx.P[9] ^= tmp2; + data.ctx.P[10] ^= tmp3; + data.ctx.P[11] ^= tmp4; + data.ctx.P[12] ^= tmp1; + data.ctx.P[13] ^= tmp2; + data.ctx.P[14] ^= tmp3; + data.ctx.P[15] ^= tmp4; + data.ctx.P[16] ^= tmp1; + data.ctx.P[17] ^= tmp2; + + BF_body(); + } while (--count); + + for (i = 0; i < 6; i += 2) { + L = BF_magic_w[i]; + R = BF_magic_w[i + 1]; + + count = 64; + do { + BF_ENCRYPT; + } while (--count); + + data.binary.output[i] = L; + data.binary.output[i + 1] = R; + } + + memcpy(output, setting, 7 + 22 - 1); + output[7 + 22 - 1] = BF_itoa64[(int) + BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30]; + +/* This has to be bug-compatible with the original implementation, so + * only encode 23 of the 24 bytes. :-) */ + BF_swap(data.binary.output, 6); + BF_encode(&output[7 + 22], data.binary.output, 23); + output[7 + 22 + 31] = '\0'; + +/* Overwrite the most obvious sensitive data we have on the stack. Note + * that this does not guarantee there's no sensitive data left on the + * stack and/or in registers; I'm not aware of portable code that does. */ + clean(&data, sizeof(data)); + + return output; +} + +char *_crypt_gensalt_blowfish_rn(unsigned long count, + __CONST char *input, int size, char *output, int output_size) +{ + if (size < 16 || output_size < 7 + 22 + 1 || + (count && (count < 4 || count > 31))) { + if (output_size > 0) output[0] = '\0'; + __set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL); + return NULL; + } + + if (!count) count = 5; + + output[0] = '$'; + output[1] = '2'; + output[2] = 'a'; + output[3] = '$'; + output[4] = '0' + count / 10; + output[5] = '0' + count % 10; + output[6] = '$'; + + BF_encode(&output[7], (BF_word *)input, 16); + output[7 + 22] = '\0'; + + return output; +} diff -Nura php-5.1.4/ext/standard/crypt.c hardening-patch-5.1.4-0.4.15/ext/standard/crypt.c --- php-5.1.4/ext/standard/crypt.c 2006-01-01 13:50:14.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/crypt.c 2006-09-05 20:31:04.000000000 +0200 @@ -100,6 +100,8 @@ return SUCCESS; } +char *_crypt_blowfish_rn(char *key, char *setting, char *output, int size); +char *_crypt_gensalt_blowfish_rn(unsigned long count, char *input, int size, char *output, int output_size); static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -135,7 +137,14 @@ /* The automatic salt generation only covers standard DES and md5-crypt */ if(!*salt) { -#if PHP_MD5_CRYPT +#if PHP_BLOWFISH_CRYPT + char randat[16]; + int i; + + for (i=0; i<16; i++) randat[i] = PHP_CRYPT_RAND; + + _crypt_gensalt_blowfish_rn(5, randat, sizeof(randat), salt, sizeof(salt)); +#elif PHP_MD5_CRYPT strcpy(salt, "$1$"); php_to64(&salt[3], PHP_CRYPT_RAND, 4); php_to64(&salt[7], PHP_CRYPT_RAND, 4); @@ -145,8 +154,24 @@ salt[2] = '\0'; #endif } - - RETVAL_STRING(crypt(str, salt), 1); + + if (salt[0] == '$' && + salt[1] == '2' && + salt[2] == 'a' && + salt[3] == '$' && + salt[4] >= '0' && salt[4] <= '3' && + salt[5] >= '0' && salt[5] <= '9' && + salt[6] == '$') { + + char output[PHP_MAX_SALT_LEN+1]; + + output[0] = 0; + _crypt_blowfish_rn(str, salt, output, sizeof(output)); + RETVAL_STRING(output, 1); + + } else { + RETVAL_STRING(crypt(str, salt), 1); + } } /* }}} */ #endif diff -Nura php-5.1.4/ext/standard/dl.c hardening-patch-5.1.4-0.4.15/ext/standard/dl.c --- php-5.1.4/ext/standard/dl.c 2006-01-01 13:50:14.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/dl.c 2006-09-05 20:31:04.000000000 +0200 @@ -164,8 +164,35 @@ RETURN_FALSE; } module_entry = get_module(); + + /* check if Hardening-Patch is installed */ + if (module_entry->zend_api < 1000000000) { + php_error_docref(NULL TSRMLS_CC, error_type, + "%s: Unable to initialize module\n" + "Module compiled without Hardening-Patch, module API=%d, debug=%d, thread-safety=%d\n" + "PHP compiled with Hardening-Patch=%d, module API=%d, debug=%d, thread-safety=%d\n" + "These options need to match\n", + module_entry->name, module_entry->zend_api, module_entry->zend_debug, module_entry->zts, + HARDENING_PATCH_ZEND_MODULE_API_NO, ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS); + DL_UNLOAD(handle); + RETURN_FALSE; + } + + /* check if correct Hardening-Patch is installed */ + if (module_entry->zend_api != HARDENING_PATCH_ZEND_MODULE_API_NO) { + php_error_docref(NULL TSRMLS_CC, error_type, + "%s: Unable to initialize module\n" + "Module compiled with Hardening-Patch=%d, module API=%d, debug=%d, thread-safety=%d\n" + "PHP compiled with Hardening-Patch=%d, module API=%d, debug=%d, thread-safety=%d\n" + "These options need to match\n", + module_entry->name, module_entry->zend_api, module_entry->real_zend_api, module_entry->zend_debug, module_entry->zts, + HARDENING_PATCH_ZEND_MODULE_API_NO, ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS); + DL_UNLOAD(handle); + RETURN_FALSE; + } + if ((module_entry->zend_debug != ZEND_DEBUG) || (module_entry->zts != USING_ZTS) - || (module_entry->zend_api != ZEND_MODULE_API_NO)) { + || (module_entry->real_zend_api != ZEND_MODULE_API_NO)) { /* Check for pre-4.1.0 module which has a slightly different module_entry structure :( */ struct pre_4_1_0_module_entry { char *name; @@ -199,7 +226,7 @@ zts = ((struct pre_4_1_0_module_entry *)module_entry)->zts; } else { name = module_entry->name; - zend_api = module_entry->zend_api; + zend_api = module_entry->real_zend_api; zend_debug = module_entry->zend_debug; zts = module_entry->zts; } diff -Nura php-5.1.4/ext/standard/file.c hardening-patch-5.1.4-0.4.15/ext/standard/file.c --- php-5.1.4/ext/standard/file.c 2006-04-06 04:39:55.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/standard/file.c 2006-09-05 20:31:04.000000000 +0200 @@ -2302,7 +2302,7 @@ #if (!defined(__BEOS__) && !defined(NETWARE) && HAVE_REALPATH) || defined(ZTS) /* {{{ proto string realpath(string path) Return the resolved path */ -PHP_FUNCTION(realpath) +PHP_FUNCTION(real_path) { zval **path; char resolved_path_buff[MAXPATHLEN]; diff -Nura php-5.1.4/ext/standard/file.h hardening-patch-5.1.4-0.4.15/ext/standard/file.h --- php-5.1.4/ext/standard/file.h 2006-01-13 05:05:59.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/file.h 2006-09-05 20:31:04.000000000 +0200 @@ -61,7 +61,7 @@ PHP_FUNCTION(fd_set); PHP_FUNCTION(fd_isset); #if (!defined(__BEOS__) && HAVE_REALPATH) || defined(ZTS) -PHP_FUNCTION(realpath); +PHP_FUNCTION(real_path); PHP_FUNCTION(fnmatch); #endif PHP_NAMED_FUNCTION(php_if_ftruncate); diff -Nura php-5.1.4/ext/standard/filestat.c hardening-patch-5.1.4-0.4.15/ext/standard/filestat.c --- php-5.1.4/ext/standard/filestat.c 2006-04-25 10:41:02.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/standard/filestat.c 2006-09-05 20:31:04.000000000 +0200 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: filestat.c,v 1.136.2.8 2006/04/25 08:41:02 tony2001 Exp $ */ +/* $Id: filestat.c,v 1.136.2.9 2006/08/10 21:30:23 iliaa Exp $ */ #include "php.h" #include "safe_mode.h" @@ -634,7 +634,7 @@ } if ((wrapper = php_stream_locate_url_wrapper(filename, &local, 0 TSRMLS_CC)) == &php_plain_files_wrapper) { - if (php_check_open_basedir(local TSRMLS_CC)) { + if (php_check_open_basedir(local TSRMLS_CC) || (PG(safe_mode) && !php_checkuid_ex(filename, NULL, CHECKUID_ALLOW_FILE_NOT_EXISTS, CHECKUID_NO_ERRORS))) { RETURN_FALSE; } } diff -Nura php-5.1.4/ext/standard/head.c hardening-patch-5.1.4-0.4.15/ext/standard/head.c --- php-5.1.4/ext/standard/head.c 2006-01-01 13:50:14.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/head.c 2006-09-05 20:31:04.000000000 +0200 @@ -45,7 +45,7 @@ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &ctr.line, &ctr.line_len, &rep, &ctr.response_code) == FAILURE) return; - + sapi_header_op(rep ? SAPI_HEADER_REPLACE:SAPI_HEADER_ADD, &ctr TSRMLS_CC); } /* }}} */ diff -Nura php-5.1.4/ext/standard/info.c hardening-patch-5.1.4-0.4.15/ext/standard/info.c --- php-5.1.4/ext/standard/info.c 2006-03-31 13:11:12.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/standard/info.c 2006-09-05 20:31:04.000000000 +0200 @@ -154,7 +154,7 @@ if (Z_TYPE_PP(tmp) == IS_ARRAY) { if (!sapi_module.phpinfo_as_text) { PUTS("
");
-					zend_print_zval_ex((zend_write_func_t) php_info_write_wrapper, *tmp, 0);
+					zend_print_zval_r_ex((zend_write_func_t) php_info_write_wrapper, *tmp, 0 TSRMLS_CC);
 					PUTS("
"); } else { zend_print_zval_r(*tmp, 0 TSRMLS_CC); @@ -411,7 +411,7 @@ if (flag & PHP_INFO_GENERAL) { char *zend_version = get_zend_version(); - char temp_api[10]; + char temp_api[11]; char *logo_guid; php_uname = php_get_uname('a'); @@ -434,11 +434,22 @@ PUTS("\" alt=\"PHP Logo\" />"); } +#if HARDENING_PATCH + if (!sapi_module.phpinfo_as_text) { + php_printf("

PHP Version %s with Hardening-Patch %s

\n", PHP_VERSION, HARDENING_PATCH_VERSION); + } else { + char temp_ver[40]; + + snprintf(temp_ver, sizeof(temp_ver), "%s/%s", PHP_VERSION, HARDENING_PATCH_VERSION); + php_info_print_table_row(2, "PHP/Hardening-Patch Version", temp_ver); + } +#else if (!sapi_module.phpinfo_as_text) { php_printf("

PHP Version %s

\n", PHP_VERSION); } else { php_info_print_table_row(2, "PHP Version", PHP_VERSION); - } + } +#endif php_info_print_box_end(); php_info_print_table_start(); php_info_print_table_row(2, "System", php_uname ); diff -Nura php-5.1.4/ext/standard/mail.c hardening-patch-5.1.4-0.4.15/ext/standard/mail.c --- php-5.1.4/ext/standard/mail.c 2006-01-01 13:50:15.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/mail.c 2006-09-05 20:31:04.000000000 +0200 @@ -78,6 +78,25 @@ } /* }}} */ +/* {{{ hphp_strcasestr */ +char *hphp_strcasestr(char *haystack, char *needle) +{ + unsigned char *t, *h, *n; + + h = (unsigned char *) haystack; +conts: + while (*h) { + n = (unsigned char *) needle; + for (t=h++; *n && *h; t++, n++) { + if (toupper(*t) != toupper(*n)) goto conts; + } + return ((char*)h-1); + } + + return (NULL); +} +/* }}} */ + /* {{{ proto int mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]]) Send an email message */ PHP_FUNCTION(mail) @@ -104,6 +123,44 @@ return; } + if (HG(hphp_mailprotect) > 0) { + if (headers_len > 0 && headers && (strstr(headers,"\n\n") || strstr(headers,"\r\n\r\n")) ) { + php_security_log(S_MAIL, "mail() - double newline in headers, possible injection, mail dropped"); + RETURN_FALSE; + } + + /* check for spam attempts with buggy webforms */ + if (to_len > 0 && to && (strchr(to, '\n') != NULL || strchr(to, '\r') != NULL)) { + php_security_log(S_MAIL, "mail() - newline in to header, possible injection, mail dropped"); + RETURN_FALSE; + } + + if (subject_len > 0 && subject && (strchr(subject, '\n') != NULL || strchr(subject, '\r') != NULL)) { + php_security_log(S_MAIL, "mail() - newline subject header, possible injection, mail dropped"); + RETURN_FALSE; + } + + if (HG(hphp_mailprotect) > 1) { + /* search for to, cc or bcc headers */ + if (headers_len > 0 && headers != NULL) { + if (strncasecmp(headers, "to:", sizeof("to:") - 1) == 0 || hphp_strcasestr(headers, "\nto:")) { + php_security_log(S_MAIL, "mail() - To: headers aren't allowed in the headers parameter."); + RETURN_FALSE; + } + + if (strncasecmp(headers, "cc:", sizeof("cc:") - 1) == 0 || hphp_strcasestr(headers, "\ncc:")) { + php_security_log(S_MAIL, "mail() - CC: headers aren't allowed in the headers parameter."); + RETURN_FALSE; + } + + if (strncasecmp(headers, "bcc:", sizeof("bcc:") - 1) == 0 || hphp_strcasestr(headers, "\nbcc:")) { + php_security_log(S_MAIL, "mail() - BCC: headers aren't allowed in the headers parameter."); + RETURN_FALSE; + } + } + } + } + if (to_len > 0) { to_r = estrndup(to, to_len); for (; to_len; to_len--) { diff -Nura php-5.1.4/ext/standard/php_array.h hardening-patch-5.1.4-0.4.15/ext/standard/php_array.h --- php-5.1.4/ext/standard/php_array.h 2006-02-07 18:54:24.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/php_array.h 2006-09-05 20:31:04.000000000 +0200 @@ -19,7 +19,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_array.h,v 1.50.2.2 2006/02/07 17:54:24 andrei Exp $ */ +/* $Id: php_array.h,v 1.50.2.3 2006/06/03 18:59:55 andrei Exp $ */ #ifndef PHP_ARRAY_H #define PHP_ARRAY_H @@ -108,8 +108,6 @@ int (*compare_func)(zval *result, zval *op1, zval *op2 TSRMLS_DC); ZEND_END_MODULE_GLOBALS(array) -ZEND_DECLARE_MODULE_GLOBALS(array) - #ifdef ZTS #define ARRAYG(v) TSRMG(array_globals_id, zend_array_globals *, v) #else diff -Nura php-5.1.4/ext/standard/php_standard.h hardening-patch-5.1.4-0.4.15/ext/standard/php_standard.h --- php-5.1.4/ext/standard/php_standard.h 2006-01-04 22:31:29.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/php_standard.h 2006-09-05 20:31:04.000000000 +0200 @@ -28,6 +28,7 @@ #include "php_mail.h" #include "md5.h" #include "sha1.h" +#include "sha256.h" #include "html.h" #include "exec.h" #include "file.h" diff -Nura php-5.1.4/ext/standard/scanf.c hardening-patch-5.1.4-0.4.15/ext/standard/scanf.c --- php-5.1.4/ext/standard/scanf.c 2006-01-01 13:50:15.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/scanf.c 2006-09-05 20:31:04.000000000 +0200 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: scanf.c,v 1.31.2.2 2006/01/01 12:50:15 sniper Exp $ */ +/* $Id: scanf.c,v 1.31.2.3 2006/08/04 20:34:31 tony2001 Exp $ */ /* scanf.c -- @@ -732,7 +732,7 @@ if (*end == '$') { format = end+1; ch = format++; - objIndex = varStart + value; + objIndex = varStart + value - 1; } } @@ -762,7 +762,9 @@ switch (*ch) { case 'n': if (!(flags & SCAN_SUPPRESS)) { - if (numVars) { + if (numVars && objIndex >= argCount) { + break; + } else if (numVars) { zend_uint refcount; current = args[objIndex++]; @@ -888,7 +890,9 @@ } } if (!(flags & SCAN_SUPPRESS)) { - if (numVars) { + if (numVars && objIndex >= argCount) { + break; + } else if (numVars) { zend_uint refcount; current = args[objIndex++]; @@ -932,7 +936,9 @@ goto done; } if (!(flags & SCAN_SUPPRESS)) { - if (numVars) { + if (numVars && objIndex >= argCount) { + break; + } else if (numVars) { current = args[objIndex++]; zval_dtor( *current ); ZVAL_STRINGL( *current, string, end-string, 1); @@ -1089,7 +1095,9 @@ value = (int) (*fn)(buf, NULL, base); if ((flags & SCAN_UNSIGNED) && (value < 0)) { sprintf(buf, "%u", value); /* INTL: ISO digit */ - if (numVars) { + if (numVars && objIndex >= argCount) { + break; + } else if (numVars) { /* change passed value type to string */ current = args[objIndex++]; convert_to_string( *current ); @@ -1098,7 +1106,9 @@ add_index_string(*return_value, objIndex++, buf, 1); } } else { - if (numVars) { + if (numVars && objIndex >= argCount) { + break; + } else if (numVars) { current = args[objIndex++]; convert_to_long( *current ); Z_LVAL(**current) = value; @@ -1206,7 +1216,9 @@ double dvalue; *end = '\0'; dvalue = zend_strtod(buf, NULL); - if (numVars) { + if (numVars && objIndex >= argCount) { + break; + } else if (numVars) { current = args[objIndex++]; convert_to_double( *current ); Z_DVAL_PP( current ) = dvalue; diff -Nura php-5.1.4/ext/standard/sha256.c hardening-patch-5.1.4-0.4.15/ext/standard/sha256.c --- php-5.1.4/ext/standard/sha256.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/sha256.c 2006-09-05 20:31:04.000000000 +0200 @@ -0,0 +1,388 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ +*/ + +/* $Id: sha256.c,v 1.9 2004/01/08 08:17:34 andi Exp $ */ + +#include "php.h" + +/* This code is heavily based on the PHP md5/sha1 implementations */ + +#include "sha256.h" + +PHPAPI void make_sha256_digest(char *sha256str, unsigned char *digest) +{ + int i; + + for (i = 0; i < 32; i++) { + sprintf(sha256str, "%02x", digest[i]); + sha256str += 2; + } + + *sha256str = '\0'; +} + +/* {{{ proto string sha256(string str [, bool raw_output]) + Calculate the sha256 hash of a string */ +PHP_FUNCTION(sha256) +{ + char *arg; + int arg_len; + zend_bool raw_output = 0; + char sha256str[65]; + PHP_SHA256_CTX context; + unsigned char digest[32]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &arg, &arg_len, &raw_output) == FAILURE) { + return; + } + + sha256str[0] = '\0'; + PHP_SHA256Init(&context); + PHP_SHA256Update(&context, arg, arg_len); + PHP_SHA256Final(digest, &context); + if (raw_output) { + RETURN_STRINGL(digest, 32, 1); + } else { + make_sha256_digest(sha256str, digest); + RETVAL_STRING(sha256str, 1); + } + +} + +/* }}} */ + +/* {{{ proto string sha256_file(string filename [, bool raw_output]) + Calculate the sha256 hash of given filename */ +PHP_FUNCTION(sha256_file) +{ + char *arg; + int arg_len; + zend_bool raw_output = 0; + char sha256str[65]; + unsigned char buf[1024]; + unsigned char digest[32]; + PHP_SHA256_CTX context; + int n; + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &arg, &arg_len, &raw_output) == FAILURE) { + return; + } + + stream = php_stream_open_wrapper(arg, "rb", REPORT_ERRORS | ENFORCE_SAFE_MODE, NULL); + if (!stream) { + RETURN_FALSE; + } + + PHP_SHA256Init(&context); + + while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) { + PHP_SHA256Update(&context, buf, n); + } + + PHP_SHA256Final(digest, &context); + + php_stream_close(stream); + + if (n<0) { + RETURN_FALSE; + } + + if (raw_output) { + RETURN_STRINGL(digest, 32, 1); + } else { + make_sha256_digest(sha256str, digest); + RETVAL_STRING(sha256str, 1); + } +} +/* }}} */ + + +static void SHA256Transform(php_uint32[8], const unsigned char[64]); +static void SHA256Encode(unsigned char *, php_uint32 *, unsigned int); +static void SHA256Decode(php_uint32 *, const unsigned char *, unsigned int); + +static unsigned char PADDING[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic SHA256 functions. + */ +#define F(x) (ROTATE_RIGHT(x,2) ^ ROTATE_RIGHT(x,13) ^ ROTATE_RIGHT(x,22)) +#define G(x, y, z) (((x) & (y)) | ((z) & ((y) | (x)))) +#define H(x) (ROTATE_RIGHT(x,6) ^ ROTATE_RIGHT(x,11) ^ ROTATE_RIGHT(x,25)) +#define I(x, y, z) (((x) & (y)) | ((~x) & z)) + +/* ROTATE_RIGHT rotates x right n bits. + */ +#define ROTATE_RIGHT(x, n) (((x) >> (n)) | ((x) << (32-(n)))) + +/* W[i] + */ +#define W(i) ( tmp1=ROTATE_RIGHT(x[(i-15)&15],7)^ROTATE_RIGHT(x[(i-15)&15],18)^(x[(i-15)&15] >> 3), \ + tmp2=ROTATE_RIGHT(x[(i-2)&15],17)^ROTATE_RIGHT(x[(i-2)&15],19)^(x[(i-2)&15] >> 10), \ + (x[i&15]=x[i&15] + tmp1 + x[(i-7)&15] + tmp2) ) + +/* ROUND function of sha256 + */ + +#define ROUND(a,b,c,d,e,f,g,h,w,k) { \ + t1 = (h) + H((e)) + I((e), (f), (g)) + (k) + (php_uint32)(w); \ + (h) = F((a)) + G((a), (b), (c)) + t1; \ + (d) += t1; \ + } + + +/* {{{ PHP_SHA256Init + * SHA256 initialization. Begins an SHA256 operation, writing a new context. + */ +static void PHP_SHA256Init(PHP_SHA256_CTX * context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. + */ + context->state[0] = 0x6a09e667; + context->state[1] = 0xbb67ae85; + context->state[2] = 0x3c6ef372; + context->state[3] = 0xa54ff53a; + context->state[4] = 0x510e527f; + context->state[5] = 0x9b05688c; + context->state[6] = 0x1f83d9ab; + context->state[7] = 0x5be0cd19; +} +/* }}} */ + +/* {{{ PHP_SHA256Update + SHA256 block update operation. Continues an SHA256 message-digest + operation, processing another message block, and updating the + context. + */ +static void PHP_SHA256Update(PHP_SHA256_CTX * context, const unsigned char *input, + unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((php_uint32) inputLen << 3)) + < ((php_uint32) inputLen << 3)) + context->count[1]++; + context->count[1] += ((php_uint32) inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + */ + if (inputLen >= partLen) { + memcpy + ((unsigned char*) & context->buffer[index], (unsigned char*) input, partLen); + SHA256Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + SHA256Transform(context->state, &input[i]); + + index = 0; + } else + i = 0; + + /* Buffer remaining input */ + memcpy + ((unsigned char*) & context->buffer[index], (unsigned char*) & input[i], + inputLen - i); +} +/* }}} */ + +/* {{{ PHP_SHA256Final + SHA256 finalization. Ends an SHA256 message-digest operation, writing the + the message digest and zeroizing the context. + */ +static void PHP_SHA256Final(unsigned char digest[32], PHP_SHA256_CTX * context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + bits[7] = context->count[0] & 0xFF; + bits[6] = (context->count[0] >> 8) & 0xFF; + bits[5] = (context->count[0] >> 16) & 0xFF; + bits[4] = (context->count[0] >> 24) & 0xFF; + bits[3] = context->count[1] & 0xFF; + bits[2] = (context->count[1] >> 8) & 0xFF; + bits[1] = (context->count[1] >> 16) & 0xFF; + bits[0] = (context->count[1] >> 24) & 0xFF; + + /* Pad out to 56 mod 64. + */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + PHP_SHA256Update(context, PADDING, padLen); + + /* Append length (before padding) */ + PHP_SHA256Update(context, bits, 8); + + /* Store state in digest */ + SHA256Encode(digest, context->state, 32); + + /* Zeroize sensitive information. + */ + memset((unsigned char*) context, 0, sizeof(*context)); +} +/* }}} */ + +/* {{{ SHA256Transform + * SHA256 basic transformation. Transforms state based on block. + */ +static void SHA256Transform(state, block) +php_uint32 state[8]; +const unsigned char block[64]; +{ + php_uint32 a = state[0], b = state[1], c = state[2]; + php_uint32 d = state[3], e = state[4], f = state[5]; + php_uint32 g = state[6], h = state[7], x[16], tmp1, tmp2, t1; + + SHA256Decode(x, block, 64); + + ROUND(a, b, c, d, e, f, g, h, x[0], 0x428a2f98) + ROUND(h, a, b, c, d, e, f, g, x[1], 0x71374491) + ROUND(g, h, a, b, c, d, e, f, x[2], 0xb5c0fbcf) + ROUND(f, g, h, a, b, c, d, e, x[3], 0xe9b5dba5) + ROUND(e, f, g, h, a, b, c, d, x[4], 0x3956c25b) + ROUND(d, e, f, g, h, a, b, c, x[5], 0x59f111f1) + ROUND(c, d, e, f, g, h, a, b, x[6], 0x923f82a4) + ROUND(b, c, d, e, f, g, h, a, x[7], 0xab1c5ed5) + ROUND(a, b, c, d, e, f, g, h, x[8], 0xd807aa98) + ROUND(h, a, b, c, d, e, f, g, x[9], 0x12835b01) + ROUND(g, h, a, b, c, d, e, f, x[10], 0x243185be) + ROUND(f, g, h, a, b, c, d, e, x[11], 0x550c7dc3) + ROUND(e, f, g, h, a, b, c, d, x[12], 0x72be5d74) + ROUND(d, e, f, g, h, a, b, c, x[13], 0x80deb1fe) + ROUND(c, d, e, f, g, h, a, b, x[14], 0x9bdc06a7) + ROUND(b, c, d, e, f, g, h, a, x[15], 0xc19bf174) + ROUND(a, b, c, d, e, f, g, h, W(16), 0xe49b69c1) + ROUND(h, a, b, c, d, e, f, g, W(17), 0xefbe4786) + ROUND(g, h, a, b, c, d, e, f, W(18), 0x0fc19dc6) + ROUND(f, g, h, a, b, c, d, e, W(19), 0x240ca1cc) + ROUND(e, f, g, h, a, b, c, d, W(20), 0x2de92c6f) + ROUND(d, e, f, g, h, a, b, c, W(21), 0x4a7484aa) + ROUND(c, d, e, f, g, h, a, b, W(22), 0x5cb0a9dc) + ROUND(b, c, d, e, f, g, h, a, W(23), 0x76f988da) + ROUND(a, b, c, d, e, f, g, h, W(24), 0x983e5152) + ROUND(h, a, b, c, d, e, f, g, W(25), 0xa831c66d) + ROUND(g, h, a, b, c, d, e, f, W(26), 0xb00327c8) + ROUND(f, g, h, a, b, c, d, e, W(27), 0xbf597fc7) + ROUND(e, f, g, h, a, b, c, d, W(28), 0xc6e00bf3) + ROUND(d, e, f, g, h, a, b, c, W(29), 0xd5a79147) + ROUND(c, d, e, f, g, h, a, b, W(30), 0x06ca6351) + ROUND(b, c, d, e, f, g, h, a, W(31), 0x14292967) + ROUND(a, b, c, d, e, f, g, h, W(32), 0x27b70a85) + ROUND(h, a, b, c, d, e, f, g, W(33), 0x2e1b2138) + ROUND(g, h, a, b, c, d, e, f, W(34), 0x4d2c6dfc) + ROUND(f, g, h, a, b, c, d, e, W(35), 0x53380d13) + ROUND(e, f, g, h, a, b, c, d, W(36), 0x650a7354) + ROUND(d, e, f, g, h, a, b, c, W(37), 0x766a0abb) + ROUND(c, d, e, f, g, h, a, b, W(38), 0x81c2c92e) + ROUND(b, c, d, e, f, g, h, a, W(39), 0x92722c85) + ROUND(a, b, c, d, e, f, g, h, W(40), 0xa2bfe8a1) + ROUND(h, a, b, c, d, e, f, g, W(41), 0xa81a664b) + ROUND(g, h, a, b, c, d, e, f, W(42), 0xc24b8b70) + ROUND(f, g, h, a, b, c, d, e, W(43), 0xc76c51a3) + ROUND(e, f, g, h, a, b, c, d, W(44), 0xd192e819) + ROUND(d, e, f, g, h, a, b, c, W(45), 0xd6990624) + ROUND(c, d, e, f, g, h, a, b, W(46), 0xf40e3585) + ROUND(b, c, d, e, f, g, h, a, W(47), 0x106aa070) + ROUND(a, b, c, d, e, f, g, h, W(48), 0x19a4c116) + ROUND(h, a, b, c, d, e, f, g, W(49), 0x1e376c08) + ROUND(g, h, a, b, c, d, e, f, W(50), 0x2748774c) + ROUND(f, g, h, a, b, c, d, e, W(51), 0x34b0bcb5) + ROUND(e, f, g, h, a, b, c, d, W(52), 0x391c0cb3) + ROUND(d, e, f, g, h, a, b, c, W(53), 0x4ed8aa4a) + ROUND(c, d, e, f, g, h, a, b, W(54), 0x5b9cca4f) + ROUND(b, c, d, e, f, g, h, a, W(55), 0x682e6ff3) + ROUND(a, b, c, d, e, f, g, h, W(56), 0x748f82ee) + ROUND(h, a, b, c, d, e, f, g, W(57), 0x78a5636f) + ROUND(g, h, a, b, c, d, e, f, W(58), 0x84c87814) + ROUND(f, g, h, a, b, c, d, e, W(59), 0x8cc70208) + ROUND(e, f, g, h, a, b, c, d, W(60), 0x90befffa) + ROUND(d, e, f, g, h, a, b, c, W(61), 0xa4506ceb) + ROUND(c, d, e, f, g, h, a, b, W(62), 0xbef9a3f7) + ROUND(b, c, d, e, f, g, h, a, W(63), 0xc67178f2) + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Zeroize sensitive information. */ + memset((unsigned char*) x, 0, sizeof(x)); +} +/* }}} */ + +/* {{{ SHA256Encode + Encodes input (php_uint32) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void SHA256Encode(output, input, len) +unsigned char *output; +php_uint32 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char) ((input[i] >> 24) & 0xff); + output[j + 1] = (unsigned char) ((input[i] >> 16) & 0xff); + output[j + 2] = (unsigned char) ((input[i] >> 8) & 0xff); + output[j + 3] = (unsigned char) (input[i] & 0xff); + } +} +/* }}} */ + +/* {{{ SHA256Decode + Decodes input (unsigned char) into output (php_uint32). Assumes len is + a multiple of 4. + */ +static void SHA256Decode(output, input, len) +php_uint32 *output; +const unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((php_uint32) input[j + 3]) | (((php_uint32) input[j + 2]) << 8) | + (((php_uint32) input[j + 1]) << 16) | (((php_uint32) input[j]) << 24); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff -Nura php-5.1.4/ext/standard/sha256.h hardening-patch-5.1.4-0.4.15/ext/standard/sha256.h --- php-5.1.4/ext/standard/sha256.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/sha256.h 2006-09-05 20:31:04.000000000 +0200 @@ -0,0 +1,40 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ +*/ + +/* $Id: sha256.h,v 1.4 2004/01/08 17:32:52 sniper Exp $ */ + +#ifndef SHA256_H +#define SHA256_H + +#include "ext/standard/basic_functions.h" + +/* SHA1 context. */ +typedef struct { + php_uint32 state[8]; /* state (ABCD) */ + php_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} PHP_SHA256_CTX; + +static void PHP_SHA256Init(PHP_SHA256_CTX *); +static void PHP_SHA256Update(PHP_SHA256_CTX *, const unsigned char *, unsigned int); +static void PHP_SHA256Final(unsigned char[32], PHP_SHA256_CTX *); + +PHP_FUNCTION(sha256); +PHP_FUNCTION(sha256_file); + +#endif diff -Nura php-5.1.4/ext/standard/string.c hardening-patch-5.1.4-0.4.15/ext/standard/string.c --- php-5.1.4/ext/standard/string.c 2006-04-25 14:48:41.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/standard/string.c 2006-09-05 20:31:04.000000000 +0200 @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: string.c,v 1.445.2.14 2006/04/25 12:48:41 tony2001 Exp $ */ +/* $Id: string.c,v 1.445.2.16 2006/08/10 17:46:43 iliaa Exp $ */ /* Synced with php 3.0 revision 1.193 1999-06-16 [ssb] */ @@ -632,7 +632,8 @@ { const char *text, *breakchar = "\n"; char *newtext; - int textlen, breakcharlen = 1, newtextlen, alloced, chk; + int textlen, breakcharlen = 1, newtextlen, chk; + size_t alloced; long current = 0, laststart = 0, lastspace = 0; long linelength = 75; zend_bool docut = 0; @@ -1612,10 +1613,18 @@ RETURN_FALSE; } + if (haystack_len == 0) { + RETURN_FALSE; + } + haystack_dup = estrndup(haystack, haystack_len); php_strtolower(haystack_dup, haystack_len); if (Z_TYPE_P(needle) == IS_STRING) { + if (Z_STRLEN_P(needle) == 0 || Z_STRLEN_P(needle) > haystack_len) { + efree(haystack_dup); + RETURN_FALSE; + } needle_dup = estrndup(Z_STRVAL_P(needle), Z_STRLEN_P(needle)); php_strtolower(needle_dup, Z_STRLEN_P(needle)); found = php_memnstr(haystack_dup + offset, needle_dup, Z_STRLEN_P(needle), haystack_dup + haystack_len); @@ -4194,7 +4203,7 @@ zval **input_str; /* Input string */ zval **mult; /* Multiplier */ char *result; /* Resulting string */ - int result_len; /* Length of the resulting string */ + size_t result_len; /* Length of the resulting string */ if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &input_str, &mult) == FAILURE) { WRONG_PARAM_COUNT; @@ -4219,11 +4228,7 @@ /* Initialize the result string */ result_len = Z_STRLEN_PP(input_str) * Z_LVAL_PP(mult); - if (result_len < 1 || result_len > 2147483647) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may not create strings longer than 2147483647 bytes"); - RETURN_FALSE; - } - result = (char *)emalloc(result_len + 1); + result = (char *)safe_emalloc(Z_STRLEN_PP(input_str), Z_LVAL_PP(mult), 1); /* Heavy optimization for situations where input string is 1 byte long */ if (Z_STRLEN_PP(input_str) == 1) { @@ -4894,7 +4899,7 @@ offset = (offset < 0) ? 0 : offset; } - if ((offset + len) >= s1_len) { + if ((offset + len) > s1_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The start position cannot exceed initial string length"); RETURN_FALSE; } diff -Nura php-5.1.4/ext/standard/syslog.c hardening-patch-5.1.4-0.4.15/ext/standard/syslog.c --- php-5.1.4/ext/standard/syslog.c 2006-03-21 01:59:08.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/standard/syslog.c 2006-09-05 20:31:04.000000000 +0200 @@ -42,6 +42,7 @@ */ PHP_MINIT_FUNCTION(syslog) { +#if !HARDENING_PATCH /* error levels */ REGISTER_LONG_CONSTANT("LOG_EMERG", LOG_EMERG, CONST_CS | CONST_PERSISTENT); /* system unusable */ REGISTER_LONG_CONSTANT("LOG_ALERT", LOG_ALERT, CONST_CS | CONST_PERSISTENT); /* immediate action required */ @@ -97,6 +98,7 @@ /* AIX doesn't have LOG_PERROR */ REGISTER_LONG_CONSTANT("LOG_PERROR", LOG_PERROR, CONST_CS | CONST_PERSISTENT); /*log to stderr*/ #endif +#endif BG(syslog_device)=NULL; return SUCCESS; diff -Nura php-5.1.4/ext/varfilter/config.m4 hardening-patch-5.1.4-0.4.15/ext/varfilter/config.m4 --- php-5.1.4/ext/varfilter/config.m4 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/varfilter/config.m4 2006-09-05 20:31:04.000000000 +0200 @@ -0,0 +1,11 @@ +dnl +dnl $Id: config.m4,v 1.1 2004/11/14 13:27:16 ionic Exp $ +dnl + +PHP_ARG_ENABLE(varfilter, whether to enable Hardening-Patch's variable filter, +[ --disable-varfilter Disable Hardening-Patch's variable filter], yes) + +if test "$PHP_VARFILTER" != "no"; then + AC_DEFINE(HAVE_VARFILTER, 1, [ ]) + PHP_NEW_EXTENSION(varfilter, varfilter.c, $ext_shared) +fi diff -Nura php-5.1.4/ext/varfilter/CREDITS hardening-patch-5.1.4-0.4.15/ext/varfilter/CREDITS --- php-5.1.4/ext/varfilter/CREDITS 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/varfilter/CREDITS 2006-09-05 20:31:04.000000000 +0200 @@ -0,0 +1,2 @@ +varfilter +Stefan Esser \ Kein Zeilenumbruch am Dateiende. diff -Nura php-5.1.4/ext/varfilter/php_varfilter.h hardening-patch-5.1.4-0.4.15/ext/varfilter/php_varfilter.h --- php-5.1.4/ext/varfilter/php_varfilter.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/varfilter/php_varfilter.h 2006-09-05 20:31:04.000000000 +0200 @@ -0,0 +1,144 @@ +/* + +----------------------------------------------------------------------+ + | Hardened-PHP Project's varfilter extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Stefan Esser | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ + + $Id: php_varfilter.h,v 1.1 2004/11/14 13:27:16 ionic Exp $ +*/ + +#ifndef PHP_VARFILTER_H +#define PHP_VARFILTER_H + +extern zend_module_entry varfilter_module_entry; +#define phpext_varfilter_ptr &varfilter_module_entry + +#ifdef PHP_WIN32 +#define PHP_VARFILTER_API __declspec(dllexport) +#else +#define PHP_VARFILTER_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#include "SAPI.h" + +#include "php_variables.h" + +#ifdef ZEND_ENGINE_2 +#define HASH_HTTP_GET_VARS 0x2095733f +#define HASH_HTTP_POST_VARS 0xbfee1265 +#define HASH_HTTP_COOKIE_VARS 0xaaca9d99 +#define HASH_HTTP_ENV_VARS 0x1fe186a8 +#define HASH_HTTP_SERVER_VARS 0xc987afd6 +#define HASH_HTTP_SESSION_VARS 0x7aba0d43 +#define HASH_HTTP_POST_FILES 0x98eb1ddc +#define HASH_HTTP_RAW_POST_DATA 0xdd633fec +#else +#define HASH_HTTP_GET_VARS 0x8d8645bd +#define HASH_HTTP_POST_VARS 0x7c699bf3 +#define HASH_HTTP_COOKIE_VARS 0x93ad0d6f +#define HASH_HTTP_ENV_VARS 0x84da3016 +#define HASH_HTTP_SERVER_VARS 0x6dbf964e +#define HASH_HTTP_SESSION_VARS 0x322906f5 +#define HASH_HTTP_POST_FILES 0xe4e4ce70 +#define HASH_HTTP_RAW_POST_DATA 0xe6137a0e +#endif + +PHP_MINIT_FUNCTION(varfilter); +PHP_MSHUTDOWN_FUNCTION(varfilter); +PHP_RINIT_FUNCTION(varfilter); +PHP_RSHUTDOWN_FUNCTION(varfilter); +PHP_MINFO_FUNCTION(varfilter); + + +ZEND_BEGIN_MODULE_GLOBALS(varfilter) +/* request variables */ + long max_request_variables; + long cur_request_variables; + long max_varname_length; + long max_totalname_length; + long max_value_length; + long max_array_depth; + long max_array_index_length; + zend_bool disallow_nul; +/* cookie variables */ + long max_cookie_vars; + long cur_cookie_vars; + long max_cookie_name_length; + long max_cookie_totalname_length; + long max_cookie_value_length; + long max_cookie_array_depth; + long max_cookie_array_index_length; + zend_bool disallow_cookie_nul; +/* get variables */ + long max_get_vars; + long cur_get_vars; + long max_get_name_length; + long max_get_totalname_length; + long max_get_value_length; + long max_get_array_depth; + long max_get_array_index_length; + zend_bool disallow_get_nul; +/* post variables */ + long max_post_vars; + long cur_post_vars; + long max_post_name_length; + long max_post_totalname_length; + long max_post_value_length; + long max_post_array_depth; + long max_post_array_index_length; + zend_bool disallow_post_nul; +/* fileupload */ + long max_uploads; + long cur_uploads; + zend_bool disallow_elf_files; + char *verification_script; + + zend_bool no_more_variables; + zend_bool no_more_get_variables; + zend_bool no_more_post_variables; + zend_bool no_more_cookie_variables; + zend_bool no_more_uploads; + +ZEND_END_MODULE_GLOBALS(varfilter) + + +#ifdef ZTS +#define VARFILTER_G(v) TSRMG(varfilter_globals_id, zend_varfilter_globals *, v) +#else +#define VARFILTER_G(v) (varfilter_globals.v) +#endif + +SAPI_INPUT_FILTER_FUNC(varfilter_input_filter); +SAPI_UPLOAD_VARNAME_FILTER_FUNC(varfilter_upload_varname_filter); +SAPI_PRE_UPLOAD_FILTER_FUNC(varfilter_pre_upload_filter); +SAPI_UPLOAD_CONTENT_FILTER_FUNC(varfilter_upload_content_filter); +SAPI_POST_UPLOAD_FILTER_FUNC(varfilter_post_upload_filter); +SAPI_TREAT_DATA_FUNC(varfilter_treat_data); + + + +#endif /* PHP_VARFILTER_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff -Nura php-5.1.4/ext/varfilter/varfilter.c hardening-patch-5.1.4-0.4.15/ext/varfilter/varfilter.c --- php-5.1.4/ext/varfilter/varfilter.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/ext/varfilter/varfilter.c 2006-09-07 18:51:41.000000000 +0200 @@ -0,0 +1,915 @@ +/* + +----------------------------------------------------------------------+ + | Hardened-PHP Project's varfilter extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Stefan Esser | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ + + $Id: varfilter.c,v 1.1 2004/11/14 13:27:16 ionic Exp $ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_varfilter.h" +#include "hardening_patch.h" + +ZEND_DECLARE_MODULE_GLOBALS(varfilter) + +/* True global resources - no need for thread safety here */ +static int le_varfilter; + +static void (*orig_register_server_variables)(zval *track_vars_array TSRMLS_DC) = NULL; +static int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC) = NULL; +static zend_bool hooked = 0; + +/* {{{ varfilter_module_entry + */ +zend_module_entry varfilter_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "varfilter", + NULL, + PHP_MINIT(varfilter), + PHP_MSHUTDOWN(varfilter), + PHP_RINIT(varfilter), /* Replace with NULL if there's nothing to do at request start */ + PHP_RSHUTDOWN(varfilter), /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(varfilter), +#if ZEND_MODULE_API_NO >= 20010901 + "0.4.15", /* Replace with version number for your extension */ +#endif + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_VARFILTER +ZEND_GET_MODULE(varfilter) +#endif + +/* {{{ PHP_INI + */ +PHP_INI_BEGIN() + /* for backward compatibility */ + STD_PHP_INI_ENTRY("varfilter.max_request_variables", "200", PHP_INI_PERDIR, OnUpdateLong, max_request_variables, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("varfilter.max_varname_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_varname_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("varfilter.max_value_length", "65000", PHP_INI_PERDIR, OnUpdateLong, max_value_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("varfilter.max_array_depth", "100", PHP_INI_PERDIR, OnUpdateLong, max_array_depth, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("varfilter.max_totalname_length", "256", PHP_INI_PERDIR, OnUpdateLong, max_totalname_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("varfilter.max_array_index_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_array_index_length, zend_varfilter_globals, varfilter_globals) + + STD_PHP_INI_ENTRY("hphp.request.max_vars", "200", PHP_INI_PERDIR, OnUpdateLong, max_request_variables, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.request.max_varname_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_varname_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.request.max_value_length", "65000", PHP_INI_PERDIR, OnUpdateLong, max_value_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.request.max_array_depth", "100", PHP_INI_PERDIR, OnUpdateLong, max_array_depth, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.request.max_totalname_length", "256", PHP_INI_PERDIR, OnUpdateLong, max_totalname_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.request.max_array_index_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_array_index_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.request.disallow_nul", "1", PHP_INI_PERDIR, OnUpdateBool, disallow_nul, zend_varfilter_globals, varfilter_globals) + + STD_PHP_INI_ENTRY("hphp.cookie.max_vars", "100", PHP_INI_PERDIR, OnUpdateLong, max_cookie_vars, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.cookie.max_name_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_cookie_name_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.cookie.max_totalname_length", "256", PHP_INI_PERDIR, OnUpdateLong, max_cookie_totalname_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.cookie.max_value_length", "10000", PHP_INI_PERDIR, OnUpdateLong, max_cookie_value_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.cookie.max_array_depth", "100", PHP_INI_PERDIR, OnUpdateLong, max_cookie_array_depth, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.cookie.max_array_index_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_cookie_array_index_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.cookie.disallow_nul", "1", PHP_INI_PERDIR, OnUpdateBool, disallow_cookie_nul, zend_varfilter_globals, varfilter_globals) + + STD_PHP_INI_ENTRY("hphp.get.max_vars", "100", PHP_INI_PERDIR, OnUpdateLong, max_get_vars, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.get.max_name_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_get_name_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.get.max_totalname_length", "256", PHP_INI_PERDIR, OnUpdateLong, max_get_totalname_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.get.max_value_length", "512", PHP_INI_PERDIR, OnUpdateLong, max_get_value_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.get.max_array_depth", "50", PHP_INI_PERDIR, OnUpdateLong, max_get_array_depth, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.get.max_array_index_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_get_array_index_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.get.disallow_nul", "1", PHP_INI_PERDIR, OnUpdateBool, disallow_get_nul, zend_varfilter_globals, varfilter_globals) + + STD_PHP_INI_ENTRY("hphp.post.max_vars", "200", PHP_INI_PERDIR, OnUpdateLong, max_post_vars, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.post.max_name_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_post_name_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.post.max_totalname_length", "256", PHP_INI_PERDIR, OnUpdateLong, max_post_totalname_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.post.max_value_length", "65000", PHP_INI_PERDIR, OnUpdateLong, max_post_value_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.post.max_array_depth", "100", PHP_INI_PERDIR, OnUpdateLong, max_post_array_depth, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.post.max_array_index_length", "64", PHP_INI_PERDIR, OnUpdateLong, max_post_array_index_length, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.post.disallow_nul", "1", PHP_INI_PERDIR, OnUpdateBool, disallow_post_nul, zend_varfilter_globals, varfilter_globals) + + STD_PHP_INI_ENTRY("hphp.upload.max_uploads", "25", PHP_INI_PERDIR, OnUpdateLong, max_uploads, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.upload.disallow_elf_files", "1", PHP_INI_SYSTEM, OnUpdateBool, disallow_elf_files, zend_varfilter_globals, varfilter_globals) + STD_PHP_INI_ENTRY("hphp.upload.verification_script", NULL, PHP_INI_SYSTEM, OnUpdateString, verification_script, zend_varfilter_globals, varfilter_globals) + + +PHP_INI_END() +/* }}} */ + +/* {{{ php_varfilter_init_globals + */ +static void php_varfilter_init_globals(zend_varfilter_globals *varfilter_globals) +{ + varfilter_globals->max_request_variables = 200; + varfilter_globals->max_varname_length = 64; + varfilter_globals->max_value_length = 10000; + varfilter_globals->max_array_depth = 100; + varfilter_globals->max_totalname_length = 256; + varfilter_globals->max_array_index_length = 64; + varfilter_globals->disallow_nul = 1; + + varfilter_globals->max_cookie_vars = 100; + varfilter_globals->max_cookie_name_length = 64; + varfilter_globals->max_cookie_totalname_length = 256; + varfilter_globals->max_cookie_value_length = 10000; + varfilter_globals->max_cookie_array_depth = 100; + varfilter_globals->max_cookie_array_index_length = 64; + varfilter_globals->disallow_cookie_nul = 1; + + varfilter_globals->max_get_vars = 100; + varfilter_globals->max_get_name_length = 64; + varfilter_globals->max_get_totalname_length = 256; + varfilter_globals->max_get_value_length = 512; + varfilter_globals->max_get_array_depth = 50; + varfilter_globals->max_get_array_index_length = 64; + varfilter_globals->disallow_get_nul = 1; + + varfilter_globals->max_post_vars = 200; + varfilter_globals->max_post_name_length = 64; + varfilter_globals->max_post_totalname_length = 256; + varfilter_globals->max_post_value_length = 65000; + varfilter_globals->max_post_array_depth = 100; + varfilter_globals->max_post_array_index_length = 64; + varfilter_globals->disallow_post_nul = 1; + + varfilter_globals->max_uploads = 25; + varfilter_globals->disallow_elf_files = 1; + varfilter_globals->verification_script = NULL; + + varfilter_globals->no_more_variables = 0; + varfilter_globals->no_more_get_variables = 0; + varfilter_globals->no_more_post_variables = 0; + varfilter_globals->no_more_cookie_variables = 0; + varfilter_globals->no_more_uploads = 0; + + varfilter_globals->cur_request_variables = 0; + varfilter_globals->cur_get_vars = 0; + varfilter_globals->cur_post_vars = 0; + varfilter_globals->cur_cookie_vars = 0; + + varfilter_globals->cur_uploads = 0; + +} +/* }}} */ + + +void varfilter_register_server_variables(zval *track_vars_array TSRMLS_DC) +{ + HashTable *svars; + int retval, failure=0; + + orig_register_server_variables(track_vars_array TSRMLS_CC); + + svars = Z_ARRVAL_P(track_vars_array); + + retval = zend_hash_del_key_or_index(svars, "HTTP_GET_VARS", sizeof("HTTP_GET_VARS"), HASH_HTTP_GET_VARS, HASH_DEL_INDEX); + if (retval == SUCCESS) failure = 1; + retval = zend_hash_del_key_or_index(svars, "HTTP_POST_VARS", sizeof("HTTP_POST_VARS"), HASH_HTTP_POST_VARS, HASH_DEL_INDEX); + if (retval == SUCCESS) failure = 1; + retval = zend_hash_del_key_or_index(svars, "HTTP_COOKIE_VARS", sizeof("HTTP_COOKIE_VARS"), HASH_HTTP_COOKIE_VARS, HASH_DEL_INDEX); + if (retval == SUCCESS) failure = 1; + retval = zend_hash_del_key_or_index(svars, "HTTP_ENV_VARS", sizeof("HTTP_ENV_VARS"), HASH_HTTP_ENV_VARS, HASH_DEL_INDEX); + if (retval == SUCCESS) failure = 1; + retval = zend_hash_del_key_or_index(svars, "HTTP_SERVER_VARS", sizeof("HTTP_SERVER_VARS"), HASH_HTTP_SERVER_VARS, HASH_DEL_INDEX); + if (retval == SUCCESS) failure = 1; + retval = zend_hash_del_key_or_index(svars, "HTTP_SESSION_VARS", sizeof("HTTP_SESSION_VARS"), HASH_HTTP_SESSION_VARS, HASH_DEL_INDEX); + if (retval == SUCCESS) failure = 1; + retval = zend_hash_del_key_or_index(svars, "HTTP_POST_FILES", sizeof("HTTP_POST_FILES"), HASH_HTTP_POST_FILES, HASH_DEL_INDEX); + if (retval == SUCCESS) failure = 1; + retval = zend_hash_del_key_or_index(svars, "HTTP_RAW_POST_DATA", sizeof("HTTP_RAW_POST_DATA"), HASH_HTTP_RAW_POST_DATA, HASH_DEL_INDEX); + if (retval == SUCCESS) failure = 1; + + if (failure) { + php_security_log(S_VARS, "Attacker tried to overwrite a superglobal through a HTTP header"); + } +} + +int varfilter_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC) +{ + int retval = SAPI_HEADER_ADD, i; + char *tmp; + + if (!HG(hphp_multiheader) && sapi_header && sapi_header->header) { + + tmp = sapi_header->header; + for (i=0; iheader_len; i++, tmp++) { + if (tmp[0] == 0) { + char *fname = get_active_function_name(TSRMLS_C); + + if (!fname) { + fname = "unknown"; + } + + php_security_log(S_MISC, "%s() - wanted to send a HTTP header with an ASCII NUL in it", fname); + sapi_header->header_len = i; + } else if (tmp[0] == '\n' && (i == sapi_header->header_len-1 || (tmp[1] != ' ' && tmp[1] != '\t'))) { + char *fname = get_active_function_name(TSRMLS_C); + + if (!fname) { + fname = "unknown"; + } + + php_security_log(S_MISC, "%s() - wanted to send multiple HTTP headers at once", fname); + sapi_header->header_len = i; + tmp[0] = 0; + } + } + } + + if (orig_header_handler) { + retval = orig_header_handler(sapi_header, sapi_headers TSRMLS_CC); + } + + return retval; +} + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(varfilter) +{ + ZEND_INIT_MODULE_GLOBALS(varfilter, php_varfilter_init_globals, NULL); + REGISTER_INI_ENTRIES(); + + if (!hooked) { + void *temp; + hooked = 1; + + temp = (void *)sapi_module.register_server_variables; + if (temp != varfilter_register_server_variables) { + orig_register_server_variables = temp; + } + temp = (void *)sapi_module.header_handler; + if (temp != varfilter_header_handler) { + orig_header_handler = temp; + } + } + + sapi_register_input_filter(varfilter_input_filter); + sapi_register_upload_varname_filter(varfilter_upload_varname_filter); + sapi_register_pre_upload_filter(varfilter_pre_upload_filter); + sapi_register_upload_content_filter(varfilter_upload_content_filter); + sapi_register_post_upload_filter(varfilter_post_upload_filter); + + sapi_module.header_handler = varfilter_header_handler; + sapi_module.register_server_variables = varfilter_register_server_variables; + + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(varfilter) +{ + UNREGISTER_INI_ENTRIES(); + + return SUCCESS; +} +/* }}} */ + +/* Remove if there's nothing to do at request start */ +/* {{{ PHP_RINIT_FUNCTION + */ +PHP_RINIT_FUNCTION(varfilter) +{ + VARFILTER_G(cur_request_variables) = 0; + VARFILTER_G(cur_get_vars) = 0; + VARFILTER_G(cur_post_vars) = 0; + VARFILTER_G(cur_cookie_vars) = 0; + + VARFILTER_G(cur_uploads) = 0; + + VARFILTER_G(no_more_variables) = 0; + VARFILTER_G(no_more_get_variables) = 0; + VARFILTER_G(no_more_post_variables) = 0; + VARFILTER_G(no_more_cookie_variables) = 0; + VARFILTER_G(no_more_uploads) = 0; + + return SUCCESS; +} +/* }}} */ + +/* Remove if there's nothing to do at request end */ +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(varfilter) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(varfilter) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "Hardening-Patch's variable filter support", "enabled"); + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* {{{ normalize_varname + */ +static void normalize_varname(char *varname) +{ + char *s=varname, *index=NULL, *indexend=NULL, *p; + + /* overjump leading space */ + while (*s == ' ') { + s++; + } + + /* and remove it */ + if (s != varname) { + memmove(varname, s, strlen(s)+1); + } + + for (p=varname; *p && *p != '['; p++) { + switch(*p) { + case ' ': + case '.': + *p='_'; + break; + } + } + + /* find index */ + index = strchr(varname, '['); + if (index) { + index++; + s=index; + } else { + return; + } + + /* done? */ + while (index) { + + while (*index == ' ' || *index == '\r' || *index == '\n' || *index=='\t') { + index++; + } + indexend = strchr(index, ']'); + indexend = indexend ? indexend + 1 : index + strlen(index); + + if (s != index) { + memmove(s, index, strlen(index)+1); + s += indexend-index; + } else { + s = indexend; + } + + if (*s == '[') { + s++; + index = s; + } else { + index = NULL; + } + } + *s++='\0'; +} +/* }}} */ + +/* {{{ SAPI_UPLOAD_VARNAME_FILTER_FUNC + */ +SAPI_UPLOAD_VARNAME_FILTER_FUNC(varfilter_upload_varname_filter) +{ + char *index, *prev_index = NULL, *var; + unsigned int var_len, total_len, depth = 0; + + var = estrdup(varname); + + /* Normalize the variable name */ + normalize_varname(var); + + /* Find length of variable name */ + index = strchr(var, '['); + total_len = strlen(var); + var_len = index ? index-var : total_len; + + /* Drop this variable if it exceeds the varname/total length limit */ + if (VARFILTER_G(max_varname_length) && VARFILTER_G(max_varname_length) < var_len) { + php_security_log(S_FILES, "configured request variable name length limit exceeded - dropped %s", var); + goto return_failure; + } + if (VARFILTER_G(max_totalname_length) && VARFILTER_G(max_totalname_length) < total_len) { + php_security_log(S_FILES, "configured request variable total name length limit exceeded - dropped %s", var); + goto return_failure; + } + if (VARFILTER_G(max_post_name_length) && VARFILTER_G(max_post_name_length) < var_len) { + php_security_log(S_FILES, "configured POST variable name length limit exceeded - dropped %s", var); + + goto return_failure; + } + if (VARFILTER_G(max_post_totalname_length) && VARFILTER_G(max_post_totalname_length) < var_len) { + php_security_log(S_FILES, "configured POST variable total name length limit exceeded - dropped %s", var); + goto return_failure; + } + + /* Find out array depth */ + while (index) { + unsigned int index_length; + + depth++; + index = strchr(index+1, '['); + + if (prev_index) { + index_length = index ? index - 1 - prev_index - 1: strlen(prev_index); + + if (VARFILTER_G(max_array_index_length) && VARFILTER_G(max_array_index_length) < index_length) { + php_security_log(S_FILES, "configured request variable array index length limit exceeded - dropped %s", var); + goto return_failure; + } + if (VARFILTER_G(max_post_array_index_length) && VARFILTER_G(max_post_array_index_length) < index_length) { + php_security_log(S_FILES, "configured POST variable array index length limit exceeded - dropped %s", var); + goto return_failure; + } + prev_index = index; + } + + } + + /* Drop this variable if it exceeds the array depth limit */ + if (VARFILTER_G(max_array_depth) && VARFILTER_G(max_array_depth) < depth) { + php_security_log(S_FILES, "configured request variable array depth limit exceeded - dropped %s", var); + goto return_failure; + } + if (VARFILTER_G(max_post_array_depth) && VARFILTER_G(max_post_array_depth) < depth) { + php_security_log(S_FILES, "configured POST variable array depth limit exceeded - dropped %s", var); + goto return_failure; + } + + + /* Drop this variable if it is one of GLOBALS, _GET, _POST, ... */ + /* This is to protect several silly scripts that do globalizing themself */ + + switch (var_len) { + case 18: + if (memcmp(var, "HTTP_RAW_POST_DATA", 18)==0) goto protected_varname2; + break; + case 17: + if (memcmp(var, "HTTP_SESSION_VARS", 17)==0) goto protected_varname2; + break; + case 16: + if (memcmp(var, "HTTP_SERVER_VARS", 16)==0) goto protected_varname2; + if (memcmp(var, "HTTP_COOKIE_VARS", 16)==0) goto protected_varname2; + break; + case 15: + if (memcmp(var, "HTTP_POST_FILES", 15)==0) goto protected_varname2; + break; + case 14: + if (memcmp(var, "HTTP_POST_VARS", 14)==0) goto protected_varname2; + break; + case 13: + if (memcmp(var, "HTTP_GET_VARS", 13)==0) goto protected_varname2; + if (memcmp(var, "HTTP_ENV_VARS", 13)==0) goto protected_varname2; + break; + case 8: + if (memcmp(var, "_SESSION", 8)==0) goto protected_varname2; + if (memcmp(var, "_REQUEST", 8)==0) goto protected_varname2; + break; + case 7: + if (memcmp(var, "GLOBALS", 7)==0) goto protected_varname2; + if (memcmp(var, "_COOKIE", 7)==0) goto protected_varname2; + if (memcmp(var, "_SERVER", 7)==0) goto protected_varname2; + break; + case 6: + if (memcmp(var, "_FILES", 6)==0) goto protected_varname2; + break; + case 5: + if (memcmp(var, "_POST", 5)==0) goto protected_varname2; + break; + case 4: + if (memcmp(var, "_ENV", 4)==0) goto protected_varname2; + if (memcmp(var, "_GET", 4)==0) goto protected_varname2; + break; + } + + efree(var); + return SUCCESS; +protected_varname2: + php_security_log(S_FILES, "tried to register forbidden variable '%s' through FILE variables", var); +return_failure: + efree(var); + return FAILURE; +} +/* }}} */ + +/* {{{ SAPI_PRE_UPLOAD_FILTER_FUNC + */ +SAPI_PRE_UPLOAD_FILTER_FUNC(varfilter_pre_upload_filter) +{ + /* Drop if no more variables flag is set */ + if (VARFILTER_G(no_more_uploads)) { + return FAILURE; + } + /* Drop this fileupload if the limit is reached */ + if (VARFILTER_G(max_uploads) && VARFILTER_G(max_uploads) <= VARFILTER_G(cur_uploads)) { + php_security_log(S_FILES, "configured fileupload limit exceeded - file dropped"); + VARFILTER_G(no_more_uploads) = 1; + return FAILURE; + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ SAPI_UPLOAD_CONTENT_FILTER_FUNC + */ +SAPI_UPLOAD_CONTENT_FILTER_FUNC(varfilter_upload_content_filter) +{ + + if (VARFILTER_G(disallow_elf_files)) { + + if (offset == 0 && buffer_len > 10) { + + if (buffer[0] == 0x7F && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') { + php_security_log(S_FILES, "uploaded file is an ELF executable - file dropped"); + return FAILURE; + } + } + + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ SAPI_POST_UPLOAD_FILTER_FUNC + */ +SAPI_POST_UPLOAD_FILTER_FUNC(varfilter_post_upload_filter) +{ + int retval = SUCCESS; + + if (VARFILTER_G(verification_script)) { + char cmd[8192]; + FILE *in; + int first=1; + + ap_php_snprintf(cmd, sizeof(cmd), "%s %s", VARFILTER_G(verification_script), tmpfilename); + + if ((in=VCWD_POPEN(cmd, "r"))==NULL) { + php_security_log(S_FILES, "unable to execute fileupload verification script %s - file dropped", VARFILTER_G(verification_script)); + return FAILURE; + } + + retval = FAILURE; + + /* read and forget the result */ + while (1) { + int readbytes = fread(cmd, 1, sizeof(cmd), in); + if (readbytes<=0) { + break; + } + if (first) { + retval = atoi(cmd) == 1 ? SUCCESS : FAILURE; + first = 0; + } + } + pclose(in); + } + + if (retval != SUCCESS) { + php_security_log(S_FILES, "fileupload verification script disallows file - file dropped"); + return FAILURE; + } + + VARFILTER_G(cur_uploads)++; + return SUCCESS; +} +/* }}} */ + +/* {{{ SAPI_INPUT_FILTER_FUNC + */ +SAPI_INPUT_FILTER_FUNC(varfilter_input_filter) +{ + char *index, *prev_index = NULL; + unsigned int var_len, total_len, depth = 0; + + /* Drop this variable if the limit was reached */ + switch (arg) { + case PARSE_GET: + if (VARFILTER_G(no_more_get_variables)) { + return 0; + } + break; + case PARSE_POST: + if (VARFILTER_G(no_more_post_variables)) { + return 0; + } + break; + case PARSE_COOKIE: + if (VARFILTER_G(no_more_cookie_variables)) { + return 0; + } + break; + default: /* we do not want to protect parse_str() and friends */ + if (new_val_len) { + *new_val_len = val_len; + } + return 1; + } + if (VARFILTER_G(no_more_variables)) { + return 0; + } + + /* Drop this variable if the limit is now reached */ + if (VARFILTER_G(max_request_variables) && VARFILTER_G(max_request_variables) <= VARFILTER_G(cur_request_variables)) { + php_security_log(S_VARS, "configured request variable limit exceeded - dropped %s", var); + VARFILTER_G(no_more_variables) = 1; + return 0; + } + switch (arg) { + case PARSE_GET: + if (VARFILTER_G(max_get_vars) && VARFILTER_G(max_get_vars) <= VARFILTER_G(cur_get_vars)) { + php_security_log(S_VARS, "configured GET variable limit exceeded - dropped %s", var); + VARFILTER_G(no_more_get_variables) = 1; + return 0; + } + break; + case PARSE_COOKIE: + if (VARFILTER_G(max_cookie_vars) && VARFILTER_G(max_cookie_vars) <= VARFILTER_G(cur_cookie_vars)) { + php_security_log(S_VARS, "configured COOKIE variable limit exceeded - dropped %s", var); + VARFILTER_G(no_more_cookie_variables) = 1; + return 0; + } + break; + case PARSE_POST: + if (VARFILTER_G(max_post_vars) && VARFILTER_G(max_post_vars) <= VARFILTER_G(cur_post_vars)) { + php_security_log(S_VARS, "configured POST variable limit exceeded - dropped %s", var); + VARFILTER_G(no_more_post_variables) = 1; + return 0; + } + break; + } + + + /* Drop this variable if it exceeds the value length limit */ + if (VARFILTER_G(max_value_length) && VARFILTER_G(max_value_length) < val_len) { + php_security_log(S_VARS, "configured request variable value length limit exceeded - dropped %s", var); + return 0; + } + switch (arg) { + case PARSE_GET: + if (VARFILTER_G(max_get_value_length) && VARFILTER_G(max_get_value_length) < val_len) { + php_security_log(S_VARS, "configured GET variable value length limit exceeded - dropped %s", var); + return 0; + } + break; + case PARSE_COOKIE: + if (VARFILTER_G(max_cookie_value_length) && VARFILTER_G(max_cookie_value_length) < val_len) { + php_security_log(S_VARS, "configured COOKIE variable value length limit exceeded - dropped %s", var); + return 0; + } + break; + case PARSE_POST: + if (VARFILTER_G(max_post_value_length) && VARFILTER_G(max_post_value_length) < val_len) { + php_security_log(S_VARS, "configured POST variable value length limit exceeded - dropped %s", var); + return 0; + } + break; + } + + /* Normalize the variable name */ + normalize_varname(var); + + /* Find length of variable name */ + index = strchr(var, '['); + total_len = strlen(var); + var_len = index ? index-var : total_len; + + /* Drop this variable if it exceeds the varname/total length limit */ + if (VARFILTER_G(max_varname_length) && VARFILTER_G(max_varname_length) < var_len) { + php_security_log(S_VARS, "configured request variable name length limit exceeded - dropped %s", var); + return 0; + } + if (VARFILTER_G(max_totalname_length) && VARFILTER_G(max_totalname_length) < total_len) { + php_security_log(S_VARS, "configured request variable total name length limit exceeded - dropped %s", var); + return 0; + } + switch (arg) { + case PARSE_GET: + if (VARFILTER_G(max_get_name_length) && VARFILTER_G(max_get_name_length) < var_len) { + php_security_log(S_VARS, "configured GET variable name length limit exceeded - dropped %s", var); + return 0; + } + if (VARFILTER_G(max_get_totalname_length) && VARFILTER_G(max_get_totalname_length) < var_len) { + php_security_log(S_VARS, "configured GET variable total name length limit exceeded - dropped %s", var); + return 0; + } + break; + case PARSE_COOKIE: + if (VARFILTER_G(max_cookie_name_length) && VARFILTER_G(max_cookie_name_length) < var_len) { + php_security_log(S_VARS, "configured COOKIE variable name length limit exceeded - dropped %s", var); + return 0; + } + if (VARFILTER_G(max_cookie_totalname_length) && VARFILTER_G(max_cookie_totalname_length) < var_len) { + php_security_log(S_VARS, "configured COOKIE variable total name length limit exceeded - dropped %s", var); + return 0; + } + break; + case PARSE_POST: + if (VARFILTER_G(max_post_name_length) && VARFILTER_G(max_post_name_length) < var_len) { + php_security_log(S_VARS, "configured POST variable name length limit exceeded - dropped %s", var); + return 0; + } + if (VARFILTER_G(max_post_totalname_length) && VARFILTER_G(max_post_totalname_length) < var_len) { + php_security_log(S_VARS, "configured POST variable total name length limit exceeded - dropped %s", var); + return 0; + } + break; + } + + /* Find out array depth */ + while (index) { + unsigned int index_length; + + depth++; + index = strchr(index+1, '['); + + if (prev_index) { + index_length = index ? index - 1 - prev_index - 1: strlen(prev_index); + + if (VARFILTER_G(max_array_index_length) && VARFILTER_G(max_array_index_length) < index_length) { + php_security_log(S_VARS, "configured request variable array index length limit exceeded - dropped %s", var); + return 0; + } + switch (arg) { + case PARSE_GET: + if (VARFILTER_G(max_get_array_index_length) && VARFILTER_G(max_get_array_index_length) < index_length) { + php_security_log(S_VARS, "configured GET variable array index length limit exceeded - dropped %s", var); + return 0; + } + break; + case PARSE_COOKIE: + if (VARFILTER_G(max_cookie_array_index_length) && VARFILTER_G(max_cookie_array_index_length) < index_length) { + php_security_log(S_VARS, "configured COOKIE variable array index length limit exceeded - dropped %s", var); + return 0; + } + break; + case PARSE_POST: + if (VARFILTER_G(max_post_array_index_length) && VARFILTER_G(max_post_array_index_length) < index_length) { + php_security_log(S_VARS, "configured POST variable array index length limit exceeded - dropped %s", var); + return 0; + } + break; + } + prev_index = index; + } + + } + + /* Drop this variable if it exceeds the array depth limit */ + if (VARFILTER_G(max_array_depth) && VARFILTER_G(max_array_depth) < depth) { + php_security_log(S_VARS, "configured request variable array depth limit exceeded - dropped %s", var); + return 0; + } + switch (arg) { + case PARSE_GET: + if (VARFILTER_G(max_get_array_depth) && VARFILTER_G(max_get_array_depth) < depth) { + php_security_log(S_VARS, "configured GET variable array depth limit exceeded - dropped %s", var); + return 0; + } + break; + case PARSE_COOKIE: + if (VARFILTER_G(max_cookie_array_depth) && VARFILTER_G(max_cookie_array_depth) < depth) { + php_security_log(S_VARS, "configured COOKIE variable array depth limit exceeded - dropped %s", var); + return 0; + } + break; + case PARSE_POST: + if (VARFILTER_G(max_post_array_depth) && VARFILTER_G(max_post_array_depth) < depth) { + php_security_log(S_VARS, "configured POST variable array depth limit exceeded - dropped %s", var); + return 0; + } + break; + } + + /* Check if variable value is truncated by a \0 */ + + if (val && *val && val_len != strlen(*val)) { + + if (VARFILTER_G(disallow_nul)) { + php_security_log(S_VARS, "ASCII-NUL chars not allowed within request variables - dropped %s", var); + return 0; + } + switch (arg) { + case PARSE_GET: + if (VARFILTER_G(disallow_get_nul)) { + php_security_log(S_VARS, "ASCII-NUL chars not allowed within GET variables - dropped %s", var); + return 0; + } + break; + case PARSE_COOKIE: + if (VARFILTER_G(disallow_cookie_nul)) { + php_security_log(S_VARS, "ASCII-NUL chars not allowed within COOKIE variables - dropped %s", var); + return 0; + } + break; + case PARSE_POST: + if (VARFILTER_G(disallow_post_nul)) { + php_security_log(S_VARS, "ASCII-NUL chars not allowed within POST variables - dropped %s", var); + return 0; + } + break; + } + } + + /* Drop this variable if it is one of GLOBALS, _GET, _POST, ... */ + /* This is to protect several silly scripts that do globalizing themself */ + + switch (var_len) { + case 18: + if (memcmp(var, "HTTP_RAW_POST_DATA", 18)==0) goto protected_varname; + break; + case 17: + if (memcmp(var, "HTTP_SESSION_VARS", 17)==0) goto protected_varname; + break; + case 16: + if (memcmp(var, "HTTP_SERVER_VARS", 16)==0) goto protected_varname; + if (memcmp(var, "HTTP_COOKIE_VARS", 16)==0) goto protected_varname; + break; + case 15: + if (memcmp(var, "HTTP_POST_FILES", 15)==0) goto protected_varname; + break; + case 14: + if (memcmp(var, "HTTP_POST_VARS", 14)==0) goto protected_varname; + break; + case 13: + if (memcmp(var, "HTTP_GET_VARS", 13)==0) goto protected_varname; + if (memcmp(var, "HTTP_ENV_VARS", 13)==0) goto protected_varname; + break; + case 8: + if (memcmp(var, "_SESSION", 8)==0) goto protected_varname; + if (memcmp(var, "_REQUEST", 8)==0) goto protected_varname; + break; + case 7: + if (memcmp(var, "GLOBALS", 7)==0) goto protected_varname; + if (memcmp(var, "_COOKIE", 7)==0) goto protected_varname; + if (memcmp(var, "_SERVER", 7)==0) goto protected_varname; + break; + case 6: + if (memcmp(var, "_FILES", 6)==0) goto protected_varname; + break; + case 5: + if (memcmp(var, "_POST", 5)==0) goto protected_varname; + break; + case 4: + if (memcmp(var, "_ENV", 4)==0) goto protected_varname; + if (memcmp(var, "_GET", 4)==0) goto protected_varname; + break; + } + + /* Okay let PHP register this variable */ + VARFILTER_G(cur_request_variables)++; + switch (arg) { + case PARSE_GET: + VARFILTER_G(cur_get_vars)++; + break; + case PARSE_COOKIE: + VARFILTER_G(cur_cookie_vars)++; + break; + case PARSE_POST: + VARFILTER_G(cur_post_vars)++; + break; + } + + if (new_val_len) { + *new_val_len = val_len; + } + + return 1; +protected_varname: + php_security_log(S_VARS, "tried to register forbidden variable '%s' through %s variables", var, arg == PARSE_GET ? "GET" : arg == PARSE_POST ? "POST" : "COOKIE"); + return 0; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + + diff -Nura php-5.1.4/ext/wddx/wddx.c hardening-patch-5.1.4-0.4.15/ext/wddx/wddx.c --- php-5.1.4/ext/wddx/wddx.c 2006-04-23 18:02:05.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/ext/wddx/wddx.c 2006-09-05 20:31:04.000000000 +0200 @@ -399,9 +399,9 @@ break; default: - if (iscntrl((int)*(unsigned char *)p)) { + if (iscntrl((int)*(unsigned char *)p)||(int)*(unsigned char *)p >= 127) { FLUSH_BUF(); - sprintf(control_buf, WDDX_CHAR, *p); + sprintf(control_buf, WDDX_CHAR, (int)*(unsigned char *)p); php_wddx_add_chunk(packet, control_buf); } else buf[l++] = *p; @@ -751,7 +751,7 @@ } else if (!strcmp(name, EL_CHAR)) { int i; - for (i = 0; atts[i]; i++) { + if (atts) for (i = 0; atts[i]; i++) { if (!strcmp(atts[i], EL_CHAR_CODE) && atts[++i] && atts[i][0]) { char tmp_buf[2]; @@ -771,7 +771,7 @@ } else if (!strcmp(name, EL_BOOLEAN)) { int i; - for (i = 0; atts[i]; i++) { + if (atts) for (i = 0; atts[i]; i++) { if (!strcmp(atts[i], EL_VALUE) && atts[++i] && atts[i][0]) { ent.type = ST_BOOLEAN; SET_STACK_VARNAME; @@ -812,7 +812,7 @@ } else if (!strcmp(name, EL_VAR)) { int i; - for (i = 0; atts[i]; i++) { + if (atts) for (i = 0; atts[i]; i++) { if (!strcmp(atts[i], EL_NAME) && atts[++i] && atts[i][0]) { char *decoded; int decoded_len; @@ -829,7 +829,7 @@ MAKE_STD_ZVAL(ent.data); array_init(ent.data); - for (i = 0; atts[i]; i++) { + if (atts) for (i = 0; atts[i]; i++) { if (!strcmp(atts[i], "fieldNames") && atts[++i] && atts[i][0]) { zval *tmp; char *key; @@ -869,7 +869,7 @@ ent.varname = NULL; ent.data = NULL; - for (i = 0; atts[i]; i++) { + if (atts) for (i = 0; atts[i]; i++) { if (!strcmp(atts[i], EL_NAME) && atts[++i] && atts[i][0]) { char *decoded; int decoded_len; diff -Nura php-5.1.4/main/fopen_wrappers.c hardening-patch-5.1.4-0.4.15/main/fopen_wrappers.c --- php-5.1.4/main/fopen_wrappers.c 2006-03-17 11:42:31.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/fopen_wrappers.c 2006-09-07 18:53:55.000000000 +0200 @@ -104,7 +104,10 @@ } /* Resolve the real path into resolved_name */ - if ((expand_filepath(path, resolved_name TSRMLS_CC) != NULL) && (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL)) { + if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) { + return -2; + } + if (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL) { /* Handler for basedirs that end with a / */ resolved_basedir_len = strlen(resolved_basedir); if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) { @@ -114,14 +117,20 @@ } } + resolved_name_len = strlen(resolved_name); if (path[strlen(path)-1] == PHP_DIR_SEPARATOR) { - resolved_name_len = strlen(resolved_name); if (resolved_name[resolved_name_len - 1] != PHP_DIR_SEPARATOR) { resolved_name[resolved_name_len] = PHP_DIR_SEPARATOR; resolved_name[++resolved_name_len] = '\0'; } } + if (resolved_name_len == resolved_basedir_len - 1) { + if (resolved_basedir[resolved_basedir_len - 1] == PHP_DIR_SEPARATOR) { + resolved_basedir_len--; + } + } + /* Check the path */ #if defined(PHP_WIN32) || defined(NETWARE) if (strncasecmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) { @@ -135,7 +144,7 @@ } } else { /* Unable to resolve the real path, return -1 */ - return -1; + return -3; } } /* }}} */ @@ -154,22 +163,44 @@ char *pathbuf; char *ptr; char *end; + char path_copy[MAXPATHLEN]; + int path_len; + + /* Special case path ends with a trailing slash */ + path_len = strlen(path); + if (path_len >= MAXPATHLEN) { + errno = EPERM; /* we deny permission to open it */ + return -1; + } + if (path_len > 0 && path[path_len-1] == PHP_DIR_SEPARATOR) { + memcpy(path_copy, path, path_len+1); + while (path_len > 0 && path_copy[path_len-1] == PHP_DIR_SEPARATOR) path_len--; + path_copy[path_len] = '\0'; + path = (const char *)&path_copy; + } pathbuf = estrdup(PG(open_basedir)); ptr = pathbuf; while (ptr && *ptr) { + int res; end = strchr(ptr, DEFAULT_DIR_SEPARATOR); if (end != NULL) { *end = '\0'; end++; } - if (php_check_specific_open_basedir(ptr, path TSRMLS_CC) == 0) { + res = php_check_specific_open_basedir(ptr, path TSRMLS_CC); + if (res == 0) { efree(pathbuf); return 0; } + if (res == -2) { + efree(pathbuf); + errno = EPERM; + return -1; + } ptr = end; } diff -Nura php-5.1.4/main/hardened_globals.h hardening-patch-5.1.4-0.4.15/main/hardened_globals.h --- php-5.1.4/main/hardened_globals.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/hardened_globals.h 2006-09-05 20:31:05.000000000 +0200 @@ -0,0 +1,64 @@ +/* + +----------------------------------------------------------------------+ + | Hardening-Patch for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Stefan Esser | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ + */ + +#ifndef HARDENED_GLOBALS_H +#define HARDENED_GLOBALS_H + +typedef struct _hardened_globals hardened_globals_struct; + +#ifdef ZTS +# define HG(v) TSRMG(hardened_globals_id, hardened_globals_struct *, v) +extern int hardened_globals_id; +#else +# define HG(v) (hardened_globals.v) +extern struct _hardened_globals hardened_globals; +#endif + + +struct _hardened_globals { +#if HARDENING_PATCH_MM_PROTECT + unsigned int canary_1; + unsigned int canary_2; +#endif +#if HARDENING_PATCH_LL_PROTECT + unsigned int canary_3; + unsigned int canary_4; + unsigned int ll_canary_inited; +#endif + zend_bool hphp_sql_bailout_on_error; + zend_bool hphp_multiheader; + unsigned long hphp_mailprotect; + long hard_memory_limit; + HashTable *eval_whitelist; + HashTable *eval_blacklist; + HashTable *func_whitelist; + HashTable *func_blacklist; + HashTable *include_whitelist; + HashTable *include_blacklist; + unsigned int dummy; +}; + + +#endif /* HARDENED_GLOBALS_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff -Nura php-5.1.4/main/hardening_patch.c hardening-patch-5.1.4-0.4.15/main/hardening_patch.c --- php-5.1.4/main/hardening_patch.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/hardening_patch.c 2006-09-05 20:31:05.000000000 +0200 @@ -0,0 +1,430 @@ +/* + +----------------------------------------------------------------------+ + | Hardening Patch for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Stefan Esser | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ + */ +/* $Id: hardening_patch.c,v 1.2 2004/11/21 09:38:52 ionic Exp $ */ + +#include "php.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif +#include "SAPI.h" +#include "php_globals.h" + +#if HARDENING_PATCH + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE) +#undef AF_UNIX +#endif + +#if defined(AF_UNIX) +#include +#endif + +#define SYSLOG_PATH "/dev/log" + +#include "snprintf.h" + +#include "hardening_patch.h" + +#ifdef ZTS +#include "hardened_globals.h" +int hardened_globals_id; +#else +struct _hardened_globals hardened_globals; +#endif + +static void hardened_globals_ctor(hardened_globals_struct *hardened_globals TSRMLS_DC) +{ + memset(hardened_globals, 0, sizeof(*hardened_globals)); +} + + +PHPAPI void hardened_startup() +{ +#ifdef ZTS + ts_allocate_id(&hardened_globals_id, sizeof(hardened_globals_struct), (ts_allocate_ctor) hardened_globals_ctor, NULL); +#else + hardened_globals_ctor(&hardened_globals TSRMLS_CC); +#endif +} + +PHPAPI void hardened_clear_mm_canaries(TSRMLS_D) +{ + HG(canary_1) = php_canary(); + HG(canary_2) = php_canary(); +} + +char *loglevel2string(int loglevel) +{ + switch (loglevel) { + case S_FILES: + return "FILES"; + case S_INCLUDE: + return "INCLUDE"; + case S_MEMORY: + return "MEMORY"; + case S_MISC: + return "MISC"; + case S_SQL: + return "SQL"; + case S_EXECUTOR: + return "EXECUTOR"; + case S_VARS: + return "VARS"; + default: + return "UNKNOWN"; + } +} + +PHPAPI void php_security_log(int loglevel, char *fmt, ...) +{ +#if defined(AF_UNIX) + int s, r, i=0; + struct sockaddr_un saun; + char buf[4096+64]; + char error[4096+100]; + char *ip_address; + char *fname; + int lineno; + va_list ap; + TSRMLS_FETCH(); + + if (EG(hphp_log_use_x_forwarded_for)) { + ip_address = sapi_getenv("HTTP_X_FORWARDED_FOR", 20 TSRMLS_CC); + if (ip_address == NULL) { + ip_address = "X-FORWARDED-FOR not set"; + } + } else { + ip_address = sapi_getenv("REMOTE_ADDR", 11 TSRMLS_CC); + if (ip_address == NULL) { + ip_address = "REMOTE_ADDR not set"; + } + } + + + va_start(ap, fmt); + ap_php_vsnprintf(error, sizeof(error), fmt, ap); + va_end(ap); + while (error[i]) { + if (error[i] < 32) error[i] = '.'; + i++; + } + + if (zend_is_executing(TSRMLS_C)) { + lineno = zend_get_executed_lineno(TSRMLS_C); + fname = zend_get_executed_filename(TSRMLS_C); + ap_php_snprintf(buf, sizeof(buf), "ALERT - %s (attacker '%s', file '%s', line %u)", error, ip_address, fname, lineno); + } else { + fname = sapi_getenv("SCRIPT_FILENAME", 15 TSRMLS_CC); + if (fname==NULL) { + fname = "unknown"; + } + ap_php_snprintf(buf, sizeof(buf), "ALERT - %s (attacker '%s', file '%s')", error, ip_address, fname); + } + + /* Syslog-Logging disabled? */ + if ((EG(hphp_log_syslog) & loglevel)==0) { + goto log_sapi; + } + + ap_php_snprintf(error, sizeof(error), "<%u>hphp[%u]: %s\n", EG(hphp_log_syslog_facility)|EG(hphp_log_syslog_priority),getpid(),buf); + + s = socket(AF_UNIX, SOCK_DGRAM, 0); + if (s == -1) { + goto log_sapi; + } + + memset(&saun, 0, sizeof(saun)); + saun.sun_family = AF_UNIX; + strcpy(saun.sun_path, SYSLOG_PATH); + /*saun.sun_len = sizeof(saun);*/ + + r = connect(s, (struct sockaddr *)&saun, sizeof(saun)); + if (r) { + close(s); + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) { + goto log_sapi; + } + + memset(&saun, 0, sizeof(saun)); + saun.sun_family = AF_UNIX; + strcpy(saun.sun_path, SYSLOG_PATH); + /*saun.sun_len = sizeof(saun);*/ + + r = connect(s, (struct sockaddr *)&saun, sizeof(saun)); + if (r) { + close(s); + goto log_sapi; + } + } + send(s, error, strlen(error), 0); + + close(s); + +log_sapi: + /* SAPI Logging activated? */ + if ((EG(hphp_log_sapi) & loglevel)!=0) { + sapi_module.log_message(buf); + } + +log_script: + /* script logging activaed? */ + if (((EG(hphp_log_script) & loglevel)!=0) && EG(hphp_log_scriptname)!=NULL) { + char cmd[8192], *cmdpos, *bufpos; + FILE *in; + int space; + + ap_php_snprintf(cmd, sizeof(cmd), "%s %s \'", EG(hphp_log_scriptname), loglevel2string(loglevel)); + space = sizeof(cmd) - strlen(cmd); + cmdpos = cmd + strlen(cmd); + bufpos = buf; + if (space <= 1) return; + while (space > 2 && *bufpos) { + if (*bufpos == '\'') { + if (space<=5) break; + *cmdpos++ = '\''; + *cmdpos++ = '\\'; + *cmdpos++ = '\''; + *cmdpos++ = '\''; + bufpos++; + space-=4; + } else { + *cmdpos++ = *bufpos++; + space--; + } + } + *cmdpos++ = '\''; + *cmdpos = 0; + + if ((in=VCWD_POPEN(cmd, "r"))==NULL) { + php_security_log(S_INTERNAL, "Unable to execute logging shell script: %s", EG(hphp_log_scriptname)); + return; + } + /* read and forget the result */ + while (1) { + int readbytes = fread(cmd, 1, sizeof(cmd), in); + if (readbytes<=0) { + break; + } + } + pclose(in); + } + +#endif +} +#endif + +#if HARDENING_PATCH_MM_PROTECT || HARDENING_PATCH_LL_PROTECT || HARDENING_PATCH_HASH_PROTECT + +/* will be replaced later with more compatible method */ +PHPAPI unsigned int php_canary() +{ + time_t t; + unsigned int canary; + int fd; + + fd = open("/dev/urandom", 0); + if (fd != -1) { + int r = read(fd, &canary, sizeof(canary)); + close(fd); + if (r == sizeof(canary)) { + return (canary); + } + } + /* not good but we never want to do this */ + time(&t); + canary = *(unsigned int *)&t + getpid() << 16; + return (canary); +} +#endif + +#if HARDENING_PATCH_INC_PROTECT + +PHPAPI int php_is_valid_include(zval *z) +{ + char *filename; + int len, i; + TSRMLS_FETCH(); + + /* must be of type string */ + if (z->type != IS_STRING || z->value.str.val == NULL) { + return (0); + } + + /* short cut */ + filename = z->value.str.val; + len = z->value.str.len; + + /* 1. must be shorter than MAXPATHLEN */ + if (len > MAXPATHLEN) { + char *fname = estrndup(filename, len); + for (i=0; i < len; i++) if (fname[i] < 32) fname[i]='.'; + php_security_log(S_INCLUDE, "Include filename ('%s') longer than MAXPATHLEN chars", fname); + efree(fname); + return (0); + } + + /* 2. must not be cutted */ + if (len != strlen(filename)) { + char *fname = estrndup(filename, len); + for (i=0; fname[i]; i++) if (fname[i] < 32) fname[i]='.'; + php_security_log(S_INCLUDE, "Include filename truncated by a \\0 after '%s'", fname); + efree(fname); + return (0); + } + + /* 3. when it is an URL first check black/whitelist if both are empty disallow all URLs */ + if (strstr(filename, "://")) { + char *fname = estrndup(filename, len); + for (i=0; i < len; i++) if (fname[i] < 32) fname[i]='.'; + + /* no black or whitelist then disallow all */ + if (HG(include_whitelist)==NULL && HG(include_blacklist)==NULL) { + php_security_log(S_INCLUDE, "Include filename ('%s') is an URL", fname); + efree(fname); + return (0); + } + + /* whitelist is stronger than blacklist */ + if (HG(include_whitelist)) { + char *s, *t, *h, *index; + uint indexlen; + ulong numindex; + + s = filename; + + do { + zend_bool isOk = 0; + int tlen; + + t = h = strstr(s, "://"); + if (h == NULL) break; + + + while (t > s && (isalnum(t[-1]) || t[-1]=='_')) { + t--; + } + + tlen = strlen(t); + + zend_hash_internal_pointer_reset(HG(include_whitelist)); + do { + int r = zend_hash_get_current_key_ex(HG(include_whitelist), &index, &indexlen, &numindex, 0, NULL); + + if (r==HASH_KEY_NON_EXISTANT) { + break; + } + if (r==HASH_KEY_IS_STRING) { + if (h-t <= indexlen-1 && tlen>=indexlen-1) { + if (strncmp(t, index, indexlen-1)==0) { + isOk = 1; + break; + } + } + } + + zend_hash_move_forward(HG(include_whitelist)); + } while (1); + + /* not found in whitelist */ + if (!isOk) { + php_security_log(S_INCLUDE, "Include filename ('%s') is an URL that is not allowed in whitelist", fname); + efree(fname); + return 0; + } + + s = h + 3; + } while (1); + } else { + /* okay then handle the blacklist */ + char *s, *t, *h, *index; + uint indexlen; + ulong numindex; + + s = filename; + + do { + int tlen; + + t = h = strstr(s, "://"); + if (h == NULL) break; + + + while (t > s && (isalnum(t[-1]) || t[-1]=='_')) { + t--; + } + + tlen = strlen(t); + + zend_hash_internal_pointer_reset(HG(include_blacklist)); + do { + int r = zend_hash_get_current_key_ex(HG(include_blacklist), &index, &indexlen, &numindex, 0, NULL); + + if (r==HASH_KEY_NON_EXISTANT) { + break; + } + if (r==HASH_KEY_IS_STRING) { + if (h-t <= indexlen-1 && tlen>=indexlen-1) { + if (strncmp(t, index, indexlen-1)==0) { + php_security_log(S_INCLUDE, "Include filename ('%s') is an URL that is forbidden by the blacklist", fname); + efree(fname); + return 0; + } + } + } + + zend_hash_move_forward(HG(include_blacklist)); + } while (1); + + s = h + 3; + } while (1); + } + + efree(fname); + } + + /* 4. must not be an uploaded file */ + if (SG(rfc1867_uploaded_files)) { + if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, len+1)) { + php_security_log(S_INCLUDE, "Include filename is an uploaded file"); + return (0); + } + } + + /* passed all tests */ + return (1); +} + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff -Nura php-5.1.4/main/hardening_patch.h hardening-patch-5.1.4-0.4.15/main/hardening_patch.h --- php-5.1.4/main/hardening_patch.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/hardening_patch.h 2006-09-07 18:51:30.000000000 +0200 @@ -0,0 +1,46 @@ +/* + +----------------------------------------------------------------------+ + | Hardening Patch for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Stefan Esser | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ + */ + +#ifndef HARDENING_PATCH_H +#define HARDENING_PATCH_H + +#include "zend.h" + +#if HARDENING_PATCH +PHPAPI void php_security_log(int loglevel, char *fmt, ...); +PHPAPI void hardened_startup(); +#define HARDENING_PATCH_VERSION "0.4.15" + +#endif + +#if HARDENING_PATCH_MM_PROTECT || HARDENING_PATCH_LL_PROTECT || HARDENING_PATCH_HASH_PROTECT +PHPAPI unsigned int php_canary(); +#endif + +#if HARDENING_PATCH_INC_PROTECT +PHPAPI int php_is_valid_include(zval *z); +#endif + +#endif /* HARDENING_PATCH_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff -Nura php-5.1.4/main/hardening_patch.m4 hardening-patch-5.1.4-0.4.15/main/hardening_patch.m4 --- php-5.1.4/main/hardening_patch.m4 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/hardening_patch.m4 2006-09-05 20:31:05.000000000 +0200 @@ -0,0 +1,95 @@ +dnl +dnl $Id: hardening_patch.m4,v 1.1 2004/11/14 13:24:24 ionic Exp $ +dnl +dnl This file contains Hardening Patch for PHP specific autoconf functions. +dnl + +AC_ARG_ENABLE(hardening-patch-mm-protect, +[ --disable-hardening-patch-mm-protect Disable the Memory Manager protection.],[ + DO_HARDENING_PATCH_MM_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_MM_PROTECT=yes +]) + +AC_ARG_ENABLE(hardening-patch-ll-protect, +[ --disable-hardening-patch-ll-protect Disable the Linked List protection.],[ + DO_HARDENING_PATCH_LL_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_LL_PROTECT=yes +]) + +AC_ARG_ENABLE(hardening-patch-inc-protect, +[ --disable-hardening-patch-inc-protect Disable include/require protection.],[ + DO_HARDENING_PATCH_INC_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_INC_PROTECT=yes +]) + +AC_ARG_ENABLE(hardening-patch-fmt-protect, +[ --disable-hardening-patch-fmt-protect Disable format string protection.],[ + DO_HARDENING_PATCH_FMT_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_FMT_PROTECT=yes +]) + +AC_ARG_ENABLE(hardening-patch-hash-protect, +[ --disable-hardening-patch-hash-protect Disable HashTable destructor protection.],[ + DO_HARDENING_PATCH_HASH_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_HASH_PROTECT=yes +]) + +AC_MSG_CHECKING(whether to protect the Zend Memory Manager) +AC_MSG_RESULT($DO_HARDENING_PATCH_MM_PROTECT) + +AC_MSG_CHECKING(whether to protect the Zend Linked Lists) +AC_MSG_RESULT($DO_HARDENING_PATCH_LL_PROTECT) + +AC_MSG_CHECKING(whether to protect include/require statements) +AC_MSG_RESULT($DO_HARDENING_PATCH_INC_PROTECT) + +AC_MSG_CHECKING(whether to protect PHP Format String functions) +AC_MSG_RESULT($DO_HARDENING_PATCH_FMT_PROTECT) + +AC_MSG_CHECKING(whether to protect the destructor of Zend HashTables) +AC_MSG_RESULT($DO_HARDENING_PATCH_HASH_PROTECT) + + +AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + + +if test "$DO_HARDENING_PATCH_MM_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_MM_PROTECT, 1, [Memory Manager Protection]) +else + AC_DEFINE(HARDENING_PATCH_MM_PROTECT, 0, [Memory Manager Protection]) +fi + +if test "$DO_HARDENING_PATCH_LL_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_LL_PROTECT, 1, [Linked List Protection]) +else + AC_DEFINE(HARDENING_PATCH_LL_PROTECT, 0, [Linked List Protection]) +fi + +if test "$DO_HARDENING_PATCH_INC_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_INC_PROTECT, 1, [Include/Require Protection]) +else + AC_DEFINE(HARDENING_PATCH_INC_PROTECT, 0, [Include/Require Protection]) +fi + +if test "$DO_HARDENING_PATCH_FMT_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_FMT_PROTECT, 1, [Fmt String Protection]) +else + AC_DEFINE(HARDENING_PATCH_FMT_PROTECT, 0, [Fmt String Protection]) +fi + +if test "$DO_HARDENING_PATCH_HASH_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_HASH_PROTECT, 1, [HashTable DTOR Protection]) +else + AC_DEFINE(HARDENING_PATCH_HASH_PROTECT, 0, [HashTable DTOR Protection]) +fi + diff -Nura php-5.1.4/main/main.c hardening-patch-5.1.4-0.4.15/main/main.c --- php-5.1.4/main/main.c 2006-04-12 14:49:39.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/main/main.c 2006-09-05 20:31:05.000000000 +0200 @@ -85,6 +85,10 @@ #include "SAPI.h" #include "rfc1867.h" +#if HARDENING_PATCH +#include "hardened_globals.h" +#endif + /* }}} */ #ifndef ZTS @@ -109,17 +113,39 @@ */ static PHP_INI_MH(OnChangeMemoryLimit) { +#if HARDENING_PATCH + long hard_memory_limit = 1<<30; + + if (stage == ZEND_INI_STAGE_RUNTIME) { + if (HG(hard_memory_limit) == 0) { + HG(hard_memory_limit) = PG(memory_limit); + } + hard_memory_limit = HG(hard_memory_limit); + } else { + HG(hard_memory_limit) = 0; + } +#endif if (new_value) { PG(memory_limit) = zend_atoi(new_value, new_value_length); +#if HARDENING_PATCH + if (PG(memory_limit) > hard_memory_limit) { + PG(memory_limit) = hard_memory_limit; + php_security_log(S_MISC, "script tried to increase memory_limit above allowed value"); + return FAILURE; + } +#endif } else { +#if HARDENING_PATCH + PG(memory_limit) = hard_memory_limit; +#else PG(memory_limit) = 1<<30; /* effectively, no limit */ +#endif } return zend_set_memory_limit(PG(memory_limit)); } /* }}} */ #endif - /* {{{ php_disable_functions */ static void php_disable_functions(TSRMLS_D) @@ -1095,6 +1121,13 @@ sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); } + /* Disable realpath cache if safe_mode or open_basedir are set */ + if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) { + CWDG(realpath_cache_disable) = 1; + } else { + CWDG(realpath_cache_disable) = 0; + } + if (PG(output_handler) && PG(output_handler)[0]) { php_start_ob_buffer_named(PG(output_handler), 0, 1 TSRMLS_CC); } else if (PG(output_buffering)) { @@ -1222,6 +1255,9 @@ zend_try { shutdown_memory_manager(CG(unclean_shutdown), 0 TSRMLS_CC); +#if HARDENING_PATCH + hardened_clear_mm_canaries(TSRMLS_C); +#endif } zend_end_try(); zend_try { @@ -1393,6 +1429,10 @@ tsrm_ls = ts_resource(0); #endif +#if HARDENING_PATCH + hardened_startup(); +#endif + module_shutdown = 0; module_startup = 1; sapi_initialize_empty_request(TSRMLS_C); @@ -1406,6 +1446,12 @@ php_output_startup(); +#if HARDENING_PATCH_INC_PROTECT + zuf.is_valid_include = php_is_valid_include; +#endif +#if HARDENING_PATCH + zuf.security_log_function = php_security_log; +#endif zuf.error_function = php_error_cb; zuf.printf_function = php_printf; zuf.write_function = php_body_write_wrapper; @@ -1476,7 +1522,9 @@ /* Disable realpath cache if safe_mode or open_basedir are set */ if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) { - CWDG(realpath_cache_size_limit) = 0; + CWDG(realpath_cache_disable) = 1; + } else { + CWDG(realpath_cache_disable) = 0; } /* initialize stream wrappers registry @@ -1517,6 +1565,10 @@ REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_PATH", PHP_CONFIG_FILE_PATH, strlen(PHP_CONFIG_FILE_PATH), CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_SCAN_DIR", PHP_CONFIG_FILE_SCAN_DIR, sizeof(PHP_CONFIG_FILE_SCAN_DIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_SHLIB_SUFFIX", PHP_SHLIB_SUFFIX, sizeof(PHP_SHLIB_SUFFIX)-1, CONST_PERSISTENT | CONST_CS); +#if HARDENING_PATCH + REGISTER_MAIN_LONG_CONSTANT("HARDENING_PATCH", 1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("HARDENING_PATCH_VERSION", HARDENING_PATCH_VERSION, sizeof(HARDENING_PATCH_VERSION)-1, CONST_PERSISTENT | CONST_CS); +#endif REGISTER_MAIN_STRINGL_CONSTANT("PHP_EOL", PHP_EOL, sizeof(PHP_EOL)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_INT_MAX", LONG_MAX, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_INT_SIZE", sizeof(long), CONST_PERSISTENT | CONST_CS); diff -Nura php-5.1.4/main/php_config.h.in hardening-patch-5.1.4-0.4.15/main/php_config.h.in --- php-5.1.4/main/php_config.h.in 2006-05-12 16:41:13.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/main/php_config.h.in 2006-09-05 20:31:05.000000000 +0200 @@ -788,6 +788,39 @@ /* Enabling BIND8 compatibility for Panther */ #undef BIND_8_COMPAT +/* Hardening-Patch for PHP */ +#undef HARDENING_PATCH + +/* Memory Manager Protection */ +#undef HARDENING_PATCH_MM_PROTECT + +/* Memory Manager Protection */ +#undef HARDENING_PATCH_MM_PROTECT + +/* Linked List Protection */ +#undef HARDENING_PATCH_LL_PROTECT + +/* Linked List Protection */ +#undef HARDENING_PATCH_LL_PROTECT + +/* Include/Require Protection */ +#undef HARDENING_PATCH_INC_PROTECT + +/* Include/Require Protection */ +#undef HARDENING_PATCH_INC_PROTECT + +/* Fmt String Protection */ +#undef HARDENING_PATCH_FMT_PROTECT + +/* Fmt String Protection */ +#undef HARDENING_PATCH_FMT_PROTECT + +/* HashTable DTOR Protection */ +#undef HARDENING_PATCH_HASH_PROTECT + +/* HashTable DTOR Protection */ +#undef HARDENING_PATCH_HASH_PROTECT + /* Whether you have AOLserver */ #undef HAVE_AOLSERVER @@ -1131,6 +1164,12 @@ /* Define if you have the getaddrinfo function */ #undef HAVE_GETADDRINFO +/* Whether realpath is broken */ +#undef PHP_BROKEN_REALPATH + +/* Whether realpath is broken */ +#undef PHP_BROKEN_REALPATH + /* Whether system headers declare timezone */ #undef HAVE_DECLARED_TIMEZONE diff -Nura php-5.1.4/main/php.h hardening-patch-5.1.4-0.4.15/main/php.h --- php-5.1.4/main/php.h 2006-03-07 23:37:53.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/php.h 2006-09-05 20:31:05.000000000 +0200 @@ -35,11 +35,19 @@ #include "zend_qsort.h" #include "php_compat.h" + #include "zend_API.h" #undef sprintf #define sprintf php_sprintf +#if HARDENING_PATCH +#if HAVE_REALPATH +#undef realpath +#define realpath php_realpath +#endif +#endif + /* PHP's DEBUG value must match Zend's ZEND_DEBUG value */ #undef PHP_DEBUG #define PHP_DEBUG ZEND_DEBUG @@ -338,6 +346,7 @@ #define PHP_FUNCTION ZEND_FUNCTION #define PHP_METHOD ZEND_METHOD +#define PHP_STATIC_FE ZEND_STATIC_FE #define PHP_NAMED_FE ZEND_NAMED_FE #define PHP_FE ZEND_FE #define PHP_DEP_FE ZEND_DEP_FE @@ -447,6 +456,10 @@ #endif #endif /* !XtOffsetOf */ +#if HARDENING_PATCH +#include "hardening_patch.h" +#endif + #endif /* diff -Nura php-5.1.4/main/php_open_temporary_file.c hardening-patch-5.1.4-0.4.15/main/php_open_temporary_file.c --- php-5.1.4/main/php_open_temporary_file.c 2006-01-01 13:50:17.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/php_open_temporary_file.c 2006-09-05 20:31:05.000000000 +0200 @@ -114,17 +114,16 @@ path_len = strlen(path); - if (!(opened_path = emalloc(MAXPATHLEN))) { - return -1; - } - if (!path_len || IS_SLASH(path[path_len - 1])) { trailing_slash = ""; } else { trailing_slash = "/"; } - (void)snprintf(opened_path, MAXPATHLEN, "%s%s%sXXXXXX", path, trailing_slash, pfx); + if (spprintf(&opened_path, 0, "%s%s%sXXXXXX", path, trailing_slash, pfx) >= MAXPATHLEN) { + efree(opened_path); + return -1; + } #ifdef PHP_WIN32 if (GetTempFileName(path, pfx, 0, opened_path)) { diff -Nura php-5.1.4/main/php_variables.c hardening-patch-5.1.4-0.4.15/main/php_variables.c --- php-5.1.4/main/php_variables.c 2006-05-03 13:24:29.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/main/php_variables.c 2006-09-05 20:31:05.000000000 +0200 @@ -73,6 +73,10 @@ symtable1 = Z_ARRVAL_P(track_vars_array); } else if (PG(register_globals)) { symtable1 = EG(active_symbol_table); + /* GLOBALS hijack attempt, reject parameter */ + if (!strncmp("GLOBALS", var, sizeof("GLOBALS")) || !strncmp("GLOBALS", var, sizeof("GLOBALS[")-1)) { + symtable1 = NULL; + } } if (!symtable1) { /* Nothing to do */ @@ -513,7 +517,7 @@ */ static inline void php_register_server_variables(TSRMLS_D) { - zval *array_ptr = NULL; + zval *array_ptr = NULL, *vptr; /* turn off magic_quotes while importing server variables */ int magic_quotes_gpc = PG(magic_quotes_gpc); diff -Nura php-5.1.4/main/rfc1867.c hardening-patch-5.1.4-0.4.15/main/rfc1867.c --- php-5.1.4/main/rfc1867.c 2006-01-01 13:50:17.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/rfc1867.c 2006-09-05 20:31:05.000000000 +0200 @@ -132,6 +132,7 @@ #define UPLOAD_ERROR_D 4 /* No file uploaded */ #define UPLOAD_ERROR_E 6 /* Missing /tmp or similar directory */ #define UPLOAD_ERROR_F 7 /* Failed to write file to disk */ +#define UPLOAD_ERROR_X 32 /* Filter forbids fileupload */ void php_rfc1867_register_constants(TSRMLS_D) { @@ -142,6 +143,7 @@ REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_FILE", UPLOAD_ERROR_D, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_TMP_DIR", UPLOAD_ERROR_E, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_CANT_WRITE", UPLOAD_ERROR_F, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_FILTER", UPLOAD_ERROR_X, CONST_CS | CONST_PERSISTENT); } static void normalize_protected_variable(char *varname TSRMLS_DC) @@ -854,6 +856,7 @@ char buff[FILLUNIT]; char *cd=NULL,*param=NULL,*filename=NULL, *tmp=NULL; int blen=0, wlen=0; + unsigned long offset; zend_llist_clean(&header); @@ -970,7 +973,11 @@ tmp++; } } - + + if (sapi_module.upload_varname_filter && sapi_module.upload_varname_filter(param TSRMLS_CC)==FAILURE) { + skip_upload = 1; + } + total_bytes = cancel_upload = 0; if (!skip_upload) { @@ -994,6 +1001,11 @@ cancel_upload = UPLOAD_ERROR_D; } + if (sapi_module.pre_upload_filter && sapi_module.pre_upload_filter(param, filename TSRMLS_CC)==FAILURE) { + cancel_upload = UPLOAD_ERROR_X; + } + + offset = 0; end = 0; while (!cancel_upload && (blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end TSRMLS_CC))) { @@ -1008,6 +1020,10 @@ #endif cancel_upload = UPLOAD_ERROR_B; } else if (blen > 0) { + if (sapi_module.upload_content_filter && sapi_module.upload_content_filter(offset, buff, blen, &blen TSRMLS_CC)==FAILURE) { + cancel_upload = UPLOAD_ERROR_X; + } + wlen = write(fd, buff, blen); if (wlen < blen) { @@ -1036,6 +1052,10 @@ } #endif + if (!cancel_upload && sapi_module.post_upload_filter && sapi_module.post_upload_filter(temp_filename TSRMLS_CC)==FAILURE) { + cancel_upload = UPLOAD_ERROR_X; + } + if (cancel_upload) { if (temp_filename) { if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */ diff -Nura php-5.1.4/main/SAPI.c hardening-patch-5.1.4-0.4.15/main/SAPI.c --- php-5.1.4/main/SAPI.c 2006-01-01 13:50:17.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/SAPI.c 2006-09-05 20:31:05.000000000 +0200 @@ -870,6 +870,36 @@ post_entry->content_type_len+1); } +SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC)) +{ + sapi_module.input_filter = input_filter; + return SUCCESS; +} + +SAPI_API int sapi_register_upload_varname_filter(unsigned int (*upload_varname_filter)(char *varname TSRMLS_DC)) +{ + sapi_module.upload_varname_filter = upload_varname_filter; + return SUCCESS; +} + +SAPI_API int sapi_register_pre_upload_filter(unsigned int (*pre_upload_filter)(char *varname, char *filename TSRMLS_DC)) +{ + sapi_module.pre_upload_filter = pre_upload_filter; + return SUCCESS; +} + +SAPI_API int sapi_register_upload_content_filter(unsigned int (*upload_content_filter)(unsigned long offset, char *buffer, unsigned int buffer_len, unsigned int *new_buffer_len TSRMLS_DC)) +{ + sapi_module.upload_content_filter = upload_content_filter; + return SUCCESS; +} + +SAPI_API int sapi_register_post_upload_filter(unsigned int (*post_upload_filter)(char *tmpfilename TSRMLS_DC)) +{ + sapi_module.post_upload_filter = post_upload_filter; + return SUCCESS; +} + SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D)) { @@ -884,11 +914,6 @@ return SUCCESS; } -SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC)) -{ - sapi_module.input_filter = input_filter; - return SUCCESS; -} SAPI_API int sapi_flush(TSRMLS_D) { diff -Nura php-5.1.4/main/SAPI.h hardening-patch-5.1.4-0.4.15/main/SAPI.h --- php-5.1.4/main/SAPI.h 2006-01-01 13:50:17.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/SAPI.h 2006-09-05 20:31:05.000000000 +0200 @@ -190,6 +190,10 @@ SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC)); SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC)); +SAPI_API int sapi_register_pre_upload_filter(unsigned int (*pre_upload_filter)(char *varname, char *filename TSRMLS_DC)); +SAPI_API int sapi_register_upload_content_filter(unsigned int (*upload_content_filter)(unsigned long offset, char *buffer, unsigned int buffer_len, unsigned int *new_buffer_len TSRMLS_DC)); +SAPI_API int sapi_register_post_upload_filter(unsigned int (*post_upload_filter)(char *tmpfilename TSRMLS_DC)); + SAPI_API int sapi_flush(TSRMLS_D); SAPI_API struct stat *sapi_get_stat(TSRMLS_D); SAPI_API char *sapi_getenv(char *name, size_t name_len TSRMLS_DC); @@ -254,6 +258,11 @@ int (*get_target_gid)(gid_t * TSRMLS_DC); unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC); + + unsigned int (*upload_varname_filter)(char *varname TSRMLS_DC); + unsigned int (*pre_upload_filter)(char *varname, char *filename TSRMLS_DC); + unsigned int (*upload_content_filter)(unsigned long offset, char *buffer, unsigned int buffer_len, unsigned int *new_buffer_len TSRMLS_DC); + unsigned int (*post_upload_filter)(char *tmpfilename TSRMLS_DC); void (*ini_defaults)(HashTable *configuration_hash); int phpinfo_as_text; @@ -279,7 +288,11 @@ #define SAPI_DEFAULT_MIMETYPE "text/html" #define SAPI_DEFAULT_CHARSET "" +#if HARDENING_PATCH +#define SAPI_PHP_VERSION_HEADER "X-Powered-By: PHP/" PHP_VERSION " with Hardening-Patch" +#else #define SAPI_PHP_VERSION_HEADER "X-Powered-By: PHP/" PHP_VERSION +#endif #define SAPI_POST_READER_FUNC(post_reader) void post_reader(TSRMLS_D) #define SAPI_POST_HANDLER_FUNC(post_handler) void post_handler(char *content_type_dup, void *arg TSRMLS_DC) @@ -287,6 +300,11 @@ #define SAPI_TREAT_DATA_FUNC(treat_data) void treat_data(int arg, char *str, zval* destArray TSRMLS_DC) #define SAPI_INPUT_FILTER_FUNC(input_filter) unsigned int input_filter(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC) +#define SAPI_UPLOAD_VARNAME_FILTER_FUNC(upload_varname_filter) unsigned int upload_varname_filter(char *varname TSRMLS_DC) +#define SAPI_PRE_UPLOAD_FILTER_FUNC(pre_upload_filter) unsigned int pre_upload_filter(char *varname, char *filename TSRMLS_DC) +#define SAPI_UPLOAD_CONTENT_FILTER_FUNC(upload_content_filter) unsigned int upload_content_filter(unsigned long offset, char *buffer, unsigned int buffer_len, unsigned int *new_buffer_len TSRMLS_DC) +#define SAPI_POST_UPLOAD_FILTER_FUNC(post_upload_filter) unsigned int post_upload_filter(char *tmpfilename TSRMLS_DC) + BEGIN_EXTERN_C() SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data); SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader); diff -Nura php-5.1.4/main/snprintf.c hardening-patch-5.1.4-0.4.15/main/snprintf.c --- php-5.1.4/main/snprintf.c 2006-01-24 21:59:46.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/snprintf.c 2006-09-05 20:31:05.000000000 +0200 @@ -1014,7 +1014,11 @@ case 'n': +#if HARDENING_PATCH_FMT_PROTECT + php_security_log(S_MISC, "'n' specifier within format string"); +#else *(va_arg(ap, int *)) = cc; +#endif goto skip_output; /* diff -Nura php-5.1.4/main/spprintf.c hardening-patch-5.1.4-0.4.15/main/spprintf.c --- php-5.1.4/main/spprintf.c 2006-01-24 21:59:46.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/main/spprintf.c 2006-09-05 20:31:05.000000000 +0200 @@ -630,7 +630,11 @@ case 'n': +#if HARDENING_PATCH_FMT_PROTECT + php_security_log(S_MISC, "'n' specifier within format string"); +#else *(va_arg(ap, int *)) = xbuf->len; +#endif goto skip_output; /* diff -Nura php-5.1.4/pear/Makefile.frag hardening-patch-5.1.4-0.4.15/pear/Makefile.frag --- php-5.1.4/pear/Makefile.frag 2006-02-08 02:12:12.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/pear/Makefile.frag 2006-09-05 20:31:05.000000000 +0200 @@ -3,7 +3,7 @@ peardir=$(PEAR_INSTALLDIR) # Skip all php.ini files altogether -PEAR_INSTALL_FLAGS = -n -dshort_open_tag=0 -dsafe_mode=0 -derror_reporting=E_ALL -dmemory_limit=-1 -ddetect_unicode=0 +PEAR_INSTALL_FLAGS = -n -dshort_open_tag=0 -dsafe_mode=0 -derror_reporting=E_ALL -dmemory_limit=-1 -ddetect_unicode=0 -dhphp.executor.include.whitelist=phar install-pear-installer: $(SAPI_CLI_PATH) @$(top_builddir)/sapi/cli/php $(PEAR_INSTALL_FLAGS) $(builddir)/install-pear-nozlib.phar -d "$(peardir)" -b "$(bindir)" diff -Nura php-5.1.4/php.ini-dist hardening-patch-5.1.4-0.4.15/php.ini-dist --- php-5.1.4/php.ini-dist 2006-02-09 00:43:48.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/php.ini-dist 2006-09-05 20:31:05.000000000 +0200 @@ -1197,6 +1197,209 @@ ; instead of original one. soap.wsdl_cache_ttl=86400 +[hardening-patch] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; +; hphp.log.syslog - Configures level for alerts reported through syslog +; hphp.log.sapi - Configures level for alerts reported through SAPI errorlog +; hphp.log.script - Configures level for alerts reported through external script +; +; hphp.log.syslog, hphp.log.sapi, hphp.log.script are bit-fields. +; Or each number up to get desired Hardening-Patch's reporting level +; +; S_ALL - All alerts +; S_MEMORY - All canary violations and the safe unlink protection use this class +; S_VARS - All variable filters trigger this class +; S_FILES - All violation of uploaded files filter use this class +; S_INCLUDE - The protection against malicious include filenames use this class +; S_SQL - Failed SQL queries in MySQL are logged with this class +; S_EXECUTOR - The execution depth protection uses this logging class +; S_MISC - All other log messages (f.e. format string protection) use this class +; +; Example: +; +; - Report all alerts (except memory alerts) to the SAPI errorlog, +; memory alerts through syslog and SQL+Include alerts fo the script +; +;hphp.log.syslog = S_MEMORY +;hphp.log.sapi = S_ALL & ~S_MEMORY +;hphp.log.script = S_INCLUDE | S_SQL +; +; Syslog logging: +; +; - Facility configuration: one of the following facilities +; +; LOG_KERN, LOG_USER, LOG_MAIL, LOG_DAEMON +; LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS +; LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_LOCAL0 +; LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4 +; LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_PID +; LOG_CONS, LOG_ODELAY, LOG_NDELAY, LOG_NOWAIT +; LOG_PERROR +; +; - Priority configuration: one of the followinf priorities +; +; LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_WARNING +; LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_ERR +; +hphp.log.syslog.priority = LOG_ALERT +hphp.log.syslog.facility = LOG_USER +; +; Script logging: +; +;hphp.log.script.name = /home/hphp/log_script +; +; Alert configuration: +; +; - Logged IP addresses from X-Forwarded-For instead of REMOTE_ADDR +; +;hphp.log.use-x-forwarded-for = On +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's Executor options ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Execution depth limit +;hphp.executor.max_depth = 8000 + +; White-/blacklist for function calls during normal execution +;hphp.executor.func.whitelist = ord,chr +;hphp.executor.func.blacklist = system,shell_exec,popen,proc_open,exec,passthru + +; White-/blacklist for function calls during eval() execution +;hphp.executor.eval.whitelist = ord,chr +;hphp.executor.eval.blacklist = system,shell_exec,popen,proc_open,exec,passthru + +; White-/blacklist for URLs allowes in include filenames +; +; - When both options are not set all URLs are forbidden +; +; - When both options are set whitelist is taken and blacklist ignored +; +; - An entry in the lists is either a URL sheme like: http, https +; or the beginning of an URL like: php://input +; +;hphp.executor.include.whitelist = cookietest +;hphp.executor.include.blacklist = http, https, ftp, ftps, php://input, file + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's REQUEST variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of REQUEST variables +hphp.request.max_vars = 200 + +; Limits the length of variable names (without indices) +hphp.request.max_varname_length = 64 + +; Limits the length of complete variable names (with indices) +hphp.request.max_totalname_length = 256 + +; Limits the length of array indices +hphp.request.max_array_index_length = 64 + +; Limits the depth of arrays +hphp.request.max_array_depth = 100 + +; Limits the length of variable values +hphp.request.max_value_length = 65000 + +; Disallow ASCII-NUL characters in input +hphp.request.disallow_nul = 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's COOKIE variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of COOKIE variables +hphp.cookie.max_vars = 100 + +; Limits the length of variable names (without indices) +hphp.cookie.max_name_length = 64 + +; Limits the length of complete variable names (with indices) +hphp.cookie.max_totalname_length = 256 + +; Limits the length of array indices +hphp.cookie.max_array_index_length = 64 + +; Limits the depth of arrays +hphp.cookie.max_array_depth = 100 + +; Limits the length of variable values +hphp.cookie.max_value_length = 10000 + +; Disallow ASCII-NUL characters in input +hphp.cookie.disallow_nul = 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's GET variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of COOKIE variables +hphp.get.max_vars = 100 + +; Limits the length of variable names (without indices) +hphp.get.max_name_length = 64 + +; Limits the length of complete variable names (with indices) +hphp.get.max_totalname_length = 256 + +; Limits the length of array indices +hphp.get.max_array_index_length = 64 + +; Limits the depth of arrays +hphp.get.max_array_depth = 50 + +; Limits the length of variable values +hphp.get.max_value_length = 512 + +; Disallow ASCII-NUL characters in input +hphp.get.disallow_nul = 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's POST variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of POST variables +hphp.post.max_vars = 200 + +; Limits the length of variable names (without indices) +hphp.post.max_name_length = 64 + +; Limits the length of complete variable names (with indices) +hphp.post.max_totalname_length = 256 + +; Limits the length of array indices +hphp.post.max_array_index_length = 64 + +; Limits the depth of arrays +hphp.post.max_array_depth = 100 + +; Limits the length of variable values +hphp.post.max_value_length = 65000 + +; Disallow ASCII-NUL characters in input +hphp.post.disallow_nul = 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's fileupload variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of uploadable files +hphp.upload.max_uploads = 25 + +; Filter out the upload of ELF executables +hphp.upload.disallow_elf_files = On + +; External filterscript for upload verification +;hphp.upload.verification_script = /home/hphp/verify_script + + ; Local Variables: ; tab-width: 4 ; End: diff -Nura php-5.1.4/php.ini-recommended hardening-patch-5.1.4-0.4.15/php.ini-recommended --- php-5.1.4/php.ini-recommended 2006-02-09 00:43:48.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/php.ini-recommended 2006-09-05 20:31:06.000000000 +0200 @@ -1255,6 +1255,209 @@ ; instead of original one. soap.wsdl_cache_ttl=86400 +[hardening-patch] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; +; hphp.log.syslog - Configures level for alerts reported through syslog +; hphp.log.sapi - Configures level for alerts reported through SAPI errorlog +; hphp.log.script - Configures level for alerts reported through external script +; +; hphp.log.syslog, hphp.log.sapi, hphp.log.script are bit-fields. +; Or each number up to get desired Hardening-Patch's reporting level +; +; S_ALL - All alerts +; S_MEMORY - All canary violations and the safe unlink protection use this class +; S_VARS - All variable filters trigger this class +; S_FILES - All violation of uploaded files filter use this class +; S_INCLUDE - The protection against malicious include filenames use this class +; S_SQL - Failed SQL queries in MySQL are logged with this class +; S_EXECUTOR - The execution depth protection uses this logging class +; S_MISC - All other log messages (f.e. format string protection) use this class +; +; Example: +; +; - Report all alerts (except memory alerts) to the SAPI errorlog, +; memory alerts through syslog and SQL+Include alerts fo the script +; +;hphp.log.syslog = S_MEMORY +;hphp.log.sapi = S_ALL & ~S_MEMORY +;hphp.log.script = S_INCLUDE | S_SQL +; +; Syslog logging: +; +; - Facility configuration: one of the following facilities +; +; LOG_KERN, LOG_USER, LOG_MAIL, LOG_DAEMON +; LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS +; LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_LOCAL0 +; LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4 +; LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_PID +; LOG_CONS, LOG_ODELAY, LOG_NDELAY, LOG_NOWAIT +; LOG_PERROR +; +; - Priority configuration: one of the followinf priorities +; +; LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_WARNING +; LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_ERR +; +hphp.log.syslog.priority = LOG_ALERT +hphp.log.syslog.facility = LOG_USER +; +; Script logging: +; +;hphp.log.script.name = /home/hphp/log_script +; +; Alert configuration: +; +; - Logged IP addresses from X-Forwarded-For instead of REMOTE_ADDR +; +;hphp.log.use-x-forwarded-for = On +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's Executor options ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Execution depth limit +;hphp.executor.max_depth = 8000 + +; White-/blacklist for function calls during normal execution +;hphp.executor.func.whitelist = ord,chr +;hphp.executor.func.blacklist = system,shell_exec,popen,proc_open,exec,passthru + +; White-/blacklist for function calls during eval() execution +;hphp.executor.eval.whitelist = ord,chr +;hphp.executor.eval.blacklist = system,shell_exec,popen,proc_open,exec,passthru + +; White-/blacklist for URLs allowes in include filenames +; +; - When both options are not set all URLs are forbidden +; +; - When both options are set whitelist is taken and blacklist ignored +; +; - An entry in the lists is either a URL sheme like: http, https +; or the beginning of an URL like: php://input +; +;hphp.executor.include.whitelist = cookietest +;hphp.executor.include.blacklist = http, https, ftp, ftps, php://input, file + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's REQUEST variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of REQUEST variables +hphp.request.max_vars = 200 + +; Limits the length of variable names (without indices) +hphp.request.max_varname_length = 64 + +; Limits the length of complete variable names (with indices) +hphp.request.max_totalname_length = 256 + +; Limits the length of array indices +hphp.request.max_array_index_length = 64 + +; Limits the depth of arrays +hphp.request.max_array_depth = 100 + +; Limits the length of variable values +hphp.request.max_value_length = 65000 + +; Disallow ASCII-NUL characters in input +hphp.request.disallow_nul = 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's COOKIE variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of COOKIE variables +hphp.cookie.max_vars = 100 + +; Limits the length of variable names (without indices) +hphp.cookie.max_name_length = 64 + +; Limits the length of complete variable names (with indices) +hphp.cookie.max_totalname_length = 256 + +; Limits the length of array indices +hphp.cookie.max_array_index_length = 64 + +; Limits the depth of arrays +hphp.cookie.max_array_depth = 100 + +; Limits the length of variable values +hphp.cookie.max_value_length = 10000 + +; Disallow ASCII-NUL characters in input +hphp.cookie.disallow_nul = 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's GET variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of COOKIE variables +hphp.get.max_vars = 100 + +; Limits the length of variable names (without indices) +hphp.get.max_name_length = 64 + +; Limits the length of complete variable names (with indices) +hphp.get.max_totalname_length = 256 + +; Limits the length of array indices +hphp.get.max_array_index_length = 64 + +; Limits the depth of arrays +hphp.get.max_array_depth = 50 + +; Limits the length of variable values +hphp.get.max_value_length = 512 + +; Disallow ASCII-NUL characters in input +hphp.get.disallow_nul = 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's POST variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of POST variables +hphp.post.max_vars = 200 + +; Limits the length of variable names (without indices) +hphp.post.max_name_length = 64 + +; Limits the length of complete variable names (with indices) +hphp.post.max_totalname_length = 256 + +; Limits the length of array indices +hphp.post.max_array_index_length = 64 + +; Limits the depth of arrays +hphp.post.max_array_depth = 100 + +; Limits the length of variable values +hphp.post.max_value_length = 65000 + +; Disallow ASCII-NUL characters in input +hphp.post.disallow_nul = 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's fileupload variable filters ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Limits the number of uploadable files +hphp.upload.max_uploads = 25 + +; Filter out the upload of ELF executables +hphp.upload.disallow_elf_files = On + +; External filterscript for upload verification +;hphp.upload.verification_script = /home/hphp/verify_script + + ; Local Variables: ; tab-width: 4 ; End: diff -Nura php-5.1.4/run-tests.php hardening-patch-5.1.4-0.4.15/run-tests.php --- php-5.1.4/run-tests.php 2006-05-03 23:37:16.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/run-tests.php 2006-09-05 20:31:06.000000000 +0200 @@ -161,6 +161,10 @@ 'error_reporting=4095', 'display_errors=1', 'log_errors=0', + 'hphp.executor.include.whitelist=cookietest', + 'hphp.log.syslog=0', + 'hphp.log.sapi=0', + 'hphp.log.script=0', 'html_errors=0', 'track_errors=1', 'report_memleaks=1', diff -Nura php-5.1.4/sapi/apache/mod_php5.c hardening-patch-5.1.4-0.4.15/sapi/apache/mod_php5.c --- php-5.1.4/sapi/apache/mod_php5.c 2006-04-02 19:58:17.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/sapi/apache/mod_php5.c 2006-09-05 20:31:06.000000000 +0200 @@ -482,7 +482,7 @@ sapi_apache_get_fd, sapi_apache_force_http_10, sapi_apache_get_target_uid, - sapi_apache_get_target_gid + sapi_apache_get_target_gid, }; /* }}} */ @@ -936,7 +936,11 @@ { TSRMLS_FETCH(); if (PG(expose_php)) { +#if HARDENING_PATCH + ap_add_version_component("PHP/" PHP_VERSION " with Hardening-Patch"); +#else ap_add_version_component("PHP/" PHP_VERSION); +#endif } } #endif diff -Nura php-5.1.4/sapi/apache2filter/sapi_apache2.c hardening-patch-5.1.4-0.4.15/sapi/apache2filter/sapi_apache2.c --- php-5.1.4/sapi/apache2filter/sapi_apache2.c 2006-03-19 15:54:53.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/sapi/apache2filter/sapi_apache2.c 2006-09-05 20:31:06.000000000 +0200 @@ -573,7 +573,11 @@ { TSRMLS_FETCH(); if (PG(expose_php)) { +#if HARDENING_PATCH + ap_add_version_component(p, "PHP/" PHP_VERSION " with Hardening-Patch"); +#else ap_add_version_component(p, "PHP/" PHP_VERSION); +#endif } } diff -Nura php-5.1.4/sapi/apache2handler/sapi_apache2.c hardening-patch-5.1.4-0.4.15/sapi/apache2handler/sapi_apache2.c --- php-5.1.4/sapi/apache2handler/sapi_apache2.c 2006-03-19 15:54:53.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/sapi/apache2handler/sapi_apache2.c 2006-09-05 20:31:06.000000000 +0200 @@ -341,7 +341,11 @@ { TSRMLS_FETCH(); if (PG(expose_php)) { +#if HARDENING_PATCH + ap_add_version_component(p, "PHP/" PHP_VERSION " with Hardening-Patch"); +#else ap_add_version_component(p, "PHP/" PHP_VERSION); +#endif } } diff -Nura php-5.1.4/sapi/cgi/cgi_main.c hardening-patch-5.1.4-0.4.15/sapi/cgi/cgi_main.c --- php-5.1.4/sapi/cgi/cgi_main.c 2006-05-03 21:40:58.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/sapi/cgi/cgi_main.c 2006-09-05 20:31:06.000000000 +0200 @@ -1444,10 +1444,18 @@ SG(headers_sent) = 1; SG(request_info).no_headers = 1; } +#if HARDENING_PATCH #if ZEND_DEBUG - php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2006 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); + php_printf("PHP %s with Hardening-Patch %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2004 The PHP Group\n%s", PHP_VERSION, HARDENING_PATCH_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); #else - php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2006 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); + php_printf("PHP %s with Hardening-Patch %s (%s) (built: %s %s)\nCopyright (c) 1997-2004 The PHP Group\n%s", PHP_VERSION, HARDENING_PATCH_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); +#endif +#else +#if ZEND_DEBUG + php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2004 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); +#else + php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2004 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); +#endif #endif php_end_ob_buffers(1 TSRMLS_CC); exit(0); diff -Nura php-5.1.4/sapi/cli/php_cli.c hardening-patch-5.1.4-0.4.15/sapi/cli/php_cli.c --- php-5.1.4/sapi/cli/php_cli.c 2006-02-21 22:15:13.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/sapi/cli/php_cli.c 2006-09-05 20:31:06.000000000 +0200 @@ -753,8 +753,14 @@ goto err; } +#if HARDENING_PATCH + php_printf("PHP %s with Hardening-Patch %s (%s) (built: %s %s) %s\nCopyright (c) 1997-2006 The PHP Group\n%s", + PHP_VERSION, HARDENING_PATCH_VERSION, +#else php_printf("PHP %s (%s) (built: %s %s) %s\nCopyright (c) 1997-2006 The PHP Group\n%s", - PHP_VERSION, sapi_module.name, __DATE__, __TIME__, + PHP_VERSION, +#endif + sapi_module.name, __DATE__, __TIME__, #if ZEND_DEBUG && defined(HAVE_GCOV) "(DEBUG GCOV)", #elif ZEND_DEBUG diff -Nura php-5.1.4/TSRM/TSRM.h hardening-patch-5.1.4-0.4.15/TSRM/TSRM.h --- php-5.1.4/TSRM/TSRM.h 2006-03-14 16:16:07.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/TSRM/TSRM.h 2006-09-05 20:31:06.000000000 +0200 @@ -33,6 +33,13 @@ # define TSRM_API #endif +#if HARDENING_PATCH +# if HAVE_REALPATH +# undef realpath +# define realpath php_realpath +# endif +#endif + /* Only compile multi-threading functions if we're in ZTS mode */ #ifdef ZTS @@ -88,6 +95,7 @@ #define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts + #ifdef __cplusplus extern "C" { #endif diff -Nura php-5.1.4/TSRM/tsrm_virtual_cwd.c hardening-patch-5.1.4-0.4.15/TSRM/tsrm_virtual_cwd.c --- php-5.1.4/TSRM/tsrm_virtual_cwd.c 2006-03-05 19:57:54.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/TSRM/tsrm_virtual_cwd.c 2006-09-05 20:31:06.000000000 +0200 @@ -163,6 +163,7 @@ static void cwd_globals_ctor(virtual_cwd_globals *cwd_globals TSRMLS_DC) { CWD_STATE_COPY(&cwd_globals->cwd, &main_cwd_state); + cwd_globals->realpath_cache_disable = 0; cwd_globals->realpath_cache_size = 0; cwd_globals->realpath_cache_size_limit = REALPATH_CACHE_SIZE; cwd_globals->realpath_cache_ttl = REALPATH_CACHE_TTL; @@ -201,6 +202,176 @@ return p; } +#if HARDENING_PATCH +CWD_API char *php_realpath(const char *path, char *resolved) +{ + struct stat sb; + char *p, *q, *s; + size_t left_len, resolved_len; + unsigned symlinks; + int serrno, slen; + int is_dir = 1; + char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; + + serrno = errno; + symlinks = 0; + if (path[0] == '/') { + resolved[0] = '/'; + resolved[1] = '\0'; + if (path[1] == '\0') + return (resolved); + resolved_len = 1; + left_len = strlcpy(left, path + 1, sizeof(left)); + } else { + if (getcwd(resolved, PATH_MAX) == NULL) { + strlcpy(resolved, ".", PATH_MAX); + return (NULL); + } + resolved_len = strlen(resolved); + left_len = strlcpy(left, path, sizeof(left)); + } + if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + + /* + * Iterate over path components in `left'. + */ + while (left_len != 0) { + /* + * Extract the next path component and adjust `left' + * and its length. + */ + p = strchr(left, '/'); + s = p ? p : left + left_len; + if (s - left >= sizeof(next_token)) { + errno = ENAMETOOLONG; + return (NULL); + } + memcpy(next_token, left, s - left); + next_token[s - left] = '\0'; + left_len -= s - left; + if (p != NULL) + memmove(left, s + 1, left_len + 1); + if (resolved[resolved_len - 1] != '/') { + if (resolved_len + 1 >= PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + resolved[resolved_len++] = '/'; + resolved[resolved_len] = '\0'; + } + if (next_token[0] == '\0') + continue; + else if (strcmp(next_token, ".") == 0) + continue; + else if (strcmp(next_token, "..") == 0) { + /* + * Strip the last path component except when we have + * single "/" + */ + if (!is_dir) { + errno = ENOENT; + return (NULL); + } + if (resolved_len > 1) { + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/'); + *q = '\0'; + resolved_len = q - resolved; + } + continue; + } + + /* + * Append the next path component and lstat() it. If + * lstat() fails we still can return successfully if + * there are no more path components left. + */ + resolved_len = strlcat(resolved, next_token, PATH_MAX); + if (resolved_len >= PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + if (lstat(resolved, &sb) != 0) { + if (errno == ENOENT) { + if (p == NULL) { + errno = serrno; + return (resolved); + } else if (strstr(left, "/.") == NULL && strstr(left, "./") == NULL) { + resolved_len = strlcat(resolved, "/", PATH_MAX); + resolved_len = strlcat(resolved, left, PATH_MAX); + if (resolved_len >= PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + errno = serrno; + return (resolved); + } + } + return (NULL); + } + if (S_ISLNK(sb.st_mode)) { + if (symlinks++ > MAXSYMLINKS) { + errno = ELOOP; + return (NULL); + } + slen = readlink(resolved, symlink, sizeof(symlink) - 1); + if (slen < 0) + return (NULL); + symlink[slen] = '\0'; + if (symlink[0] == '/') { + resolved[1] = 0; + resolved_len = 1; + } else if (resolved_len > 1) { + /* Strip the last path component. */ + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/'); + *q = '\0'; + resolved_len = q - resolved; + } + + /* + * If there are any path components left, then + * append them to symlink. The result is placed + * in `left'. + */ + if (p != NULL) { + if (symlink[slen - 1] != '/') { + if (slen + 1 >= sizeof(symlink)) { + errno = ENAMETOOLONG; + return (NULL); + } + symlink[slen] = '/'; + symlink[slen + 1] = 0; + } + left_len = strlcat(symlink, left, sizeof(left)); + if (left_len >= sizeof(left)) { + errno = ENAMETOOLONG; + return (NULL); + } + } + left_len = strlcpy(left, symlink, sizeof(left)); + } else { + if (S_ISDIR(sb.st_mode)) { + is_dir = 1; + } else { + is_dir = 0; + } + } + } + + /* + * Remove trailing slash except when the resolved pathname + * is a single "/". + */ + if (resolved_len > 1 && resolved[resolved_len - 1] == '/') + resolved[resolved_len - 1] = '\0'; + return (resolved); +} +#endif + CWD_API void virtual_cwd_startup(void) { char cwd[MAXPATHLEN]; @@ -381,22 +552,33 @@ #endif char orig_path[MAXPATHLEN]; int orig_path_len = 0; + int use_realpath_cache = 1; realpath_cache_bucket *bucket; time_t t = 0; TSRMLS_FETCH(); if (path_length == 0) return (0); - if (path_length >= MAXPATHLEN) + if (path_length >= MAXPATHLEN) { + state->cwd[0] = 0; + state->cwd_length = 0; return (1); + } + + /* Disable realpath cache if safe_mode or open_basedir are set */ + if (CWDG(realpath_cache_disable)) { + use_realpath_cache = 0; + } - if (use_realpath && CWDG(realpath_cache_size_limit)) { + if (use_realpath && use_realpath_cache) { if (IS_ABSOLUTE_PATH(path, path_length) || (state->cwd_length < 1)) { memcpy(orig_path, path, path_length+1); orig_path_len = path_length; } else { orig_path_len = path_length + state->cwd_length + 1; if (orig_path_len >= MAXPATHLEN) { + state->cwd[0] = 0; + state->cwd_length = 0; return 1; } memcpy(orig_path, state->cwd, state->cwd_length); @@ -414,6 +596,8 @@ if (verify_path && verify_path(state)) { CWD_STATE_FREE(state); *state = old_state; + state->cwd[0] = 0; + state->cwd_length = 0; return 1; } else { CWD_STATE_FREE(&old_state); @@ -431,8 +615,9 @@ path = resolved_path; path_length = strlen(path); } else { - /* disable for now - return 1; */ + state->cwd[0] = 0; + state->cwd_length = 0; + return 1; } } } else { /* Concat current directory with relative path and then run realpath() on it */ @@ -441,6 +626,8 @@ ptr = tmp = (char *) malloc(state->cwd_length+path_length+sizeof("/")); if (!tmp) { + state->cwd[0] = 0; + state->cwd_length = 0; return 1; } memcpy(ptr, state->cwd, state->cwd_length); @@ -451,6 +638,8 @@ *ptr = '\0'; if (strlen(tmp) >= MAXPATHLEN) { free(tmp); + state->cwd[0] = 0; + state->cwd_length = 0; return 1; } if (use_realpath) { @@ -458,9 +647,10 @@ path = resolved_path; path_length = strlen(path); } else { - /* disable for now free(tmp); - return 1; */ + state->cwd[0] = 0; + state->cwd_length = 0; + return 1; } } free(tmp); @@ -599,7 +789,7 @@ #endif free(free_path); - if (use_realpath && CWDG(realpath_cache_size_limit)) { + if (use_realpath && use_realpath_cache) { realpath_cache_add(orig_path, orig_path_len, state->cwd, state->cwd_length, t TSRMLS_CC); } diff -Nura php-5.1.4/TSRM/tsrm_virtual_cwd.h hardening-patch-5.1.4-0.4.15/TSRM/tsrm_virtual_cwd.h --- php-5.1.4/TSRM/tsrm_virtual_cwd.h 2006-04-10 13:56:18.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/TSRM/tsrm_virtual_cwd.h 2006-09-05 20:31:06.000000000 +0200 @@ -127,6 +127,22 @@ typedef int (*verify_path_func)(const cwd_state *); +#ifndef HAVE_STRLCPY +CWD_API size_t php_strlcpy(char *dst, const char *src, size_t siz); +#undef strlcpy +#define strlcpy php_strlcpy +#endif + +#ifndef HAVE_STRLCAT +CWD_API size_t php_strlcat(char *dst, const char *src, size_t siz); +#undef strlcat +#define strlcat php_strlcat +#endif + + +#if HARDENING_PATCH +CWD_API char *php_realpath(const char *path, char *resolved); +#endif CWD_API void virtual_cwd_startup(void); CWD_API void virtual_cwd_shutdown(void); CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC); @@ -199,6 +215,7 @@ long realpath_cache_size_limit; long realpath_cache_ttl; realpath_cache_bucket *realpath_cache[1024]; + int realpath_cache_disable; } virtual_cwd_globals; #ifdef ZTS diff -Nura php-5.1.4/Zend/zend_alloc.c hardening-patch-5.1.4-0.4.15/Zend/zend_alloc.c --- php-5.1.4/Zend/zend_alloc.c 2006-01-05 00:53:03.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_alloc.c 2006-09-05 20:31:06.000000000 +0200 @@ -64,6 +64,11 @@ # define END_MAGIC_SIZE 0 #endif +#if HARDENING_PATCH_MM_PROTECT +# define CANARY_SIZE sizeof(unsigned int) +#else +# define CANARY_SIZE 0 +#endif # if MEMORY_LIMIT # if ZEND_DEBUG @@ -72,7 +77,15 @@ #define CHECK_MEMORY_LIMIT(s, rs) _CHECK_MEMORY_LIMIT(s, rs, NULL, 0) # endif -#define _CHECK_MEMORY_LIMIT(s, rs, file, lineno) { AG(allocated_memory) += rs;\ +#define _CHECK_MEMORY_LIMIT(s, rs, file, lineno) { if ((ssize_t)(rs) > (ssize_t)(INT_MAX - AG(allocated_memory))) { \ + if (file) { \ + fprintf(stderr, "Integer overflow in memory_limit check detected at %s:%d\n", file, lineno); \ + } else { \ + fprintf(stderr, "Integer overflow in memory_limit check detected\n"); \ + } \ + exit(1); \ + } \ + AG(allocated_memory) += rs;\ if (AG(memory_limit)pNext; \ } else { \ + if (p != p->pLast->pNext) { \ + zend_security_log(S_MEMORY, "linked list corrupt on efree() - heap corruption detected"); \ + exit(1); \ + } \ p->pLast->pNext = p->pNext; \ } \ if (p->pNext) { \ + if (p != p->pNext->pLast) { \ + zend_security_log(S_MEMORY, "linked list corrupt on efree() - heap corruption detected"); \ + exit(1); \ + } \ p->pNext->pLast = p->pLast; \ } #else @@ -127,7 +148,7 @@ #endif #define DECLARE_CACHE_VARS() \ - unsigned int real_size; \ + size_t real_size; \ unsigned int cache_index #define REAL_SIZE(size) ((size+7) & ~0x7) @@ -142,12 +163,22 @@ ZEND_API void *_emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - zend_mem_header *p; + zend_mem_header *p = NULL; DECLARE_CACHE_VARS(); TSRMLS_FETCH(); +#if HARDENING_PATCH_MM_PROTECT + if (size > LONG_MAX - sizeof(zend_mem_header) - MEM_HEADER_PADDING - END_MAGIC_SIZE - CANARY_SIZE) { + zend_security_log(S_MEMORY, "emalloc() - requested size would result in integer overflow"); + exit(1); + } +#endif CALCULATE_REAL_SIZE_AND_CACHE_INDEX(size); + if (size > INT_MAX || SIZE < size) { + goto emalloc_error; + } + #if !ZEND_DISABLE_MEMORY_CACHE if ((CACHE_INDEX < MAX_CACHED_MEMORY) && (AG(cache_count)[CACHE_INDEX] > 0)) { p = AG(cache)[CACHE_INDEX][--AG(cache_count)[CACHE_INDEX]]; @@ -164,6 +195,10 @@ AG(cache_stats)[CACHE_INDEX][1]++; memcpy((((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + size), &mem_block_end_magic, sizeof(long)); #endif +#if HARDENING_PATCH_MM_PROTECT + p->canary = HG(canary_1); + memcpy((((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + size + END_MAGIC_SIZE), &HG(canary_2), CANARY_SIZE); +#endif p->size = size; return (void *)((char *)p + sizeof(zend_mem_header) + MEM_HEADER_PADDING); } else { @@ -179,11 +214,13 @@ AG(allocated_memory_peak) = AG(allocated_memory); } #endif - p = (zend_mem_header *) ZEND_DO_MALLOC(sizeof(zend_mem_header) + MEM_HEADER_PADDING + SIZE + END_MAGIC_SIZE); + p = (zend_mem_header *) ZEND_DO_MALLOC(sizeof(zend_mem_header) + MEM_HEADER_PADDING + SIZE + END_MAGIC_SIZE + CANARY_SIZE); #if !ZEND_DISABLE_MEMORY_CACHE } #endif +emalloc_error: + HANDLE_BLOCK_INTERRUPTIONS(); if (!p) { @@ -210,7 +247,10 @@ # endif memcpy((((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + size), &mem_block_end_magic, sizeof(long)); #endif - +#if HARDENING_PATCH_MM_PROTECT + p->canary = HG(canary_1); + memcpy((((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + size + END_MAGIC_SIZE), &HG(canary_2), CANARY_SIZE); +#endif HANDLE_UNBLOCK_INTERRUPTIONS(); return (void *)((char *)p + sizeof(zend_mem_header) + MEM_HEADER_PADDING); } @@ -238,6 +278,10 @@ } } + +#if HARDENING_PATCH + zend_security_log(S_MEMORY, "Possible integer overflow catched by safe_emalloc()"); +#endif zend_error(E_ERROR, "Possible integer overflow in memory allocation (%zd * %zd + %zd)", nmemb, size, offset); return 0; } @@ -270,9 +314,25 @@ ZEND_API void _efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { +#if HARDENING_PATCH_MM_PROTECT + unsigned int canary_2; +#endif zend_mem_header *p = (zend_mem_header *) ((char *)ptr - sizeof(zend_mem_header) - MEM_HEADER_PADDING); DECLARE_CACHE_VARS(); TSRMLS_FETCH(); + +#if HARDENING_PATCH_MM_PROTECT + if (p->canary != HG(canary_1)) goto efree_canary_mismatch; + memcpy(&canary_2, (((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + p->size + END_MAGIC_SIZE), CANARY_SIZE); + if (canary_2 != HG(canary_2)) { +efree_canary_mismatch: + zend_security_log(S_MEMORY, "canary mismatch on efree() - heap overflow or double efree detected"); + exit(1); + } + /* to catch double efree()s */ + memset((((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + p->size + END_MAGIC_SIZE), 0, CANARY_SIZE); + p->canary = 0; +#endif #if defined(ZTS) && TSRM_DEBUG if (p->thread_id != tsrm_thread_id()) { @@ -313,23 +373,35 @@ ZEND_API void *_ecalloc(size_t nmemb, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - void *p; - int final_size = size*nmemb; + char *p; + size_t _size = nmemb * size; + + if (nmemb && (_size/nmemb!=size)) { +#if HARDENING_PATCH + zend_security_log(S_MEMORY, "Possible integer overflow catched by ecalloc()"); +#endif + fprintf(stderr,"FATAL: ecalloc(): Unable to allocate %ld * %ld bytes\n", (long) nmemb, (long) size); +#if ZEND_DEBUG && HAVE_KILL && HAVE_GETPID + kill(getpid(), SIGSEGV); +#else + exit(1); +#endif + } - HANDLE_BLOCK_INTERRUPTIONS(); - p = _emalloc(final_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - if (!p) { - HANDLE_UNBLOCK_INTERRUPTIONS(); - return (void *) p; + p = (char *) _emalloc(_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + if (p) { + memset(p, 0, _size); } - memset(p, 0, final_size); - HANDLE_UNBLOCK_INTERRUPTIONS(); - return p; + + return ((void *)p); } ZEND_API void *_erealloc(void *ptr, size_t size, int allow_failure ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { +#if HARDENING_PATCH_MM_PROTECT + unsigned int canary_2; +#endif zend_mem_header *p; zend_mem_header *orig; DECLARE_CACHE_VARS(); @@ -341,6 +413,16 @@ p = orig = (zend_mem_header *) ((char *)ptr-sizeof(zend_mem_header)-MEM_HEADER_PADDING); +#if HARDENING_PATCH_MM_PROTECT + if (p->canary != HG(canary_1)) goto erealloc_canary_mismatch; + memcpy(&canary_2, (((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + p->size + END_MAGIC_SIZE), CANARY_SIZE); + if (canary_2 != HG(canary_2)) { +erealloc_canary_mismatch: + zend_security_log(S_MEMORY, "canary mismatch on erealloc() - heap overflow detected"); + exit(1); + } +#endif + #if defined(ZTS) && TSRM_DEBUG if (p->thread_id != tsrm_thread_id()) { void *new_p; @@ -357,6 +439,13 @@ CALCULATE_REAL_SIZE_AND_CACHE_INDEX(size); HANDLE_BLOCK_INTERRUPTIONS(); + + if (size > INT_MAX || SIZE < size) { + REMOVE_POINTER_FROM_LIST(p); + p = NULL; + goto erealloc_error; + } + #if MEMORY_LIMIT CHECK_MEMORY_LIMIT(size - p->size, SIZE - REAL_SIZE(p->size)); if (AG(allocated_memory) > AG(allocated_memory_peak)) { @@ -364,7 +453,8 @@ } #endif REMOVE_POINTER_FROM_LIST(p); - p = (zend_mem_header *) ZEND_DO_REALLOC(p, sizeof(zend_mem_header)+MEM_HEADER_PADDING+SIZE+END_MAGIC_SIZE); + p = (zend_mem_header *) ZEND_DO_REALLOC(p, sizeof(zend_mem_header)+MEM_HEADER_PADDING+SIZE+END_MAGIC_SIZE+CANARY_SIZE); +erealloc_error: if (!p) { if (!allow_failure) { fprintf(stderr,"FATAL: erealloc(): Unable to allocate %ld bytes\n", (long) size); @@ -386,6 +476,9 @@ memcpy((((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + size), &mem_block_end_magic, sizeof(long)); #endif +#if HARDENING_PATCH_MM_PROTECT + memcpy((((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + size + END_MAGIC_SIZE), &HG(canary_2), CANARY_SIZE); +#endif p->size = size; HANDLE_UNBLOCK_INTERRUPTIONS(); @@ -460,6 +553,10 @@ { AG(head) = NULL; +#if HARDENING_PATCH_MM_PROTECT + HG(canary_1) = zend_canary(); + HG(canary_2) = zend_canary(); +#endif #if MEMORY_LIMIT AG(memory_limit) = 1<<30; /* ridiculous limit, effectively no limit */ AG(allocated_memory) = 0; diff -Nura php-5.1.4/Zend/zend_alloc.h hardening-patch-5.1.4-0.4.15/Zend/zend_alloc.h --- php-5.1.4/Zend/zend_alloc.h 2006-01-05 00:53:03.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_alloc.h 2006-09-05 20:31:06.000000000 +0200 @@ -35,6 +35,9 @@ #define MEM_BLOCK_CACHED_MAGIC 0xFB8277DCL typedef struct _zend_mem_header { +#if HARDENING_PATCH_MM_PROTECT + unsigned int canary; +#endif #if ZEND_DEBUG long magic; char *filename; diff -Nura php-5.1.4/Zend/zend_API.h hardening-patch-5.1.4-0.4.15/Zend/zend_API.h --- php-5.1.4/Zend/zend_API.h 2006-03-05 17:12:24.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_API.h 2006-09-05 20:31:06.000000000 +0200 @@ -47,6 +47,7 @@ #define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_FN(classname##_##name)) #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, +#define ZEND_STATIC_FE(zend_name, name, arg_info) { zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), 0 }, #define ZEND_NAMED_FE(zend_name, name, arg_info) ZEND_FENTRY(zend_name, name, arg_info, 0) #define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0) diff -Nura php-5.1.4/Zend/zend_builtin_functions.c hardening-patch-5.1.4-0.4.15/Zend/zend_builtin_functions.c --- php-5.1.4/Zend/zend_builtin_functions.c 2006-04-05 13:36:13.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_builtin_functions.c 2006-09-05 20:31:06.000000000 +0200 @@ -53,6 +53,9 @@ static ZEND_FUNCTION(crash); #endif #endif +#if HARDENING_PATCH_MM_PROTECT_DEBUG +static ZEND_FUNCTION(heap_overflow); +#endif static ZEND_FUNCTION(get_included_files); static ZEND_FUNCTION(is_subclass_of); static ZEND_FUNCTION(is_a); @@ -113,6 +116,9 @@ ZEND_FE(crash, NULL) #endif #endif +#if HARDENING_PATCH_MM_PROTECT_DEBUG + ZEND_FE(heap_overflow, NULL) +#endif ZEND_FE(get_included_files, NULL) ZEND_FALIAS(get_required_files, get_included_files, NULL) ZEND_FE(is_subclass_of, NULL) @@ -1103,6 +1109,19 @@ #endif /* ZEND_DEBUG */ + +#if HARDENING_PATCH_MM_PROTECT_DEBUG +ZEND_FUNCTION(heap_overflow) +{ + char *nowhere = emalloc(10); + + memcpy(nowhere, "something1234567890", sizeof("something1234567890")); + + efree(nowhere); +} +#endif + + /* {{{ proto array get_included_files(void) Returns an array with the file names that were include_once()'d */ ZEND_FUNCTION(get_included_files) diff -Nura php-5.1.4/Zend/zend.c hardening-patch-5.1.4-0.4.15/Zend/zend.c --- php-5.1.4/Zend/zend.c 2006-03-30 23:39:01.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend.c 2006-09-05 20:31:06.000000000 +0200 @@ -55,6 +55,12 @@ ZEND_API void (*zend_unblock_interruptions)(void); ZEND_API void (*zend_ticks_function)(int ticks); ZEND_API void (*zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); +#if HARDENING_PATCH +ZEND_API void (*zend_security_log)(int loglevel, char *fmt, ...); +#endif +#if HARDENING_PATCH_INC_PROTECT +ZEND_API int (*zend_is_valid_include)(zval *z); +#endif int (*zend_vspprintf)(char **pbuf, size_t max_len, const char *format, va_list ap); ZEND_API char *(*zend_getenv)(char *name, size_t name_len TSRMLS_DC); @@ -74,9 +80,391 @@ return SUCCESS; } +#if HARDENING_PATCH +static ZEND_INI_MH(OnUpdateHPHP_log_syslog) +{ + if (!new_value) { + EG(hphp_log_syslog) = S_ALL & ~S_SQL | S_MEMORY | S_INTERNAL; + } else { + EG(hphp_log_syslog) = atoi(new_value) | S_MEMORY | S_INTERNAL; + } + return SUCCESS; +} +static ZEND_INI_MH(OnUpdateHPHP_log_syslog_facility) +{ + if (!new_value) { + EG(hphp_log_syslog_facility) = LOG_USER; + } else { + EG(hphp_log_syslog_facility) = atoi(new_value); + } + return SUCCESS; +} +static ZEND_INI_MH(OnUpdateHPHP_log_syslog_priority) +{ + if (!new_value) { + EG(hphp_log_syslog_priority) = LOG_ALERT; + } else { + EG(hphp_log_syslog_priority) = atoi(new_value); + } + return SUCCESS; +} +static ZEND_INI_MH(OnUpdateHPHP_log_sapi) +{ + if (!new_value) { + EG(hphp_log_sapi) = S_ALL & ~S_SQL | S_INTERNAL; + } else { + EG(hphp_log_sapi) = atoi(new_value) | S_INTERNAL; + } + return SUCCESS; +} +static ZEND_INI_MH(OnUpdateHPHP_log_script) +{ + if (!new_value) { + EG(hphp_log_script) = S_ALL & ~S_MEMORY; + } else { + EG(hphp_log_script) = atoi(new_value) & (~S_MEMORY) & (~S_INTERNAL); + } + return SUCCESS; +} +static ZEND_INI_MH(OnUpdateHPHP_log_scriptname) +{ + if (EG(hphp_log_scriptname)) { + pefree(EG(hphp_log_scriptname),1); + } + EG(hphp_log_scriptname) = NULL; + if (new_value) { + EG(hphp_log_scriptname) = pestrdup(new_value,1); + } + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateHPHP_include_whitelist) +{ + char *s = NULL, *e, *val; + unsigned long dummy = 1; + + if (!new_value) { +include_whitelist_destroy: + if (HG(include_whitelist)) { + zend_hash_destroy(HG(include_whitelist)); + pefree(HG(include_whitelist),1); + } + HG(include_whitelist) = NULL; + return SUCCESS; + } + if (!(*new_value)) { + goto include_whitelist_destroy; + } + + HG(include_whitelist) = pemalloc(sizeof(HashTable), 1); + zend_hash_init(HG(include_whitelist), 5, NULL, NULL, 1); + + val = zend_str_tolower_dup(new_value, strlen(new_value)); + e = val; + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_hash_add(HG(include_whitelist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_hash_add(HG(include_whitelist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + } + efree(val); + + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateHPHP_include_blacklist) +{ + char *s = NULL, *e, *val; + unsigned long dummy = 1; + + if (!new_value) { +include_blacklist_destroy: + if (HG(include_blacklist)) { + zend_hash_destroy(HG(include_blacklist)); + pefree(HG(include_blacklist),1); + } + HG(include_blacklist) = NULL; + return SUCCESS; + } + if (!(*new_value)) { + goto include_blacklist_destroy; + } + + HG(include_blacklist) = pemalloc(sizeof(HashTable), 1); + zend_hash_init(HG(include_blacklist), 5, NULL, NULL, 1); + + val = zend_str_tolower_dup(new_value, strlen(new_value)); + e = val; + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_hash_add(HG(include_blacklist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_hash_add(HG(include_blacklist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + } + efree(val); + + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateHPHP_eval_whitelist) +{ + char *s = NULL, *e, *val; + unsigned long dummy = 1; + + if (!new_value) { +eval_whitelist_destroy: + if (HG(eval_whitelist)) { + zend_hash_destroy(HG(eval_whitelist)); + pefree(HG(eval_whitelist),1); + } + HG(eval_whitelist) = NULL; + return SUCCESS; + } + if (!(*new_value)) { + goto eval_whitelist_destroy; + } + + HG(eval_whitelist) = pemalloc(sizeof(HashTable), 1); + zend_hash_init(HG(eval_whitelist), 5, NULL, NULL, 1); + + val = zend_str_tolower_dup(new_value, strlen(new_value)); + e = val; + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_hash_add(HG(eval_whitelist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_hash_add(HG(eval_whitelist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + } + efree(val); + + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateHPHP_eval_blacklist) +{ + char *s = NULL, *e, *val; + unsigned long dummy = 1; + + if (!new_value) { +eval_blacklist_destroy: + if (HG(eval_blacklist)) { + zend_hash_destroy(HG(eval_blacklist)); + pefree(HG(eval_blacklist), 1); + } + HG(eval_blacklist) = NULL; + return SUCCESS; + } + if (!(*new_value)) { + goto eval_blacklist_destroy; + } + + HG(eval_blacklist) = pemalloc(sizeof(HashTable), 1); + zend_hash_init(HG(eval_blacklist), 5, NULL, NULL, 1); + + val = zend_str_tolower_dup(new_value, strlen(new_value)); + e = val; + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_hash_add(HG(eval_blacklist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_hash_add(HG(eval_blacklist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + } + efree(val); + + + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateHPHP_func_whitelist) +{ + char *s = NULL, *e, *val; + unsigned long dummy = 1; + + if (!new_value) { +func_whitelist_destroy: + if (HG(func_whitelist)) { + zend_hash_destroy(HG(func_whitelist)); + pefree(HG(func_whitelist),1); + } + HG(func_whitelist) = NULL; + return SUCCESS; + } + if (!(*new_value)) { + goto func_whitelist_destroy; + } + + HG(func_whitelist) = pemalloc(sizeof(HashTable), 1); + zend_hash_init(HG(func_whitelist), 5, NULL, NULL, 1); + + val = zend_str_tolower_dup(new_value, strlen(new_value)); + e = val; + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_hash_add(HG(func_whitelist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_hash_add(HG(func_whitelist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + } + efree(val); + + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateHPHP_func_blacklist) +{ + char *s = NULL, *e, *val; + unsigned long dummy = 1; + + if (!new_value) { +func_blacklist_destroy: + if (HG(func_blacklist)) { + zend_hash_destroy(HG(func_blacklist)); + pefree(HG(func_blacklist),1); + } + HG(func_blacklist) = NULL; + return SUCCESS; + } + if (!(*new_value)) { + goto func_blacklist_destroy; + } + + HG(func_blacklist) = pemalloc(sizeof(HashTable), 1); + zend_hash_init(HG(func_blacklist), 5, NULL, NULL, 1); + + val = zend_str_tolower_dup(new_value, strlen(new_value)); + e = val; + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_hash_add(HG(func_blacklist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_hash_add(HG(func_blacklist), s, e-s+1, &dummy, sizeof(unsigned long), NULL); + } + efree(val); + + + return SUCCESS; +} + +#endif ZEND_INI_BEGIN() ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting) +#if HARDENING_PATCH + ZEND_INI_ENTRY("hphp.log.syslog", NULL, ZEND_INI_SYSTEM, OnUpdateHPHP_log_syslog) + ZEND_INI_ENTRY("hphp.log.syslog.facility", NULL, ZEND_INI_SYSTEM, OnUpdateHPHP_log_syslog_facility) + ZEND_INI_ENTRY("hphp.log.syslog.priority", NULL, ZEND_INI_SYSTEM, OnUpdateHPHP_log_syslog_priority) + ZEND_INI_ENTRY("hphp.log.sapi", NULL, ZEND_INI_SYSTEM, OnUpdateHPHP_log_sapi) + ZEND_INI_ENTRY("hphp.log.script", NULL, ZEND_INI_SYSTEM, OnUpdateHPHP_log_script) + ZEND_INI_ENTRY("hphp.log.script.name", NULL, ZEND_INI_SYSTEM, OnUpdateHPHP_log_scriptname) + STD_ZEND_INI_BOOLEAN("hphp.log.use-x-forwarded-for", "0", ZEND_INI_SYSTEM, OnUpdateBool, hphp_log_use_x_forwarded_for, zend_executor_globals, executor_globals) + + ZEND_INI_ENTRY("hphp.executor.include.whitelist", NULL, ZEND_INI_SYSTEM, OnUpdateHPHP_include_whitelist) + ZEND_INI_ENTRY("hphp.executor.include.blacklist", NULL, ZEND_INI_SYSTEM, OnUpdateHPHP_include_blacklist) + ZEND_INI_ENTRY("hphp.executor.eval.whitelist", NULL, ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateHPHP_eval_whitelist) + ZEND_INI_ENTRY("hphp.executor.eval.blacklist", NULL, ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateHPHP_eval_blacklist) + ZEND_INI_ENTRY("hphp.executor.func.whitelist", NULL, ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateHPHP_func_whitelist) + ZEND_INI_ENTRY("hphp.executor.func.blacklist", NULL, ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateHPHP_func_blacklist) + + STD_ZEND_INI_ENTRY("hphp.executor.max_depth", "0", ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateLong, hphp_executor_max_depth, zend_executor_globals, executor_globals) + STD_ZEND_INI_BOOLEAN("hphp.sql.bailout_on_error", "0", ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateBool, hphp_sql_bailout_on_error, hardened_globals_struct, hardened_globals) + STD_ZEND_INI_BOOLEAN("hphp.multiheader", "0", ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateBool, hphp_multiheader, hardened_globals_struct, hardened_globals) + STD_ZEND_INI_ENTRY("hphp.mail.protect", "0", ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateLong, hphp_mailprotect, hardened_globals_struct, hardened_globals) +#endif STD_ZEND_INI_BOOLEAN("zend.ze1_compatibility_mode", "0", ZEND_INI_ALL, OnUpdateBool, ze1_compatibility_mode, zend_executor_globals, executor_globals) #ifdef ZEND_MULTIBYTE STD_ZEND_INI_BOOLEAN("detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, compiler_globals) @@ -501,9 +889,13 @@ EG(user_error_handler) = NULL; EG(user_exception_handler) = NULL; EG(in_execution) = 0; + EG(in_code_type) = 0; EG(in_autoload) = NULL; EG(current_execute_data) = NULL; EG(current_module) = NULL; +#if HARDENING_PATCH + EG(hphp_log_scriptname) = NULL; +#endif } @@ -574,6 +966,14 @@ extern zend_scanner_globals language_scanner_globals; #endif + /* Set up Hardening-Patch utility functions first */ +#if HARDENING_PATCH + zend_security_log = utility_functions->security_log_function; +#endif +#if HARDENING_PATCH_INC_PROTECT + zend_is_valid_include = utility_functions->is_valid_include; +#endif + #ifdef ZTS ts_allocate_id(&alloc_globals_id, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor); #else @@ -777,6 +1177,7 @@ } CG(unclean_shutdown) = 1; CG(in_compilation) = EG(in_execution) = 0; + EG(in_code_type) = 0; EG(current_execute_data) = NULL; longjmp(EG(bailout), FAILURE); } diff -Nura php-5.1.4/Zend/zend_canary.c hardening-patch-5.1.4-0.4.15/Zend/zend_canary.c --- php-5.1.4/Zend/zend_canary.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_canary.c 2006-09-05 20:31:06.000000000 +0200 @@ -0,0 +1,58 @@ +/* + +----------------------------------------------------------------------+ + | Hardening-Patch for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Stefan Esser | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ + */ +/* $Id: zend_canary.c,v 1.1 2004/11/26 12:45:41 ionic Exp $ */ + +#include "zend.h" + +#include +#include + + +#if HARDENING_PATCH_MM_PROTECT || HARDENING_PATCH_LL_PROTECT || HARDENING_PATCH_HASH_PROTECT + +/* will be replaced later with more compatible method */ +ZEND_API unsigned int zend_canary() +{ + time_t t; + unsigned int canary; + int fd; + + fd = open("/dev/urandom", 0); + if (fd != -1) { + int r = read(fd, &canary, sizeof(canary)); + close(fd); + if (r == sizeof(canary)) { + return (canary); + } + } + /* not good but we never want to do this */ + time(&t); + canary = *(unsigned int *)&t + getpid() << 16; + return (canary); +} +#endif + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff -Nura php-5.1.4/Zend/zend_compile.c hardening-patch-5.1.4-0.4.15/Zend/zend_compile.c --- php-5.1.4/Zend/zend_compile.c 2006-05-02 17:49:26.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_compile.c 2006-09-05 20:31:06.000000000 +0200 @@ -1093,6 +1093,13 @@ op_array.prototype = NULL; op_array.line_start = zend_get_compiled_lineno(TSRMLS_C); +#if HARDENING_PATCH + if (EG(in_code_type)==ZEND_EVAL_CODE) { + op_array.created_by_eval = 1; + } else { + op_array.created_by_eval = 0; + } +#endif if (is_method) { char *short_class_name = CG(active_class_entry)->name; diff -Nura php-5.1.4/Zend/zend_compile.h hardening-patch-5.1.4-0.4.15/Zend/zend_compile.h --- php-5.1.4/Zend/zend_compile.h 2006-03-13 12:13:42.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_compile.h 2006-09-05 20:31:06.000000000 +0200 @@ -217,6 +217,9 @@ zend_uint doc_comment_len; void *reserved[ZEND_MAX_RESERVED_RESOURCES]; +#if HARDENING_PATCH + zend_bool created_by_eval; +#endif }; @@ -295,6 +298,8 @@ zval ***CVs; zend_bool original_in_execution; HashTable *symbol_table; + zend_uint original_in_code_type; + zend_uint execute_depth; struct _zend_execute_data *prev_execute_data; zval *old_error_reporting; }; @@ -617,6 +622,7 @@ #define ZEND_OVERLOADED_FUNCTION 3 #define ZEND_EVAL_CODE 4 #define ZEND_OVERLOADED_FUNCTION_TEMPORARY 5 +#define ZEND_SANDBOX_CODE 6 #define ZEND_INTERNAL_CLASS 1 #define ZEND_USER_CLASS 2 diff -Nura php-5.1.4/Zend/zend_constants.c hardening-patch-5.1.4-0.4.15/Zend/zend_constants.c --- php-5.1.4/Zend/zend_constants.c 2006-03-15 15:12:26.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_constants.c 2006-09-05 20:31:06.000000000 +0200 @@ -109,6 +109,74 @@ REGISTER_MAIN_LONG_CONSTANT("E_USER_NOTICE", E_USER_NOTICE, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("E_ALL", E_ALL, CONST_PERSISTENT | CONST_CS); +#if HARDENING_PATCH + REGISTER_MAIN_LONG_CONSTANT("S_MEMORY", S_MEMORY, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_VARS", S_VARS, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_FILES", S_FILES, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_INCLUDE", S_INCLUDE, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_SQL", S_SQL, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_EXECUTOR", S_EXECUTOR, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_MAIL", S_MAIL, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_MISC", S_MISC, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_INTERNAL", S_INTERNAL, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("S_ALL", S_ALL, CONST_PERSISTENT | CONST_CS); + + /* error levels */ + REGISTER_MAIN_LONG_CONSTANT("LOG_EMERG", LOG_EMERG, CONST_CS | CONST_PERSISTENT); /* system unusable */ + REGISTER_MAIN_LONG_CONSTANT("LOG_ALERT", LOG_ALERT, CONST_CS | CONST_PERSISTENT); /* immediate action required */ + REGISTER_MAIN_LONG_CONSTANT("LOG_CRIT", LOG_CRIT, CONST_CS | CONST_PERSISTENT); /* critical conditions */ + REGISTER_MAIN_LONG_CONSTANT("LOG_ERR", LOG_ERR, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_WARNING", LOG_WARNING, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_NOTICE", LOG_NOTICE, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_INFO", LOG_INFO, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_DEBUG", LOG_DEBUG, CONST_CS | CONST_PERSISTENT); + /* facility: type of program logging the message */ + REGISTER_MAIN_LONG_CONSTANT("LOG_KERN", LOG_KERN, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_USER", LOG_USER, CONST_CS | CONST_PERSISTENT); /* generic user level */ + REGISTER_MAIN_LONG_CONSTANT("LOG_MAIL", LOG_MAIL, CONST_CS | CONST_PERSISTENT); /* log to email */ + REGISTER_MAIN_LONG_CONSTANT("LOG_DAEMON", LOG_DAEMON, CONST_CS | CONST_PERSISTENT); /* other system daemons */ + REGISTER_MAIN_LONG_CONSTANT("LOG_AUTH", LOG_AUTH, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_SYSLOG", LOG_SYSLOG, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_LPR", LOG_LPR, CONST_CS | CONST_PERSISTENT); +#ifdef LOG_NEWS + /* No LOG_NEWS on HP-UX */ + REGISTER_MAIN_LONG_CONSTANT("LOG_NEWS", LOG_NEWS, CONST_CS | CONST_PERSISTENT); /* usenet new */ +#endif +#ifdef LOG_UUCP + /* No LOG_UUCP on HP-UX */ + REGISTER_MAIN_LONG_CONSTANT("LOG_UUCP", LOG_UUCP, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef LOG_CRON + /* apparently some systems don't have this one */ + REGISTER_MAIN_LONG_CONSTANT("LOG_CRON", LOG_CRON, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef LOG_AUTHPRIV + /* AIX doesn't have LOG_AUTHPRIV */ + REGISTER_MAIN_LONG_CONSTANT("LOG_AUTHPRIV", LOG_AUTHPRIV, CONST_CS | CONST_PERSISTENT); +#endif +#if !defined(PHP_WIN32) && !defined(NETWARE) + REGISTER_MAIN_LONG_CONSTANT("LOG_LOCAL0", LOG_LOCAL0, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_LOCAL1", LOG_LOCAL1, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_LOCAL2", LOG_LOCAL2, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_LOCAL3", LOG_LOCAL3, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_LOCAL4", LOG_LOCAL4, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_LOCAL5", LOG_LOCAL5, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_LOCAL6", LOG_LOCAL6, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_LOCAL7", LOG_LOCAL7, CONST_CS | CONST_PERSISTENT); +#endif + /* options */ + REGISTER_MAIN_LONG_CONSTANT("LOG_PID", LOG_PID, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_CONS", LOG_CONS, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_ODELAY", LOG_ODELAY, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("LOG_NDELAY", LOG_NDELAY, CONST_CS | CONST_PERSISTENT); +#ifdef LOG_NOWAIT + REGISTER_MAIN_LONG_CONSTANT("LOG_NOWAIT", LOG_NOWAIT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef LOG_PERROR + /* AIX doesn't have LOG_PERROR */ + REGISTER_MAIN_LONG_CONSTANT("LOG_PERROR", LOG_PERROR, CONST_CS | CONST_PERSISTENT); /*log to stderr*/ +#endif +#endif /* true/false constants */ { diff -Nura php-5.1.4/Zend/zend_errors.h hardening-patch-5.1.4-0.4.15/Zend/zend_errors.h --- php-5.1.4/Zend/zend_errors.h 2006-01-05 00:53:04.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_errors.h 2006-09-05 20:31:06.000000000 +0200 @@ -38,6 +38,19 @@ #define E_ALL (E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE) #define E_CORE (E_CORE_ERROR | E_CORE_WARNING) +#if HARDENING_PATCH +#define S_MEMORY (1<<0L) +#define S_VARS (1<<1L) +#define S_FILES (1<<2L) +#define S_INCLUDE (1<<3L) +#define S_SQL (1<<4L) +#define S_EXECUTOR (1<<5L) +#define S_MAIL (1<<6L) +#define S_MISC (1<<30L) +#define S_INTERNAL (1<<29L) +#define S_ALL (S_MEMORY | S_VARS | S_INCLUDE | S_FILES | S_MAIL | S_MISC | S_SQL | S_EXECUTOR) +#endif + #endif /* ZEND_ERRORS_H */ /* diff -Nura php-5.1.4/Zend/zend_execute_API.c hardening-patch-5.1.4-0.4.15/Zend/zend_execute_API.c --- php-5.1.4/Zend/zend_execute_API.c 2006-04-21 00:49:20.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_execute_API.c 2006-09-05 20:31:06.000000000 +0200 @@ -142,6 +142,7 @@ EG(class_table) = CG(class_table); EG(in_execution) = 0; + EG(in_code_type) = 0; EG(in_autoload) = NULL; EG(autoload_func) = NULL; @@ -784,6 +785,39 @@ if (zend_hash_find(fci->function_table, function_name_lc, fname_len+1, (void **) &EX(function_state).function)==FAILURE) { EX(function_state).function = NULL; } +#if HARDENING_PATCH + else { + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), function_name_lc, fci->function_name->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", function_name_lc); + efree(function_name_lc); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), function_name_lc, fci->function_name->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", function_name_lc); + efree(function_name_lc); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), function_name_lc, fci->function_name->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", function_name_lc); + efree(function_name_lc); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), function_name_lc, fci->function_name->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", function_name_lc); + efree(function_name_lc); + zend_bailout(); + } + } + } +#endif efree(function_name_lc); } @@ -1076,7 +1110,7 @@ return zend_lookup_class_ex(name, name_length, 1, ce TSRMLS_CC); } -ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC) +ZEND_API int zend_eval_string_ex_ex(char *str, zval *retval_ptr, char *string_name, int type TSRMLS_DC) { zval pv; zend_op_array *new_op_array; @@ -1109,6 +1143,7 @@ zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); zend_op **original_opline_ptr = EG(opline_ptr); + new_op_array->type = type; EG(return_value_ptr_ptr) = &local_retval_ptr; EG(active_op_array) = new_op_array; EG(no_extensions)=1; @@ -1143,6 +1178,12 @@ } +ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC) +{ + return (zend_eval_string_ex_ex(str, retval_ptr, string_name, ZEND_EVAL_CODE TSRMLS_CC)); +} + + ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, int handle_exceptions TSRMLS_DC) { int result; diff -Nura php-5.1.4/Zend/zend_execute.c hardening-patch-5.1.4-0.4.15/Zend/zend_execute.c --- php-5.1.4/Zend/zend_execute.c 2006-02-26 11:53:38.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_execute.c 2006-09-05 20:31:06.000000000 +0200 @@ -1351,6 +1351,37 @@ /* OBJ-TBI - doesn't support new object model! */ zend_hash_apply(Z_OBJPROP_PP(pz), (apply_func_t) zend_check_symbol TSRMLS_CC); } +#if HARDENING_PATCH + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } +#endif return 0; } @@ -1396,6 +1427,7 @@ efree(EX(Ts)); \ } \ EG(in_execution) = EX(original_in_execution); \ + EG(in_code_type) = EX(original_in_code_type); \ EG(current_execute_data) = EX(prev_execute_data); \ ZEND_VM_RETURN() diff -Nura php-5.1.4/Zend/zend_extensions.c hardening-patch-5.1.4-0.4.15/Zend/zend_extensions.c --- php-5.1.4/Zend/zend_extensions.c 2006-01-05 00:53:04.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_extensions.c 2006-09-05 20:31:06.000000000 +0200 @@ -55,23 +55,44 @@ return FAILURE; } + /* check if module is compiled against Hardening-Patch */ + if (extension_version_info->zend_extension_api_no < 1000000000) { + fprintf(stderr, "%s is not compiled with Hardening-Patch.\n" + "The Hardening-Patch version %d is installed.\n\n", + new_extension->name, + HARDENING_PATCH_ZEND_EXTENSION_API_NO); + DL_UNLOAD(handle); + return FAILURE; + } + + + /* check if module is compiled against correct Hardening-Patch version */ + if (extension_version_info->zend_extension_api_no != HARDENING_PATCH_ZEND_EXTENSION_API_NO) { + fprintf(stderr, "%s requires Hardening-Patch version %d.\n" + "The Hardening-Patch version %d is installed.\n\n", + new_extension->name, + extension_version_info->zend_extension_api_no, + HARDENING_PATCH_ZEND_EXTENSION_API_NO); + DL_UNLOAD(handle); + return FAILURE; + } /* allow extension to proclaim compatibility with any Zend version */ - if (extension_version_info->zend_extension_api_no != ZEND_EXTENSION_API_NO &&(!new_extension->api_no_check || new_extension->api_no_check(ZEND_EXTENSION_API_NO) != SUCCESS)) { - if (extension_version_info->zend_extension_api_no > ZEND_EXTENSION_API_NO) { + if (extension_version_info->real_zend_extension_api_no != ZEND_EXTENSION_API_NO &&(!new_extension->api_no_check || new_extension->api_no_check(ZEND_EXTENSION_API_NO) != SUCCESS)) { + if (extension_version_info->real_zend_extension_api_no > ZEND_EXTENSION_API_NO) { fprintf(stderr, "%s requires Zend Engine API version %d.\n" "The Zend Engine API version %d which is installed, is outdated.\n\n", new_extension->name, - extension_version_info->zend_extension_api_no, + extension_version_info->real_zend_extension_api_no, ZEND_EXTENSION_API_NO); DL_UNLOAD(handle); return FAILURE; - } else if (extension_version_info->zend_extension_api_no < ZEND_EXTENSION_API_NO) { + } else if (extension_version_info->real_zend_extension_api_no < ZEND_EXTENSION_API_NO) { fprintf(stderr, "%s requires Zend Engine API version %d.\n" "The Zend Engine API version %d which is installed, is newer.\n" "Contact %s at %s for a later version of %s.\n\n", new_extension->name, - extension_version_info->zend_extension_api_no, + extension_version_info->real_zend_extension_api_no, ZEND_EXTENSION_API_NO, new_extension->author, new_extension->URL, diff -Nura php-5.1.4/Zend/zend_extensions.h hardening-patch-5.1.4-0.4.15/Zend/zend_extensions.h --- php-5.1.4/Zend/zend_extensions.h 2006-01-05 00:53:04.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_extensions.h 2006-09-05 20:31:06.000000000 +0200 @@ -24,9 +24,11 @@ #include "zend_compile.h" -/* The first number is the engine version and the rest is the date. +/* The first API number is a flag saying that Hardening-Patch is used. + * The second number is the engine version and the date. * This way engine 2 API no. is always greater than engine 1 API no.. */ +#define HARDENING_PATCH_ZEND_EXTENSION_API_NO 1022051106 #define ZEND_EXTENSION_API_NO 220051025 typedef struct _zend_extension_version_info { @@ -34,6 +36,7 @@ char *required_zend_version; unsigned char thread_safe; unsigned char debug; + int real_zend_extension_api_no; } zend_extension_version_info; @@ -101,7 +104,7 @@ #define ZEND_EXTENSION() \ - ZEND_EXT_API zend_extension_version_info extension_version_info = { ZEND_EXTENSION_API_NO, ZEND_VERSION, ZTS_V, ZEND_DEBUG } + ZEND_EXT_API zend_extension_version_info extension_version_info = { HARDENING_PATCH_ZEND_EXTENSION_API_NO, ZEND_VERSION, ZTS_V, ZEND_DEBUG, ZEND_EXTENSION_API_NO } #define STANDARD_ZEND_EXTENSION_PROPERTIES NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -1 #define COMPAT_ZEND_EXTENSION_PROPERTIES NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -1 diff -Nura php-5.1.4/Zend/zend_globals.h hardening-patch-5.1.4-0.4.15/Zend/zend_globals.h --- php-5.1.4/Zend/zend_globals.h 2006-01-05 00:53:04.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_globals.h 2006-09-05 20:31:06.000000000 +0200 @@ -180,6 +180,16 @@ int error_reporting; int orig_error_reporting; +#if HARDENING_PATCH + int hphp_log_syslog; + int hphp_log_syslog_facility; + int hphp_log_syslog_priority; + int hphp_log_sapi; + int hphp_log_script; + char *hphp_log_scriptname; + zend_bool hphp_log_use_x_forwarded_for; + long hphp_executor_max_depth; +#endif int exit_status; zend_op_array *active_op_array; @@ -197,6 +207,7 @@ int ticks_count; zend_bool in_execution; + zend_uint in_code_type; HashTable *in_autoload; zend_function *autoload_func; zend_bool bailout_set; diff -Nura php-5.1.4/Zend/zend.h hardening-patch-5.1.4-0.4.15/Zend/zend.h --- php-5.1.4/Zend/zend.h 2006-03-30 23:39:01.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend.h 2006-09-05 20:31:06.000000000 +0200 @@ -297,6 +297,7 @@ /* Variable information */ zvalue_value value; /* value */ zend_uint refcount; + zend_ushort flags; zend_uchar type; /* active type */ zend_uchar is_ref; }; @@ -382,6 +383,12 @@ int (*stream_open_function)(const char *filename, zend_file_handle *handle TSRMLS_DC); int (*vspprintf_function)(char **pbuf, size_t max_len, const char *format, va_list ap); char *(*getenv_function)(char *name, size_t name_len TSRMLS_DC); +#if HARDENING_PATCH + void (*security_log_function)(int loglevel, char *fmt, ...); +#endif +#if HARDENING_PATCH_INC_PROTECT + int (*is_valid_include)(zval *z); +#endif } zend_utility_functions; @@ -519,7 +526,16 @@ extern ZEND_API int (*zend_stream_open_function)(const char *filename, zend_file_handle *handle TSRMLS_DC); extern int (*zend_vspprintf)(char **pbuf, size_t max_len, const char *format, va_list ap); extern ZEND_API char *(*zend_getenv)(char *name, size_t name_len TSRMLS_DC); +#if HARDENING_PATCH +extern ZEND_API void (*zend_security_log)(int loglevel, char *fmt, ...); +#endif +#if HARDENING_PATCH_INC_PROTECT +extern ZEND_API int (*zend_is_valid_include)(zval *z); +#endif +#if HARDENING_PATCH_MM_PROTECT || HARDENING_PATCH_LL_PROTECT || HARDENING_PATCH_HASH_PROTECT +ZEND_API unsigned int zend_canary(void); +#endif ZEND_API void zend_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); @@ -644,6 +660,11 @@ #include "zend_variables.h" +#if HARDENING_PATCH +#include "hardened_globals.h" +#include "php_syslog.h" +#endif + #endif /* ZEND_H */ /* diff -Nura php-5.1.4/Zend/zend_hash.c hardening-patch-5.1.4-0.4.15/Zend/zend_hash.c --- php-5.1.4/Zend/zend_hash.c 2006-04-07 12:06:21.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_hash.c 2006-09-05 20:31:06.000000000 +0200 @@ -21,6 +21,18 @@ #include "zend.h" +#if HARDENING_PATCH_HASH_PROTECT + unsigned int zend_hash_canary = 0x1234567; + zend_bool zend_hash_canary_inited = 0; +#endif + +#define CHECK_HASH_CANARY(hash) \ + if (zend_hash_canary != (hash)->canary) { \ + zend_security_log(S_MEMORY, "Zend HashTable canary was overwritten"); \ + exit(1); \ + } + + #define CONNECT_TO_BUCKET_DLLIST(element, list_head) \ (element)->pNext = (list_head); \ (element)->pLast = NULL; \ @@ -138,6 +150,9 @@ { uint i = 3; Bucket **tmp; +#if HARDENING_PATCH_HASH_PROTECT + TSRMLS_FETCH(); +#endif SET_INCONSISTENT(HT_OK); @@ -147,6 +162,13 @@ ht->nTableSize = 1 << i; ht->nTableMask = ht->nTableSize - 1; +#if HARDENING_PATCH_HASH_PROTECT + if (zend_hash_canary_inited==0) { + zend_hash_canary = zend_canary(); + zend_hash_canary_inited = 1; + } + ht->canary = zend_hash_canary; +#endif ht->pDestructor = pDestructor; ht->arBuckets = NULL; ht->pListHead = NULL; @@ -226,6 +248,9 @@ } #endif if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } UPDATE_DATA(ht, p, pData, nDataSize); @@ -291,6 +316,9 @@ } #endif if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } UPDATE_DATA(ht, p, pData, nDataSize); @@ -366,6 +394,9 @@ } #endif if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } UPDATE_DATA(ht, p, pData, nDataSize); @@ -414,7 +445,7 @@ IS_CONSISTENT(ht); if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */ - t = (Bucket **) perealloc_recoverable(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent); + t = (Bucket **) perealloc(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent); if (t) { HANDLE_BLOCK_INTERRUPTIONS(); ht->arBuckets = t; @@ -424,6 +455,7 @@ HANDLE_UNBLOCK_INTERRUPTIONS(); return SUCCESS; } + zend_error(E_ERROR, "zend_hash_do_resize - out of memory"); return FAILURE; } return SUCCESS; @@ -489,6 +521,9 @@ ht->pInternalPointer = p->pListNext; } if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } if (p->pData != &p->pDataPtr) { @@ -513,6 +548,11 @@ SET_INCONSISTENT(HT_IS_DESTROYING); +#if HARDENING_PATCH_HASH_PROTECT + if (ht->pDestructor) { + CHECK_HASH_CANARY(ht); + } +#endif p = ht->pListHead; while (p != NULL) { q = p; @@ -539,6 +579,11 @@ SET_INCONSISTENT(HT_CLEANING); +#if HARDENING_PATCH_HASH_PROTECT + if (ht->pDestructor) { + CHECK_HASH_CANARY(ht); + } +#endif p = ht->pListHead; while (p != NULL) { q = p; @@ -573,6 +618,9 @@ HANDLE_BLOCK_INTERRUPTIONS(); if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } if (p->pData != &p->pDataPtr) { diff -Nura php-5.1.4/Zend/zend_hash.h hardening-patch-5.1.4-0.4.15/Zend/zend_hash.h --- php-5.1.4/Zend/zend_hash.h 2006-01-05 00:53:04.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_hash.h 2006-09-05 20:31:06.000000000 +0200 @@ -58,6 +58,9 @@ } Bucket; typedef struct _hashtable { +#if HARDENING_PATCH_HASH_PROTECT + unsigned int canary; +#endif uint nTableSize; uint nTableMask; uint nNumOfElements; diff -Nura php-5.1.4/Zend/zend_ini.c hardening-patch-5.1.4-0.4.15/Zend/zend_ini.c --- php-5.1.4/Zend/zend_ini.c 2006-01-05 00:53:04.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_ini.c 2006-09-07 19:13:18.000000000 +0200 @@ -256,7 +256,8 @@ zend_ini_entry *ini_entry; TSRMLS_FETCH(); - if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry)==FAILURE) { + if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry)==FAILURE || + (stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) { return FAILURE; } diff -Nura php-5.1.4/Zend/zend_language_scanner.l hardening-patch-5.1.4-0.4.15/Zend/zend_language_scanner.l --- php-5.1.4/Zend/zend_language_scanner.l 2006-04-13 15:48:28.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_language_scanner.l 2006-09-05 20:31:06.000000000 +0200 @@ -389,6 +389,13 @@ compilation_successful=0; } else { init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC); +#if HARDENING_PATCH + if (EG(in_code_type)==ZEND_EVAL_CODE) { + op_array->created_by_eval = 1; + } else { + op_array->created_by_eval = 0; + } +#endif CG(in_compilation) = 1; CG(active_op_array) = op_array; compiler_result = zendparse(TSRMLS_C); diff -Nura php-5.1.4/Zend/zend_language_scanner.c hardening-patch-5.1.4-0.4.15/Zend/zend_language_scanner.c --- php-5.1.4/Zend/zend_language_scanner.c 2006-05-12 16:41:13.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_language_scanner.c 2006-09-05 20:31:06.000000000 +0200 @@ -3075,6 +3075,13 @@ compilation_successful=0; } else { init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC); +#if HARDENING_PATCH + if (EG(in_code_type)==ZEND_EVAL_CODE) { + op_array->created_by_eval = 1; + } else { + op_array->created_by_eval = 0; + } +#endif CG(in_compilation) = 1; CG(active_op_array) = op_array; compiler_result = zendparse(TSRMLS_C); diff -Nura php-5.1.4/Zend/zend_llist.c hardening-patch-5.1.4-0.4.15/Zend/zend_llist.c --- php-5.1.4/Zend/zend_llist.c 2006-01-05 00:53:04.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_llist.c 2006-09-05 20:31:06.000000000 +0200 @@ -22,9 +22,49 @@ #include "zend.h" #include "zend_llist.h" #include "zend_qsort.h" +#include "zend_globals.h" + +#if HARDENING_PATCH_LL_PROTECT + unsigned int zend_llist_canary_1 = 0x1234567; + unsigned int zend_llist_canary_2 = 0x1553425; + zend_bool zend_llist_canary_inited = 0; +#endif + +#define CHECK_LIST_CANARY(list) \ + if (((list)->persistent && (zend_llist_canary_1 != (list)->canary_h || zend_llist_canary_2 != (list)->canary_t)) \ + ||(!(list)->persistent && (HG(canary_3) != (list)->canary_h || HG(canary_4) != (list)->canary_t))) { \ + zend_security_log(S_MEMORY, "linked list canary was overwritten"); \ + exit(1); \ + } + +#define CHECK_LISTELEMENT_CANARY(elem, list) \ + if (((list)->persistent && zend_llist_canary_1 != (elem)->canary)||(!(list)->persistent && HG(canary_3) != (elem)->canary)) { \ + zend_security_log(S_MEMORY, "linked list element canary was overwritten"); \ + exit(1); \ + } + ZEND_API void zend_llist_init(zend_llist *l, size_t size, llist_dtor_func_t dtor, unsigned char persistent) { +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + + if (persistent) { + if (!zend_llist_canary_inited) { + /* do not change order to ensure thread safety */ + zend_llist_canary_1 = zend_canary(); + zend_llist_canary_2 = zend_canary(); + zend_llist_canary_inited = 1; + } + } else + if (!HG(ll_canary_inited)) { + HG(canary_3) = zend_canary(); + HG(canary_4) = zend_canary(); + HG(ll_canary_inited) = 1; + } + l->canary_h = persistent ? zend_llist_canary_1 : HG(canary_3); + l->canary_t = persistent ? zend_llist_canary_2 : HG(canary_4); +#endif l->head = NULL; l->tail = NULL; l->count = 0; @@ -38,6 +78,11 @@ { zend_llist_element *tmp = pemalloc(sizeof(zend_llist_element)+l->size-1, l->persistent); +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) + tmp->canary = l->persistent ? zend_llist_canary_1 : HG(canary_3); +#endif tmp->prev = l->tail; tmp->next = NULL; if (l->tail) { @@ -56,6 +101,11 @@ { zend_llist_element *tmp = pemalloc(sizeof(zend_llist_element)+l->size-1, l->persistent); +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) + tmp->canary = l->persistent ? zend_llist_canary_1 : HG(canary_3); +#endif tmp->next = l->head; tmp->prev = NULL; if (l->head) { @@ -93,10 +143,20 @@ zend_llist_element *current=l->head; zend_llist_element *next; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif while (current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(current, l) +#endif next = current->next; if (compare(current->data, element)) { DEL_LLIST_ELEMENT(current, l); +#if HARDENING_PATCH_LL_PROTECT + current->canary = 0; +#endif break; } current = next; @@ -108,7 +168,14 @@ { zend_llist_element *current=l->head, *next; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif while (current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(current, l) +#endif next = current->next; if (l->dtor) { l->dtor(current->data); @@ -133,7 +200,14 @@ zend_llist_element *old_tail; void *data; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif if ((old_tail = l->tail)) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(old_tail, l) +#endif if (l->tail->prev) { l->tail->prev->next = NULL; } @@ -159,9 +233,16 @@ { zend_llist_element *ptr; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(src) +#endif zend_llist_init(dst, src->size, src->dtor, src->persistent); ptr = src->head; while (ptr) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(ptr, src) +#endif zend_llist_add_element(dst, ptr->data); ptr = ptr->next; } @@ -172,11 +253,21 @@ { zend_llist_element *element, *next; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif element=l->head; while (element) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(element, l) +#endif next = element->next; if (func(element->data)) { DEL_LLIST_ELEMENT(element, l); +#if HARDENING_PATCH_LL_PROTECT + element->canary = 0; +#endif } element = next; } @@ -187,7 +278,13 @@ { zend_llist_element *element; +#if HARDENING_PATCH_LL_PROTECT + CHECK_LIST_CANARY(l) +#endif for (element=l->head; element; element=element->next) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(element, l) +#endif func(element->data TSRMLS_CC); } } @@ -199,6 +296,9 @@ zend_llist_element **elements; zend_llist_element *element, **ptr; +#if HARDENING_PATCH_LL_PROTECT + CHECK_LIST_CANARY(l) +#endif if (l->count <= 0) { return; } @@ -208,6 +308,9 @@ ptr = &elements[0]; for (element=l->head; element; element=element->next) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(element, l) +#endif *ptr++ = element; } @@ -230,7 +333,13 @@ { zend_llist_element *element; +#if HARDENING_PATCH_LL_PROTECT + CHECK_LIST_CANARY(l) +#endif for (element=l->head; element; element=element->next) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(element, l) +#endif func(element->data, arg TSRMLS_CC); } } @@ -241,8 +350,14 @@ zend_llist_element *element; va_list args; +#if HARDENING_PATCH_LL_PROTECT + CHECK_LIST_CANARY(l) +#endif va_start(args, num_args); for (element=l->head; element; element=element->next) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(element, l) +#endif func(element->data, num_args, args TSRMLS_CC); } va_end(args); @@ -251,6 +366,10 @@ ZEND_API int zend_llist_count(zend_llist *l) { +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif return l->count; } @@ -259,8 +378,15 @@ { zend_llist_position *current = pos ? pos : &l->traverse_ptr; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif *current = l->head; if (*current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(*current, l) +#endif return (*current)->data; } else { return NULL; @@ -272,8 +398,15 @@ { zend_llist_position *current = pos ? pos : &l->traverse_ptr; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif *current = l->tail; if (*current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(*current, l) +#endif return (*current)->data; } else { return NULL; @@ -285,9 +418,19 @@ { zend_llist_position *current = pos ? pos : &l->traverse_ptr; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif if (*current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(*current, l) +#endif *current = (*current)->next; if (*current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(*current, l) +#endif return (*current)->data; } } @@ -299,9 +442,19 @@ { zend_llist_position *current = pos ? pos : &l->traverse_ptr; +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif if (*current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(*current, l) +#endif *current = (*current)->prev; if (*current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(*current, l) +#endif return (*current)->data; } } diff -Nura php-5.1.4/Zend/zend_llist.h hardening-patch-5.1.4-0.4.15/Zend/zend_llist.h --- php-5.1.4/Zend/zend_llist.h 2006-01-05 00:53:04.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_llist.h 2006-09-05 20:31:06.000000000 +0200 @@ -23,6 +23,9 @@ #define ZEND_LLIST_H typedef struct _zend_llist_element { +#if HARDENING_PATCH_LL_PROTECT + unsigned int canary, padding; +#endif struct _zend_llist_element *next; struct _zend_llist_element *prev; char data[1]; /* Needs to always be last in the struct */ @@ -35,6 +38,9 @@ typedef void (*llist_apply_func_t)(void * TSRMLS_DC); typedef struct _zend_llist { +#if HARDENING_PATCH_LL_PROTECT + unsigned int canary_h; /* head */ +#endif zend_llist_element *head; zend_llist_element *tail; size_t count; @@ -42,6 +48,9 @@ llist_dtor_func_t dtor; unsigned char persistent; zend_llist_element *traverse_ptr; +#if HARDENING_PATCH_LL_PROTECT + unsigned int canary_t; /* tail */ +#endif } zend_llist; typedef zend_llist_element* zend_llist_position; diff -Nura php-5.1.4/Zend/zend_modules.h hardening-patch-5.1.4-0.4.15/Zend/zend_modules.h --- php-5.1.4/Zend/zend_modules.h 2006-04-06 23:10:45.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_modules.h 2006-09-05 20:31:06.000000000 +0200 @@ -39,6 +39,7 @@ extern struct _zend_arg_info fifth_arg_force_ref[6]; extern struct _zend_arg_info all_args_by_ref[1]; +#define HARDENING_PATCH_ZEND_MODULE_API_NO 1002051106 #define ZEND_MODULE_API_NO 20050922 #ifdef ZTS #define USING_ZTS 1 @@ -46,13 +47,13 @@ #define USING_ZTS 0 #endif -#define STANDARD_MODULE_HEADER_EX sizeof(zend_module_entry), ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS +#define STANDARD_MODULE_HEADER_EX sizeof(zend_module_entry), HARDENING_PATCH_ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS #define STANDARD_MODULE_HEADER \ STANDARD_MODULE_HEADER_EX, NULL, NULL #define ZE2_STANDARD_MODULE_HEADER \ STANDARD_MODULE_HEADER_EX, ini_entries, NULL -#define STANDARD_MODULE_PROPERTIES_EX 0, 0, 0, NULL, 0 +#define STANDARD_MODULE_PROPERTIES_EX 0, 0, 0, NULL, 0, ZEND_MODULE_API_NO #define STANDARD_MODULE_PROPERTIES \ NULL, STANDARD_MODULE_PROPERTIES_EX @@ -87,6 +88,7 @@ unsigned char type; void *handle; int module_number; + unsigned int real_zend_api; }; #define MODULE_DEP_REQUIRED 1 diff -Nura php-5.1.4/Zend/zend_opcode.c hardening-patch-5.1.4-0.4.15/Zend/zend_opcode.c --- php-5.1.4/Zend/zend_opcode.c 2006-04-10 14:26:53.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_opcode.c 2006-09-05 20:31:06.000000000 +0200 @@ -98,6 +98,9 @@ op_array->uses_this = 0; op_array->start_op = NULL; +#if HARDENING_PATCH + op_array->created_by_eval = 0; +#endif zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_ctor_handler, op_array TSRMLS_CC); } diff -Nura php-5.1.4/Zend/zend_vm_def.h hardening-patch-5.1.4-0.4.15/Zend/zend_vm_def.h --- php-5.1.4/Zend/zend_vm_def.h 2006-04-12 13:37:50.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_vm_def.h 2006-09-05 20:31:06.000000000 +0200 @@ -1769,6 +1769,37 @@ efree(lcname); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } +#if HARDENING_PATCH + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } +#endif efree(lcname); if (OP2_TYPE != IS_CONST) { @@ -1994,6 +2025,34 @@ if (zend_hash_find(EG(function_table), fname->value.str.val, fname->value.str.len+1, (void **) &EX(function_state).function)==FAILURE) { zend_error_noreturn(E_ERROR, "Unknown function: %s()", fname->value.str.val); } +#if HARDENING_PATCH + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), fname->value.str.val, fname->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", fname->value.str.val); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), fname->value.str.val, fname->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", fname->value.str.val); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), fname->value.str.val, fname->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", fname->value.str.val); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), fname->value.str.val, fname->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", fname->value.str.val); + zend_bailout(); + } + } +#endif + EX(object) = NULL; FREE_OP1(); @@ -2709,7 +2768,12 @@ int dummy = 1; zend_file_handle file_handle; - if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#if HARDENING_PATCH_INC_PROTECT + if (zend_is_valid_include(inc_filename) + && (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC))) { +#else + if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#endif if (!file_handle.opened_path) { file_handle.opened_path = estrndup(inc_filename->value.str.val, inc_filename->value.str.len); @@ -2734,6 +2798,11 @@ break; case ZEND_INCLUDE: case ZEND_REQUIRE: +#if HARDENING_PATCH_INC_PROTECT + if (!zend_is_valid_include(inc_filename)) { + break; + } +#endif new_op_array = compile_filename(opline->op2.u.constant.value.lval, inc_filename TSRMLS_CC); break; case ZEND_EVAL: { diff -Nura php-5.1.4/Zend/zend_vm_execute.h hardening-patch-5.1.4-0.4.15/Zend/zend_vm_execute.h --- php-5.1.4/Zend/zend_vm_execute.h 2006-04-12 13:37:50.000000000 +0200 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_vm_execute.h 2006-09-05 20:31:07.000000000 +0200 @@ -56,6 +56,16 @@ EX(symbol_table) = EG(active_symbol_table); EX(prev_execute_data) = EG(current_execute_data); EG(current_execute_data) = &execute_data; +#if HARDENING_PATCH + EX(execute_depth) = 0; + + if ((op_array->type == ZEND_EVAL_CODE || op_array->created_by_eval)&& EG(in_code_type) != ZEND_SANDBOX_CODE) { + EG(in_code_type) = ZEND_EVAL_CODE; + } else if (op_array->type == ZEND_SANDBOX_CODE) { + EG(in_code_type) = ZEND_SANDBOX_CODE; + op_array->type = ZEND_EVAL_CODE; + } +#endif EG(in_execution) = 1; if (op_array->start_op) { @@ -81,6 +91,18 @@ */ EX(function_state).function_symbol_table = NULL; #endif +#if HARDENING_PATCH + if (EX(prev_execute_data) == NULL) { + EX(execute_depth) = 0; + } else { + EX(execute_depth) = EX(prev_execute_data)->execute_depth + 1; + } + + if (EG(hphp_executor_max_depth) > 0 && EX(execute_depth) > EG(hphp_executor_max_depth)) { + zend_security_log(S_EXECUTOR, "Maximum execution depth of %u violated", EG(hphp_executor_max_depth)); + zend_bailout(); + } +#endif while (1) { #ifdef ZEND_WIN32 @@ -724,6 +746,37 @@ efree(lcname); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } +#if HARDENING_PATCH + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } +#endif efree(lcname); if (IS_CONST != IS_CONST) { @@ -925,6 +978,37 @@ efree(lcname); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } +#if HARDENING_PATCH + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } +#endif efree(lcname); if (IS_TMP_VAR != IS_CONST) { @@ -1083,6 +1167,37 @@ efree(lcname); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } +#if HARDENING_PATCH + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } +#endif efree(lcname); if (IS_VAR != IS_CONST) { @@ -1330,6 +1445,37 @@ efree(lcname); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval); } +#if HARDENING_PATCH + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), lcname, function_name_strlen+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", lcname); + efree(lcname); + zend_bailout(); + } + } +#endif efree(lcname); if (IS_CV != IS_CONST) { @@ -1635,6 +1781,34 @@ if (zend_hash_find(EG(function_table), fname->value.str.val, fname->value.str.len+1, (void **) &EX(function_state).function)==FAILURE) { zend_error_noreturn(E_ERROR, "Unknown function: %s()", fname->value.str.val); } +#if HARDENING_PATCH + if (EG(in_code_type) == ZEND_EVAL_CODE) { + if (HG(eval_whitelist) != NULL) { + if (!zend_hash_exists(HG(eval_whitelist), fname->value.str.val, fname->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function outside of eval whitelist called: %s()", fname->value.str.val); + zend_bailout(); + } + } else if (HG(eval_blacklist) != NULL) { + if (zend_hash_exists(HG(eval_blacklist), fname->value.str.val, fname->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function within eval blacklist called: %s()", fname->value.str.val); + zend_bailout(); + } + } + } + + if (HG(func_whitelist) != NULL) { + if (!zend_hash_exists(HG(func_whitelist), fname->value.str.val, fname->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function outside of whitelist called: %s()", fname->value.str.val); + zend_bailout(); + } + } else if (HG(func_blacklist) != NULL) { + if (zend_hash_exists(HG(func_blacklist), fname->value.str.val, fname->value.str.len+1)) { + zend_security_log(S_EXECUTOR, "function within blacklist called: %s()", fname->value.str.val); + zend_bailout(); + } + } +#endif + EX(object) = NULL; return zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -1914,7 +2088,12 @@ int dummy = 1; zend_file_handle file_handle; - if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#if HARDENING_PATCH_INC_PROTECT + if (zend_is_valid_include(inc_filename) + && (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC))) { +#else + if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#endif if (!file_handle.opened_path) { file_handle.opened_path = estrndup(inc_filename->value.str.val, inc_filename->value.str.len); @@ -1939,6 +2118,11 @@ break; case ZEND_INCLUDE: case ZEND_REQUIRE: +#if HARDENING_PATCH_INC_PROTECT + if (!zend_is_valid_include(inc_filename)) { + break; + } +#endif new_op_array = compile_filename(opline->op2.u.constant.value.lval, inc_filename TSRMLS_CC); break; case ZEND_EVAL: { @@ -4345,7 +4529,12 @@ int dummy = 1; zend_file_handle file_handle; - if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#if HARDENING_PATCH_INC_PROTECT + if (zend_is_valid_include(inc_filename) + && (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC))) { +#else + if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#endif if (!file_handle.opened_path) { file_handle.opened_path = estrndup(inc_filename->value.str.val, inc_filename->value.str.len); @@ -4370,6 +4559,11 @@ break; case ZEND_INCLUDE: case ZEND_REQUIRE: +#if HARDENING_PATCH_INC_PROTECT + if (!zend_is_valid_include(inc_filename)) { + break; + } +#endif new_op_array = compile_filename(opline->op2.u.constant.value.lval, inc_filename TSRMLS_CC); break; case ZEND_EVAL: { @@ -7358,7 +7552,12 @@ int dummy = 1; zend_file_handle file_handle; - if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#if HARDENING_PATCH_INC_PROTECT + if (zend_is_valid_include(inc_filename) + && (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC))) { +#else + if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#endif if (!file_handle.opened_path) { file_handle.opened_path = estrndup(inc_filename->value.str.val, inc_filename->value.str.len); @@ -7383,6 +7582,11 @@ break; case ZEND_INCLUDE: case ZEND_REQUIRE: +#if HARDENING_PATCH_INC_PROTECT + if (!zend_is_valid_include(inc_filename)) { + break; + } +#endif new_op_array = compile_filename(opline->op2.u.constant.value.lval, inc_filename TSRMLS_CC); break; case ZEND_EVAL: { @@ -19470,7 +19674,12 @@ int dummy = 1; zend_file_handle file_handle; - if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#if HARDENING_PATCH_INC_PROTECT + if (zend_is_valid_include(inc_filename) + && (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC))) { +#else + if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#endif if (!file_handle.opened_path) { file_handle.opened_path = estrndup(inc_filename->value.str.val, inc_filename->value.str.len); @@ -19495,6 +19704,11 @@ break; case ZEND_INCLUDE: case ZEND_REQUIRE: +#if HARDENING_PATCH_INC_PROTECT + if (!zend_is_valid_include(inc_filename)) { + break; + } +#endif new_op_array = compile_filename(opline->op2.u.constant.value.lval, inc_filename TSRMLS_CC); break; case ZEND_EVAL: { diff -Nura php-5.1.4/Zend/zend_vm_execute.skl hardening-patch-5.1.4-0.4.15/Zend/zend_vm_execute.skl --- php-5.1.4/Zend/zend_vm_execute.skl 2005-12-01 13:50:58.000000000 +0100 +++ hardening-patch-5.1.4-0.4.15/Zend/zend_vm_execute.skl 2006-09-05 20:31:07.000000000 +0200 @@ -27,6 +27,16 @@ EX(symbol_table) = EG(active_symbol_table); EX(prev_execute_data) = EG(current_execute_data); EG(current_execute_data) = &execute_data; +#if HARDENING_PATCH + EX(execute_depth) = 0; + + if ((op_array->type == ZEND_EVAL_CODE || op_array->created_by_eval)&& EG(in_code_type) != ZEND_SANDBOX_CODE) { + EG(in_code_type) = ZEND_EVAL_CODE; + } else if (op_array->type == ZEND_SANDBOX_CODE) { + EG(in_code_type) = ZEND_SANDBOX_CODE; + op_array->type = ZEND_EVAL_CODE; + } +#endif EG(in_execution) = 1; if (op_array->start_op) { @@ -52,6 +62,18 @@ */ EX(function_state).function_symbol_table = NULL; #endif +#if HARDENING_PATCH + if (EX(prev_execute_data) == NULL) { + EX(execute_depth) = 0; + } else { + EX(execute_depth) = EX(prev_execute_data)->execute_depth + 1; + } + + if (EG(hphp_executor_max_depth) > 0 && EX(execute_depth) > EG(hphp_executor_max_depth)) { + zend_security_log(S_EXECUTOR, "Maximum execution depth of %u violated", EG(hphp_executor_max_depth)); + zend_bailout(); + } +#endif while (1) { {%ZEND_VM_CONTINUE_LABEL%}