diff -Naur php-4.4.0RC2/acinclude.m4 hardening-patch-4.4.0RC2-0.3.2/acinclude.m4 --- php-4.4.0RC2/acinclude.m4 2005-04-30 11:31:09.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/acinclude.m4 2005-07-09 08:53:20.597607352 +0200 @@ -1173,6 +1173,36 @@ fi ]) +dnl +dnl Check for broken realpath() +dnl +dnl realpath("/etc/hosts/../passwd",XXX) should not return +dnl "/etc/passwd" +dnl +AC_DEFUN([PHP_AC_BROKEN_REALPATH],[ + AC_CACHE_CHECK(whether realpath is broken, ac_cv_broken_realpath,[ + AC_TRY_RUN([ +main() { + char buf[4096+1]; + buf[0] = 0; + realpath("/etc/hosts/../passwd", buf); + exit(strcmp(buf, "/etc/passwd")==0); +} + ],[ + ac_cv_broken_realpath=no + ],[ + ac_cv_broken_realpath=yes + ],[ + ac_cv_broken_realpath=no + ]) + ]) + if test "$ac_cv_broken_realpath" = "yes"; then + AC_DEFINE(PHP_BROKEN_REALPATH, 1, [Whether realpath is broken]) + else + AC_DEFINE(PHP_BROKEN_REALPATH, 0, [Whether realpath is broken]) + fi +]) + dnl PHP_SHARED_MODULE(module-name, object-var, build-dir, cxx) dnl dnl Basically sets up the link-stage for building module-name diff -Naur php-4.4.0RC2/configure hardening-patch-4.4.0RC2-0.3.2/configure --- php-4.4.0RC2/configure 2005-07-01 12:07:55.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/configure 2005-07-09 08:53:20.634601728 +0200 @@ -394,6 +394,16 @@ ac_default_prefix=/usr/local # Any additions from configure.in: 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: " @@ -846,6 +856,8 @@ ac_help="$ac_help --disable-tokenizer Disable tokenizer support" ac_help="$ac_help + --disable-varfilter Disable Hardening-Patch's variable filter" +ac_help="$ac_help --enable-wddx Enable WDDX support." ac_help="$ac_help --disable-xml Disable XML support using bundled expat lib" @@ -2682,6 +2694,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 @@ -15733,6 +15896,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:15738: checking for declared timezone" >&5 if eval "test \"`echo '$''{'ac_cv_declared_timezone'+set}'`\" = set"; then @@ -87305,6 +87524,265 @@ fi +echo $ac_n "checking whether to enable Hardening-Patch's variable filter""... $ac_c" 1>&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 @@ -99988,7 +100466,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 \ streams.c network.c php_open_temporary_file.c php_logos.c \ - output.c memory_streams.c user_streams.c; do + output.c memory_streams.c user_streams.c hardening_patch.c; do IFS=. set $ac_src @@ -100161,7 +100639,7 @@ zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \ 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_strtod.c; do + zend_ini.c zend_qsort.c zend_multibyte.c zend_strtod.c zend_canary.c; do IFS=. set $ac_src diff -Naur php-4.4.0RC2/configure.in hardening-patch-4.4.0RC2-0.3.2/configure.in --- php-4.4.0RC2/configure.in 2005-07-01 11:53:57.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/configure.in 2005-07-09 08:53:20.636601424 +0200 @@ -238,7 +238,7 @@ sinclude(Zend/acinclude.m4) sinclude(Zend/Zend.m4) sinclude(TSRM/tsrm.m4) - +sinclude(main/hardening_patch.m4) divert(2) @@ -612,6 +612,7 @@ AC_FUNC_ALLOCA dnl PHP_AC_BROKEN_SPRINTF dnl PHP_AC_BROKEN_SNPRINTF +PHP_AC_BROKEN_REALPATH PHP_DECLARED_TIMEZONE PHP_TIME_R_TYPE PHP_READDIR_R_TYPE @@ -1241,7 +1242,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 \ streams.c network.c php_open_temporary_file.c php_logos.c \ - output.c memory_streams.c user_streams.c) + output.c memory_streams.c user_streams.c hardening_patch.c) PHP_ADD_SOURCES(/main, internal_functions.c,, sapi) PHP_ADD_SOURCES(/main, internal_functions_cli.c,, cli) @@ -1254,7 +1255,7 @@ zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \ 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_strtod.c) + zend_ini.c zend_qsort.c zend_multibyte.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 -Naur php-4.4.0RC2/ext/fbsql/php_fbsql.c hardening-patch-4.4.0RC2-0.3.2/ext/fbsql/php_fbsql.c --- php-4.4.0RC2/ext/fbsql/php_fbsql.c 2005-02-09 20:33:32.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/ext/fbsql/php_fbsql.c 2005-07-09 08:53:20.637601272 +0200 @@ -1797,8 +1797,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 -Naur php-4.4.0RC2/ext/mbstring/mbstring.c hardening-patch-4.4.0RC2-0.3.2/ext/mbstring/mbstring.c --- php-4.4.0RC2/ext/mbstring/mbstring.c 2005-02-21 09:03:47.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/ext/mbstring/mbstring.c 2005-07-09 08:53:20.639600968 +0200 @@ -1487,6 +1487,7 @@ char *strtok_buf = NULL, **val_list; zval *array_ptr = (zval *) arg; int n, num, val_len, *len_list; + unsigned int new_val_len; enum mbfl_no_encoding from_encoding; mbfl_string string, resvar, resval; mbfl_encoding_detector *identd = NULL; @@ -1609,8 +1610,14 @@ val_len = len_list[n]; } n++; - /* add variable to symbol table */ - php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC); + /* we need val to be emalloc()ed */ + val = estrndup(val, val_len); + if (sapi_module.input_filter(info->data_type, var, &val, val_len, &new_val_len TSRMLS_CC)) { + /* add variable to symbol table */ + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + if (convd != NULL){ mbfl_string_clear(&resvar); mbfl_string_clear(&resval); diff -Naur php-4.4.0RC2/ext/mysql/php_mysql.c hardening-patch-4.4.0RC2-0.3.2/ext/mysql/php_mysql.c --- php-4.4.0RC2/ext/mysql/php_mysql.c 2005-04-08 00:23:01.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/ext/mysql/php_mysql.c 2005-07-09 08:53:20.641600664 +0200 @@ -1218,6 +1218,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); @@ -1268,6 +1270,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 @@ -1275,12 +1284,20 @@ /* check possible error */ if (MySG(trace_mode)){ if (mysql_errno(&mysql->conn)){ - php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, mysql_error(&mysql->conn)); + 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 + if(use_store == MYSQL_USE_RESULT) { mysql_result=mysql_use_result(&mysql->conn); } else { diff -Naur php-4.4.0RC2/ext/pgsql/pgsql.c hardening-patch-4.4.0RC2-0.3.2/ext/pgsql/pgsql.c --- php-4.4.0RC2/ext/pgsql/pgsql.c 2005-05-11 01:15:06.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/ext/pgsql/pgsql.c 2005-07-09 08:53:20.643600360 +0200 @@ -1001,10 +1001,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 -Naur php-4.4.0RC2/ext/standard/array.c hardening-patch-4.4.0RC2-0.3.2/ext/standard/array.c --- php-4.4.0RC2/ext/standard/array.c 2005-06-21 14:11:19.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/ext/standard/array.c 2005-07-09 08:53:20.645600056 +0200 @@ -1162,6 +1162,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 -Naur php-4.4.0RC2/ext/standard/basic_functions.c hardening-patch-4.4.0RC2-0.3.2/ext/standard/basic_functions.c --- php-4.4.0RC2/ext/standard/basic_functions.c 2005-05-16 10:55:31.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/ext/standard/basic_functions.c 2005-07-09 08:53:20.646599904 +0200 @@ -687,7 +687,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 @@ -3044,6 +3044,35 @@ memcpy(new_key, prefix, prefix_len); memcpy(new_key+prefix_len, hash_key->arKey, hash_key->nKeyLength); + 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_hash_del(&EG(symbol_table), new_key, new_key_len); ZEND_SET_SYMBOL_WITH_LENGTH(&EG(symbol_table), new_key, new_key_len, *var, (*var)->refcount+1, 0); diff -Naur php-4.4.0RC2/ext/standard/file.c hardening-patch-4.4.0RC2-0.3.2/ext/standard/file.c --- php-4.4.0RC2/ext/standard/file.c 2005-03-27 17:53:59.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/ext/standard/file.c 2005-07-09 08:53:20.648599600 +0200 @@ -2469,7 +2469,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 -Naur php-4.4.0RC2/ext/standard/file.h hardening-patch-4.4.0RC2-0.3.2/ext/standard/file.h --- php-4.4.0RC2/ext/standard/file.h 2004-06-21 21:33:47.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/ext/standard/file.h 2005-07-09 08:53:20.648599600 +0200 @@ -64,7 +64,7 @@ PHP_FUNCTION(fd_set); PHP_FUNCTION(fd_isset); #if (!defined(__BEOS__) && !defined(NETWARE) && HAVE_REALPATH) || defined(ZTS) -PHP_FUNCTION(realpath); +PHP_FUNCTION(real_path); #endif #ifdef HAVE_FNMATCH PHP_FUNCTION(fnmatch); diff -Naur php-4.4.0RC2/ext/standard/head.c hardening-patch-4.4.0RC2-0.3.2/ext/standard/head.c --- php-4.4.0RC2/ext/standard/head.c 2005-01-07 22:14:23.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/ext/standard/head.c 2005-07-09 08:53:20.649599448 +0200 @@ -45,10 +45,31 @@ { zend_bool rep = 1; sapi_header_line ctr = {0}; +#if HARDENING_PATCH + int i; +#endif if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &ctr.line, &ctr.line_len, &rep, &ctr.response_code) == FAILURE) return; + +#if HARDENING_PATCH + if (!HG(hphp_multiheader)) { + for (i=0; i0 && (iPHP 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 -Naur php-4.4.0RC2/ext/standard/syslog.c hardening-patch-4.4.0RC2-0.3.2/ext/standard/syslog.c --- php-4.4.0RC2/ext/standard/syslog.c 2004-07-30 16:38:29.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/ext/standard/syslog.c 2005-07-09 08:53:20.650599296 +0200 @@ -42,6 +42,8 @@ */ 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,7 +99,7 @@ /* AIX doesn't have LOG_PERROR */ REGISTER_LONG_CONSTANT("LOG_PERROR", LOG_PERROR, CONST_CS | CONST_PERSISTENT); /*log to stderr*/ #endif - +#endif return SUCCESS; } /* }}} */ diff -Naur php-4.4.0RC2/ext/varfilter/config.m4 hardening-patch-4.4.0RC2-0.3.2/ext/varfilter/config.m4 --- php-4.4.0RC2/ext/varfilter/config.m4 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/ext/varfilter/config.m4 2005-07-09 08:53:20.650599296 +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 -Naur php-4.4.0RC2/ext/varfilter/CREDITS hardening-patch-4.4.0RC2-0.3.2/ext/varfilter/CREDITS --- php-4.4.0RC2/ext/varfilter/CREDITS 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/ext/varfilter/CREDITS 2005-07-09 08:53:20.650599296 +0200 @@ -0,0 +1,2 @@ +varfilter +Stefan Esser \ Kein Zeilenumbruch am Dateiende. diff -Naur php-4.4.0RC2/ext/varfilter/php_varfilter.h hardening-patch-4.4.0RC2-0.3.2/ext/varfilter/php_varfilter.h --- php-4.4.0RC2/ext/varfilter/php_varfilter.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/ext/varfilter/php_varfilter.h 2005-07-09 08:53:20.651599144 +0200 @@ -0,0 +1,111 @@ +/* + +----------------------------------------------------------------------+ + | 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" + + +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; +// 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; +// 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; +// 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; +// fileupload + long max_uploads; + long cur_uploads; + zend_bool disallow_elf_files; + char *verification_script; + +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_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); + +#endif /* PHP_VARFILTER_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff -Naur php-4.4.0RC2/ext/varfilter/varfilter.c hardening-patch-4.4.0RC2-0.3.2/ext/varfilter/varfilter.c --- php-4.4.0RC2/ext/varfilter/varfilter.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/ext/varfilter/varfilter.c 2005-07-09 08:58:18.504318648 +0200 @@ -0,0 +1,604 @@ +/* + +----------------------------------------------------------------------+ + | 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; + +/* {{{ 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.3.2", /* 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.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.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.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.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->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->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->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->max_uploads = 25; + varfilter_globals->disallow_elf_files = 1; + varfilter_globals->verification_script = NULL; +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(varfilter) +{ + ZEND_INIT_MODULE_GLOBALS(varfilter, php_varfilter_init_globals, NULL); + REGISTER_INI_ENTRIES(); + + sapi_register_input_filter(varfilter_input_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); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(varfilter) +{ +// zend_hash_apply_with_arguments(&HG(lists.functionlists), (apply_func_args_t) show_stuff, 0); + + 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; + + 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_PRE_UPLOAD_FILTER_FUNC + */ +SAPI_PRE_UPLOAD_FILTER_FUNC(varfilter_pre_upload_filter) +{ + /* 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"); + 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 - file dropped"); + 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, *copy_var; + unsigned int var_len, total_len, depth = 0, rv; + + /* Drop this variable if the limit is 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); + 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); + 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); + 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); + 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; + } + + /* 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 -Naur php-4.4.0RC2/main/fopen_wrappers.c hardening-patch-4.4.0RC2-0.3.2/main/fopen_wrappers.c --- php-4.4.0RC2/main/fopen_wrappers.c 2005-02-03 00:44:07.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/main/fopen_wrappers.c 2005-07-09 08:53:20.652598992 +0200 @@ -166,6 +166,21 @@ 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)); diff -Naur php-4.4.0RC2/main/hardened_globals.h hardening-patch-4.4.0RC2-0.3.2/main/hardened_globals.h --- php-4.4.0RC2/main/hardened_globals.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/main/hardened_globals.h 2005-07-09 08:53:20.653598840 +0200 @@ -0,0 +1,56 @@ +/* + +----------------------------------------------------------------------+ + | 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 int dummy; +}; + + +#endif /* HARDENED_GLOBALS_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff -Naur php-4.4.0RC2/main/hardening_patch.c hardening-patch-4.4.0RC2-0.3.2/main/hardening_patch.c --- php-4.4.0RC2/main/hardening_patch.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/main/hardening_patch.c 2005-07-09 08:53:20.653598840 +0200 @@ -0,0 +1,322 @@ +/* + +----------------------------------------------------------------------+ + | 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 +} + +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_syslog) & 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) { + 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. must not be a URL */ + if (strstr(filename, "://")) { + 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') is an URL", fname); + efree(fname); + return (0); + } + + /* 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 -Naur php-4.4.0RC2/main/hardening_patch.h hardening-patch-4.4.0RC2-0.3.2/main/hardening_patch.h --- php-4.4.0RC2/main/hardening_patch.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/main/hardening_patch.h 2005-07-09 08:58:18.505318496 +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.3.2" + +#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 -Naur php-4.4.0RC2/main/hardening_patch.m4 hardening-patch-4.4.0RC2-0.3.2/main/hardening_patch.m4 --- php-4.4.0RC2/main/hardening_patch.m4 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/main/hardening_patch.m4 2005-07-09 08:53:20.654598688 +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 -Naur php-4.4.0RC2/main/main.c hardening-patch-4.4.0RC2-0.3.2/main/main.c --- php-4.4.0RC2/main/main.c 2005-06-20 21:59:43.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/main/main.c 2005-07-09 08:53:20.655598536 +0200 @@ -100,6 +100,10 @@ PHPAPI int core_globals_id; #endif +#if HARDENING_PATCH +#include "hardened_globals.h" +#endif + #define ERROR_BUF_LEN 1024 typedef struct { @@ -150,10 +154,33 @@ */ static PHP_INI_MH(OnChangeMemoryLimit) { +#if HARDENING_PATCH + long orig_memory_limit; + + if (entry->modified) { + orig_memory_limit = zend_atoi(entry->orig_value, entry->orig_value_length); + } else { + orig_memory_limit = 1<<30; + } + if (orig_memory_limit < 0 || orig_memory_limit > (1<<30)) { + orig_memory_limit = 1<<30; + } +#endif if (new_value) { PG(memory_limit) = zend_atoi(new_value, new_value_length); +#if HARDENING_PATCH + if (PG(memory_limit) > orig_memory_limit) { + PG(memory_limit) = orig_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) = orig_memory_limit; +#else PG(memory_limit) = 1<<30; /* effectively, no limit */ +#endif } return zend_set_memory_limit(PG(memory_limit)); } @@ -1096,6 +1123,10 @@ tsrm_ls = ts_resource(0); #endif +#if HARDENING_PATCH + hardened_startup(); +#endif + sapi_initialize_empty_request(TSRMLS_C); sapi_activate(TSRMLS_C); @@ -1108,6 +1139,12 @@ php_output_startup(); php_output_activate(TSRMLS_C); +#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; @@ -1209,6 +1246,10 @@ REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_PATH", PHP_CONFIG_FILE_PATH, sizeof(PHP_CONFIG_FILE_PATH)-1, 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); @@ -1316,7 +1357,7 @@ */ static inline void php_register_server_variables(TSRMLS_D) { - zval *array_ptr=NULL; + zval *array_ptr=NULL, *vptr; ALLOC_ZVAL(array_ptr); array_init(array_ptr); @@ -1326,6 +1367,16 @@ /* Server variables */ if (sapi_module.register_server_variables) { sapi_module.register_server_variables(array_ptr TSRMLS_CC); + if (zend_hash_find(array_ptr->value.ht, "HTTP_RAW_POST_DATA", sizeof("HTTP_RAW_POST_DATA"), (void **)&vptr)==SUCCESS) { + char *str; + if (vptr->type != IS_STRING) { + str = "Array"; + } else { + str = vptr->value.str.val; + } + php_security_log(S_VARS, "Attacker tried to overwrite HTTP_RAW_POST_DATA with '%s' through a HTTP header", str); + zend_hash_del(array_ptr->value.ht, "HTTP_RAW_POST_DATA", sizeof("HTTP_RAW_POST_DATA")); + } } /* PHP Authentication support */ diff -Naur php-4.4.0RC2/main/php_config.h.in hardening-patch-4.4.0RC2-0.3.2/main/php_config.h.in --- php-4.4.0RC2/main/php_config.h.in 2005-07-01 12:08:17.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/main/php_config.h.in 2005-07-09 08:53:20.656598384 +0200 @@ -860,6 +860,39 @@ /* Enabling BIND8 compatibility for Panther */ #undef BIND_8_COMPAT +/* Hardening-Patch */ +#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 @@ -1143,6 +1176,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 -Naur php-4.4.0RC2/main/php_content_types.c hardening-patch-4.4.0RC2-0.3.2/main/php_content_types.c --- php-4.4.0RC2/main/php_content_types.c 2002-12-31 17:26:14.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/main/php_content_types.c 2005-07-09 08:53:20.657598232 +0200 @@ -77,6 +77,7 @@ sapi_register_post_entries(php_post_entries); sapi_register_default_post_reader(php_default_post_reader); sapi_register_treat_data(php_default_treat_data); + sapi_register_input_filter(php_default_input_filter); return SUCCESS; } /* }}} */ diff -Naur php-4.4.0RC2/main/php.h hardening-patch-4.4.0RC2-0.3.2/main/php.h --- php-4.4.0RC2/main/php.h 2005-04-17 15:37:29.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/main/php.h 2005-07-09 08:53:20.657598232 +0200 @@ -26,7 +26,7 @@ #include #endif -#define PHP_API_VERSION 20020918 +#define PHP_API_VERSION 1020050626 #define PHP_HAVE_STREAMS #define YYDEBUG 0 @@ -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 @@ -435,6 +443,10 @@ #endif #endif /* !XtOffsetOf */ +#if HARDENING_PATCH +#include "hardening_patch.h" +#endif + #endif /* diff -Naur php-4.4.0RC2/main/php_variables.c hardening-patch-4.4.0RC2-0.3.2/main/php_variables.c --- php-4.4.0RC2/main/php_variables.c 2005-05-17 20:42:35.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/main/php_variables.c 2005-07-09 08:53:20.658598080 +0200 @@ -225,17 +225,28 @@ while (var) { val = strchr(var, '='); if (val) { /* have a value */ - int val_len; + unsigned int val_len, new_val_len; *val++ = '\0'; php_url_decode(var, strlen(var)); val_len = php_url_decode(val, strlen(val)); - php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC); + val = estrndup(val, val_len); + if (sapi_module.input_filter(PARSE_POST, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); } var = php_strtok_r(NULL, "&", &strtok_buf); } } +SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter) +{ + /* TODO: check .ini setting here and apply user-defined input filter */ + *new_val_len = val_len; + return 1; +} + SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data) { char *res = NULL, *var, *val, *separator=NULL; @@ -313,15 +324,26 @@ while (var) { val = strchr(var, '='); if (val) { /* have a value */ - int val_len; + unsigned int val_len, new_val_len; *val++ = '\0'; php_url_decode(var, strlen(var)); val_len = php_url_decode(val, strlen(val)); - php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC); + val = estrndup(val, val_len); + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); } else { + unsigned int val_len, new_val_len; + php_url_decode(var, strlen(var)); - php_register_variable_safe(var, "", 0, array_ptr TSRMLS_CC); + val_len = 0; + val = estrndup("", 0); + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); } var = php_strtok_r(NULL, separator, &strtok_buf); } diff -Naur php-4.4.0RC2/main/rfc1867.c hardening-patch-4.4.0RC2-0.3.2/main/rfc1867.c --- php-4.4.0RC2/main/rfc1867.c 2005-04-04 16:59:58.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/main/rfc1867.c 2005-07-09 08:53:20.659597928 +0200 @@ -127,6 +127,7 @@ #define UPLOAD_ERROR_C 3 /* Partially uploaded */ #define UPLOAD_ERROR_D 4 /* No file uploaded */ #define UPLOAD_ERROR_E 6 /* Missing /tmp or similar directory */ +#define UPLOAD_ERROR_F 7 /* Filter forbids upload */ void php_rfc1867_register_constants(TSRMLS_D) { @@ -136,6 +137,7 @@ REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_PARTIAL", UPLOAD_ERROR_C, CONST_CS | CONST_PERSISTENT); 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_FILTER", UPLOAD_ERROR_F, CONST_CS | CONST_PERSISTENT); } static void normalize_protected_variable(char *varname TSRMLS_DC) @@ -844,6 +846,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); @@ -891,21 +894,24 @@ if (!filename && param) { char *value = multipart_buffer_read_body(mbuff TSRMLS_CC); + unsigned int new_val_len; /* Dummy variable */ if (!value) { value = estrdup(""); } + if (sapi_module.input_filter(PARSE_POST, param, &value, strlen(value), &new_val_len TSRMLS_CC)) { #if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) - if (php_mb_encoding_translation(TSRMLS_C)) { - php_mb_gpc_stack_variable(param, value, &val_list, &len_list, - &num_vars, &num_vars_max TSRMLS_CC); - } else { - safe_php_register_variable(param, value, array_ptr, 0 TSRMLS_CC); - } + if (php_mb_encoding_translation(TSRMLS_C)) { + php_mb_gpc_stack_variable(param, value, &val_list, &len_list, + &num_vars, &num_vars_max TSRMLS_CC); + } else { + safe_php_register_variable(param, value, array_ptr, 0 TSRMLS_CC); + } #else - safe_php_register_variable(param, value, array_ptr, 0 TSRMLS_CC); + safe_php_register_variable(param, value, array_ptr, 0 TSRMLS_CC); #endif + } if (!strcasecmp(param, "MAX_FILE_SIZE")) { max_file_size = atol(value); } @@ -981,6 +987,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_F; + } + + offset = 0; while (!cancel_upload && (blen = multipart_buffer_read(mbuff, buff, sizeof(buff) TSRMLS_CC))) { if (PG(upload_max_filesize) > 0 && total_bytes > PG(upload_max_filesize)) { @@ -990,6 +1001,11 @@ sapi_module.sapi_error(E_WARNING, "MAX_FILE_SIZE of %ld bytes exceeded - file [%s=%s] not saved", max_file_size, param, filename); 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_F; + } + wlen = write(fd, buff, blen); if (wlen < blen) { @@ -997,6 +1013,7 @@ cancel_upload = UPLOAD_ERROR_C; } else { total_bytes += wlen; + offset += wlen; } } } @@ -1011,6 +1028,10 @@ } #endif + if (!cancel_upload && sapi_module.post_upload_filter && sapi_module.post_upload_filter(temp_filename TSRMLS_CC)==FAILURE) { + cancel_upload = UPLOAD_ERROR_F; + } + if (cancel_upload) { if (temp_filename) { if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */ diff -Naur php-4.4.0RC2/main/SAPI.c hardening-patch-4.4.0RC2-0.3.2/main/SAPI.c --- php-4.4.0RC2/main/SAPI.c 2005-02-22 15:46:24.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/main/SAPI.c 2005-07-09 08:53:20.659597928 +0200 @@ -831,6 +831,31 @@ 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_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_flush(TSRMLS_D) { diff -Naur php-4.4.0RC2/main/SAPI.h hardening-patch-4.4.0RC2-0.3.2/main/SAPI.h --- php-4.4.0RC2/main/SAPI.h 2003-04-09 22:27:55.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/main/SAPI.h 2005-07-09 08:53:20.660597776 +0200 @@ -101,9 +101,10 @@ char *current_user; int current_user_length; - /* this is necessary for CLI module */ - int argc; - char **argv; + /* this is necessary for CLI module */ + int argc; + char **argv; + } sapi_request_info; @@ -177,6 +178,10 @@ SAPI_API void sapi_unregister_post_entry(sapi_post_entry *post_entry); SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D)); 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); @@ -238,8 +243,15 @@ int (*get_target_uid)(uid_t * TSRMLS_DC); 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 (*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; + }; @@ -262,16 +274,26 @@ #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) #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_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) SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data); SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader); SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data); +SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter); #define STANDARD_SAPI_MODULE_PROPERTIES diff -Naur php-4.4.0RC2/main/snprintf.c hardening-patch-4.4.0RC2-0.3.2/main/snprintf.c --- php-4.4.0RC2/main/snprintf.c 2005-04-08 07:44:53.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/main/snprintf.c 2005-07-09 08:53:20.661597624 +0200 @@ -1013,7 +1013,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 break; /* diff -Naur php-4.4.0RC2/main/spprintf.c hardening-patch-4.4.0RC2-0.3.2/main/spprintf.c --- php-4.4.0RC2/main/spprintf.c 2005-04-08 07:44:53.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/main/spprintf.c 2005-07-09 08:53:20.661597624 +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 break; /* diff -Naur php-4.4.0RC2/php.ini-dist hardening-patch-4.4.0RC2-0.3.2/php.ini-dist --- php-4.4.0RC2/php.ini-dist 2005-04-28 15:14:45.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/php.ini-dist 2005-07-09 08:53:20.662597472 +0200 @@ -1112,6 +1112,177 @@ ;exif.decode_jis_motorola = JIS ;exif.decode_jis_intel = JIS +[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 logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Execution depth limit +;hphp.executor.max_depth = 8000 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 -Naur php-4.4.0RC2/php.ini-recommended hardening-patch-4.4.0RC2-0.3.2/php.ini-recommended --- php-4.4.0RC2/php.ini-recommended 2005-04-28 15:14:46.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/php.ini-recommended 2005-07-09 08:53:20.663597320 +0200 @@ -1110,6 +1110,177 @@ ;exif.decode_jis_motorola = JIS ;exif.decode_jis_intel = JIS +[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 logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Execution depth limit +;hphp.executor.max_depth = 8000 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 -Naur php-4.4.0RC2/README.input_filter hardening-patch-4.4.0RC2-0.3.2/README.input_filter --- php-4.4.0RC2/README.input_filter 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/README.input_filter 2005-07-09 08:53:20.664597168 +0200 @@ -0,0 +1,193 @@ +Input Filter Support ported from PHP 5 +-------------------------------------- + +XSS (Cross Site Scripting) hacks are becoming more and more prevalent, +and can be quite difficult to prevent. Whenever you accept user data +and somehow display this data back to users, you are likely vulnerable +to XSS hacks. + +The Input Filter support in PHP 5 is aimed at providing the framework +through which a company-wide or site-wide security policy can be +enforced. It is implemented as a SAPI hook and is called from the +treat_data and post handler functions. To implement your own security +policy you will need to write a standard PHP extension. + +A simple implementation might look like the following. This stores the +original raw user data and adds a my_get_raw() function while the normal +$_POST, $_GET and $_COOKIE arrays are only populated with stripped +data. In this simple example all I am doing is calling strip_tags() on +the data. If register_globals is turned on, the default globals that +are created will be stripped ($foo) while a $RAW_foo is created with the +original user input. + +ZEND_BEGIN_MODULE_GLOBALS(my_input_filter) + zval *post_array; + zval *get_array; + zval *cookie_array; +ZEND_END_MODULE_GLOBALS(my_input_filter) + +#ifdef ZTS +#define IF_G(v) TSRMG(my_input_filter_globals_id, zend_my_input_filter_globals *, v) +#else +#define IF_G(v) (my_input_filter_globals.v) +#endif + +ZEND_DECLARE_MODULE_GLOBALS(my_input_filter) + +function_entry my_input_filter_functions[] = { + PHP_FE(my_get_raw, NULL) + {NULL, NULL, NULL} +}; + +zend_module_entry my_input_filter_module_entry = { + STANDARD_MODULE_HEADER, + "my_input_filter", + my_input_filter_functions, + PHP_MINIT(my_input_filter), + PHP_MSHUTDOWN(my_input_filter), + NULL, + PHP_RSHUTDOWN(my_input_filter), + PHP_MINFO(my_input_filter), + "0.1", + STANDARD_MODULE_PROPERTIES +}; + +PHP_MINIT_FUNCTION(my_input_filter) +{ + ZEND_INIT_MODULE_GLOBALS(my_input_filter, php_my_input_filter_init_globals, NULL); + + REGISTER_LONG_CONSTANT("POST", PARSE_POST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("GET", PARSE_GET, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("COOKIE", PARSE_COOKIE, CONST_CS | CONST_PERSISTENT); + + sapi_register_input_filter(my_sapi_input_filter); + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(my_input_filter) +{ + if(IF_G(get_array)) { + zval_ptr_dtor(&IF_G(get_array)); + IF_G(get_array) = NULL; + } + if(IF_G(post_array)) { + zval_ptr_dtor(&IF_G(post_array)); + IF_G(post_array) = NULL; + } + if(IF_G(cookie_array)) { + zval_ptr_dtor(&IF_G(cookie_array)); + IF_G(cookie_array) = NULL; + } + return SUCCESS; +} + +PHP_MINFO_FUNCTION(my_input_filter) +{ + php_info_print_table_start(); + php_info_print_table_row( 2, "My Input Filter Support", "enabled" ); + php_info_print_table_row( 2, "Revision", "$Revision: 1.1 $"); + php_info_print_table_end(); +} + +/* The filter handler. If you return 1 from it, then PHP also registers the + * (modified) variable. Returning 0 prevents PHP from registering the variable; + * you can use this if your filter already registers the variable under a + * different name, or if you just don't want the variable registered at all. */ +SAPI_INPUT_FILTER_FUNC(my_sapi_input_filter) +{ + zval new_var; + zval *array_ptr = NULL; + char *raw_var; + int var_len; + + assert(*val != NULL); + + switch(arg) { + case PARSE_GET: + if(!IF_G(get_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(get_array) = array_ptr; + break; + case PARSE_POST: + if(!IF_G(post_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(post_array) = array_ptr; + break; + case PARSE_COOKIE: + if(!IF_G(cookie_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(cookie_array) = array_ptr; + break; + } + Z_STRLEN(new_var) = val_len; + Z_STRVAL(new_var) = estrndup(*val, val_len); + Z_TYPE(new_var) = IS_STRING; + + var_len = strlen(var); + raw_var = emalloc(var_len+5); /* RAW_ and a \0 */ + strcpy(raw_var, "RAW_"); + strlcat(raw_var,var,var_len+5); + + php_register_variable_ex(raw_var, &new_var, array_ptr TSRMLS_DC); + + php_strip_tags(*val, val_len, NULL, NULL, 0); + + *new_val_len = strlen(*val); + return 1; +} + +PHP_FUNCTION(my_get_raw) +{ + long arg; + char *var; + int var_len; + zval **tmp; + zval *array_ptr = NULL; + HashTable *hash_ptr; + char *raw_var; + + if(zend_parse_parameters(2 TSRMLS_CC, "ls", &arg, &var, &var_len) == FAILURE) { + return; + } + + switch(arg) { + case PARSE_GET: + array_ptr = IF_G(get_array); + break; + case PARSE_POST: + array_ptr = IF_G(post_array); + break; + case PARSE_COOKIE: + array_ptr = IF_G(post_array); + break; + } + + if(!array_ptr) RETURN_FALSE; + + /* + * I'm changing the variable name here because when running with register_globals on, + * the variable will end up in the global symbol table + */ + raw_var = emalloc(var_len+5); /* RAW_ and a \0 */ + strcpy(raw_var, "RAW_"); + strlcat(raw_var,var,var_len+5); + hash_ptr = HASH_OF(array_ptr); + + if(zend_hash_find(hash_ptr, raw_var, var_len+5, (void **)&tmp) == SUCCESS) { + *return_value = **tmp; + zval_copy_ctor(return_value); + } else { + RETVAL_FALSE; + } + efree(raw_var); +} + diff -Naur php-4.4.0RC2/sapi/apache/mod_php4.c hardening-patch-4.4.0RC2-0.3.2/sapi/apache/mod_php4.c --- php-4.4.0RC2/sapi/apache/mod_php4.c 2005-05-19 18:14:46.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/sapi/apache/mod_php4.c 2005-07-09 08:53:20.665597016 +0200 @@ -452,7 +452,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, }; /* }}} */ @@ -898,7 +898,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 -Naur php-4.4.0RC2/sapi/apache2filter/sapi_apache2.c hardening-patch-4.4.0RC2-0.3.2/sapi/apache2filter/sapi_apache2.c --- php-4.4.0RC2/sapi/apache2filter/sapi_apache2.c 2005-04-08 22:35:02.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/sapi/apache2filter/sapi_apache2.c 2005-07-09 08:53:20.665597016 +0200 @@ -556,7 +556,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 -Naur php-4.4.0RC2/sapi/apache2handler/sapi_apache2.c hardening-patch-4.4.0RC2-0.3.2/sapi/apache2handler/sapi_apache2.c --- php-4.4.0RC2/sapi/apache2handler/sapi_apache2.c 2005-04-08 22:35:02.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/sapi/apache2handler/sapi_apache2.c 2005-07-09 08:53:20.666596864 +0200 @@ -340,7 +340,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 -Naur php-4.4.0RC2/sapi/cgi/cgi_main.c hardening-patch-4.4.0RC2-0.3.2/sapi/cgi/cgi_main.c --- php-4.4.0RC2/sapi/cgi/cgi_main.c 2005-04-28 16:24:47.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/sapi/cgi/cgi_main.c 2005-07-09 08:53:20.667596712 +0200 @@ -1440,11 +1440,19 @@ SG(headers_sent) = 1; SG(request_info).no_headers = 1; } +#if HARDENING_PATCH +#if ZEND_DEBUG + 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 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(1); break; diff -Naur php-4.4.0RC2/sapi/cli/php_cli.c hardening-patch-4.4.0RC2-0.3.2/sapi/cli/php_cli.c --- php-4.4.0RC2/sapi/cli/php_cli.c 2005-03-22 16:09:36.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/sapi/cli/php_cli.c 2005-07-09 08:53:20.667596712 +0200 @@ -652,11 +652,19 @@ if (php_request_startup(TSRMLS_C)==FAILURE) { goto err; } +#if HARDENING_PATCH +#if ZEND_DEBUG + 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 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_status=1; goto out; diff -Naur php-4.4.0RC2/TSRM/TSRM.h hardening-patch-4.4.0RC2-0.3.2/TSRM/TSRM.h --- php-4.4.0RC2/TSRM/TSRM.h 2005-02-11 04:34:04.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/TSRM/TSRM.h 2005-07-09 08:53:20.668596560 +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 @@ -90,6 +97,7 @@ #define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts + #ifdef __cplusplus extern "C" { #endif diff -Naur php-4.4.0RC2/TSRM/tsrm_virtual_cwd.c hardening-patch-4.4.0RC2-0.3.2/TSRM/tsrm_virtual_cwd.c --- php-4.4.0RC2/TSRM/tsrm_virtual_cwd.c 2005-02-11 04:34:04.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/TSRM/tsrm_virtual_cwd.c 2005-07-09 08:53:20.669596408 +0200 @@ -192,6 +192,165 @@ 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 && p == 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]; @@ -314,8 +473,7 @@ path = resolved_path; path_length = strlen(path); } else { - /* disable for now - return 1; */ + return 1; } } } else { /* Concat current directory with relative path and then run realpath() on it */ @@ -341,9 +499,8 @@ path = resolved_path; path_length = strlen(path); } else { - /* disable for now free(tmp); - return 1; */ + return 1; } } free(tmp); diff -Naur php-4.4.0RC2/TSRM/tsrm_virtual_cwd.h hardening-patch-4.4.0RC2-0.3.2/TSRM/tsrm_virtual_cwd.h --- php-4.4.0RC2/TSRM/tsrm_virtual_cwd.h 2005-02-11 04:34:04.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/TSRM/tsrm_virtual_cwd.h 2005-07-09 08:53:20.669596408 +0200 @@ -128,6 +128,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); diff -Naur php-4.4.0RC2/Zend/zend_alloc.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend_alloc.c --- php-4.4.0RC2/Zend/zend_alloc.c 2005-04-07 22:54:33.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_alloc.c 2005-07-09 08:53:20.670596256 +0200 @@ -56,6 +56,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 @@ -95,9 +100,17 @@ if (p==AG(head)) { \ AG(head) = p->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; \ } @@ -129,6 +142,12 @@ 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 (!ZEND_DISABLE_MEMORY_CACHE && (CACHE_INDEX < MAX_CACHED_MEMORY) && (AG(cache_count)[CACHE_INDEX] > 0)) { @@ -146,6 +165,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->cached = 0; p->size = size; return (void *)((char *)p + sizeof(zend_mem_header) + MEM_HEADER_PADDING); @@ -161,7 +184,7 @@ 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); } HANDLE_BLOCK_INTERRUPTIONS(); @@ -191,7 +214,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); } @@ -218,17 +244,33 @@ return emalloc_rel(lval + offset); } } - + +#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 (%ld * %ld + %ld)", nmemb, size, offset); return 0; } 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 + canary_2 = (unsigned int *)(((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + p->size + END_MAGIC_SIZE); + if (p->canary != HG(canary_1) || *canary_2 != HG(canary_2)) { + zend_security_log(S_MEMORY, "canary mismatch on efree() - heap overflow or double efree detected"); + exit(1); + } + /* to catch double efree()s */ + *canary_2 = p->canary = 0; +#endif + #if defined(ZTS) && TSRM_DEBUG if (p->thread_id != tsrm_thread_id()) { tsrm_error(TSRM_ERROR_LEVEL_ERROR, "Memory block allocated at %s:(%d) on thread %x freed at %s:(%d) on thread %x, ignoring", @@ -273,6 +315,9 @@ 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); @@ -292,6 +337,9 @@ 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(); @@ -303,6 +351,14 @@ p = orig = (zend_mem_header *) ((char *)ptr-sizeof(zend_mem_header)-MEM_HEADER_PADDING); +#if HARDENING_PATCH_MM_PROTECT + canary_2 = *(unsigned int *)(((char *) p) + sizeof(zend_mem_header) + MEM_HEADER_PADDING + p->size + END_MAGIC_SIZE); + if (p->canary != HG(canary_1) || canary_2 != HG(canary_2)) { + 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; @@ -326,7 +382,7 @@ } #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); if (!p) { if (!allow_failure) { fprintf(stderr,"FATAL: erealloc(): Unable to allocate %ld bytes\n", (long) size); @@ -348,6 +404,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(); @@ -422,6 +481,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 -Naur php-4.4.0RC2/Zend/zend_alloc.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_alloc.h --- php-4.4.0RC2/Zend/zend_alloc.h 2005-06-07 15:37:33.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_alloc.h 2005-07-09 08:53:20.671596104 +0200 @@ -32,6 +32,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 -Naur php-4.4.0RC2/Zend/zend_builtin_functions.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend_builtin_functions.c --- php-4.4.0RC2/Zend/zend_builtin_functions.c 2005-06-23 14:20:47.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_builtin_functions.c 2005-07-09 08:53:20.671596104 +0200 @@ -49,6 +49,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); @@ -101,6 +104,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) @@ -805,6 +811,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 -Naur php-4.4.0RC2/Zend/zend.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend.c --- php-4.4.0RC2/Zend/zend.c 2005-06-09 12:14:25.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend.c 2005-07-09 08:53:20.672595952 +0200 @@ -53,6 +53,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 void (*zend_on_timeout)(int seconds TSRMLS_DC); @@ -70,9 +76,80 @@ 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; + } else { + EG(hphp_log_syslog) = atoi(new_value) | S_MEMORY; + } + 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; + } else { + EG(hphp_log_sapi) = atoi(new_value); + } + 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; + } + return SUCCESS; +} +static ZEND_INI_MH(OnUpdateHPHP_log_scriptname) +{ + if (!new_value) { + EG(hphp_log_scriptname) = NULL; + } else { + if (EG(hphp_log_scriptname)) { + pefree(EG(hphp_log_scriptname),1); + } + EG(hphp_log_scriptname) = pestrdup(new_value,1); + } + 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) + STD_ZEND_INI_ENTRY("hphp.executor.max_depth", "0", ZEND_INI_PERDIR, OnUpdateLong, hphp_executor_max_depth, zend_executor_globals, executor_globals) + STD_ZEND_INI_BOOLEAN("hphp.sql.bailout_on_error", "0", ZEND_INI_PERDIR, OnUpdateBool, hphp_sql_bailout_on_error, hardened_globals_struct, hardened_globals) + STD_ZEND_INI_BOOLEAN("hphp.multiheader", "0", ZEND_INI_PERDIR, OnUpdateBool, hphp_multiheader, hardened_globals_struct, hardened_globals) +#endif ZEND_INI_END() @@ -354,6 +431,7 @@ zend_init_rsrc_plist(TSRMLS_C); EG(lambda_count)=0; EG(user_error_handler) = NULL; + EG(in_code_type) = 0; EG(in_execution) = 0; EG(current_execute_data) = NULL; } @@ -420,6 +498,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 @@ -623,6 +709,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 -Naur php-4.4.0RC2/Zend/zend_canary.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend_canary.c --- php-4.4.0RC2/Zend/zend_canary.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_canary.c 2005-07-09 08:53:20.673595800 +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 -Naur php-4.4.0RC2/Zend/zend_compile.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_compile.h --- php-4.4.0RC2/Zend/zend_compile.h 2005-06-06 11:30:09.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_compile.h 2005-07-09 08:53:20.673595800 +0200 @@ -549,6 +549,7 @@ #define ZEND_USER_FUNCTION 2 #define ZEND_OVERLOADED_FUNCTION 3 #define ZEND_EVAL_CODE 4 +#define ZEND_SANDBOX_CODE 6 #define ZEND_INTERNAL_CLASS 1 #define ZEND_USER_CLASS 2 diff -Naur php-4.4.0RC2/Zend/zend_constants.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend_constants.c --- php-4.4.0RC2/Zend/zend_constants.c 2004-07-13 21:29:45.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_constants.c 2005-07-09 08:53:20.674595648 +0200 @@ -111,6 +111,72 @@ 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_VARS, 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_MISC", S_MISC, 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 -Naur php-4.4.0RC2/Zend/zend_errors.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_errors.h --- php-4.4.0RC2/Zend/zend_errors.h 2002-12-31 17:22:59.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_errors.h 2005-07-09 08:53:20.674595648 +0200 @@ -36,5 +36,16 @@ #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_MISC (1<<30L) +#define S_ALL (S_MEMORY | S_VARS | S_INCLUDE | S_FILES | S_MISC | S_SQL | S_EXECUTOR) +#endif + #endif /* ZEND_ERRORS_H */ diff -Naur php-4.4.0RC2/Zend/zend_execute_API.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend_execute_API.c --- php-4.4.0RC2/Zend/zend_execute_API.c 2005-05-18 19:58:09.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_execute_API.c 2005-07-09 08:53:20.675595496 +0200 @@ -142,6 +142,7 @@ EG(class_table) = CG(class_table); EG(in_execution) = 0; + EG(in_code_type) = 0; zend_ptr_stack_init(&EG(argument_stack)); @@ -431,12 +432,14 @@ zend_execute_data execute_data; /* Initialize execute_data */ + memset(&execute_data, 0, sizeof(execute_data)); EX(fbc) = NULL; EX(object).ptr = NULL; EX(ce) = NULL; EX(Ts) = NULL; EX(op_array) = NULL; EX(opline) = NULL; + EX(execute_depth) = 0; *retval_ptr_ptr = NULL; @@ -606,8 +609,7 @@ return SUCCESS; } - -ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC) +ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, int type TSRMLS_DC) { zval pv; zend_op_array *new_op_array; @@ -640,6 +642,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; @@ -673,6 +676,10 @@ return retval; } +ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC) +{ + return (zend_eval_string_ex(str, retval_ptr, string_name, ZEND_EVAL_CODE TSRMLS_CC)); +} void execute_new_code(TSRMLS_D) { diff -Naur php-4.4.0RC2/Zend/zend_execute.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend_execute.c --- php-4.4.0RC2/Zend/zend_execute.c 2005-06-27 08:15:48.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_execute.c 2005-07-09 08:53:20.676595344 +0200 @@ -1042,6 +1042,7 @@ zend_execute_data execute_data; /* Initialize execute_data */ + memset(&execute_data, 0, sizeof(execute_data)); EX(fbc) = NULL; EX(ce) = NULL; EX(object).ptr = NULL; @@ -1053,9 +1054,21 @@ } EX(prev_execute_data) = EG(current_execute_data); EX(original_in_execution)=EG(in_execution); + EX(original_in_code_type)=EG(in_code_type); EG(current_execute_data) = &execute_data; +#if HARDENING_PATCH + EX(execute_depth) = 0; + + if (op_array->type == ZEND_EVAL_CODE && 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) { EX(opline) = op_array->start_op; @@ -1087,6 +1100,19 @@ } } +#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 if (EG(timed_out)) { @@ -1816,6 +1842,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); return; } @@ -2195,7 +2222,12 @@ int dummy = 1; zend_file_handle file_handle = {0}; +#if HARDENING_PATCH_INC_PROTECT + if (zend_is_valid_include(inc_filename) + && zend_open(inc_filename->value.str.val, &file_handle) == SUCCESS +#else if (zend_open(inc_filename->value.str.val, &file_handle) == SUCCESS +#endif && ZEND_IS_VALID_FILE_HANDLE(&file_handle)) { file_handle.filename = inc_filename->value.str.val; @@ -2224,6 +2256,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(EX(opline)->op2.u.constant.value.lval, inc_filename TSRMLS_CC); break; case ZEND_EVAL: { diff -Naur php-4.4.0RC2/Zend/zend_execute_globals.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_execute_globals.h --- php-4.4.0RC2/Zend/zend_execute_globals.h 2005-06-06 11:30:09.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_execute_globals.h 2005-07-09 08:53:20.677595192 +0200 @@ -60,6 +60,8 @@ object_info object; temp_variable *Ts; zend_bool original_in_execution; + zend_uint original_in_code_type; + zend_uint execute_depth; zend_op_array *op_array; struct _zend_execute_data *prev_execute_data; } zend_execute_data; diff -Naur php-4.4.0RC2/Zend/zend_extensions.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_extensions.h --- php-4.4.0RC2/Zend/zend_extensions.h 2005-06-06 11:44:59.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_extensions.h 2005-07-09 08:53:20.677595192 +0200 @@ -23,7 +23,9 @@ #include "zend_compile.h" -#define ZEND_EXTENSION_API_NO 20050606 +/* Create own API version number for Hardening-Patch */ + +#define ZEND_EXTENSION_API_NO 1020050705 typedef struct _zend_extension_version_info { int zend_extension_api_no; diff -Naur php-4.4.0RC2/Zend/zend_globals.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_globals.h --- php-4.4.0RC2/Zend/zend_globals.h 2004-11-04 00:15:05.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_globals.h 2005-07-09 08:53:20.678595040 +0200 @@ -163,6 +163,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; @@ -176,6 +186,7 @@ int ticks_count; zend_bool in_execution; + zend_uint in_code_type; zend_bool bailout_set; zend_bool full_tables_cleanup; diff -Naur php-4.4.0RC2/Zend/zend.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend.h --- php-4.4.0RC2/Zend/zend.h 2005-01-25 14:08:41.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend.h 2005-07-09 08:53:20.678595040 +0200 @@ -275,9 +275,10 @@ struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ + zend_uint refcount; + zend_ushort flags; zend_uchar type; /* active type */ zend_uchar is_ref; - zend_ushort refcount; }; @@ -338,6 +339,12 @@ void (*ticks_function)(int ticks); void (*on_timeout)(int seconds TSRMLS_DC); zend_bool (*open_function)(const char *filename, struct _zend_file_handle *); +#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; @@ -469,7 +476,16 @@ extern ZEND_API void (*zend_ticks_function)(int ticks); extern ZEND_API void (*zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) ZEND_ATTRIBUTE_PTR_FORMAT(printf, 4, 0); extern void (*zend_on_timeout)(int seconds 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_PTR_FORMAT(printf, 2, 3); @@ -576,6 +592,11 @@ #define ZEND_MAX_RESERVED_RESOURCES 4 +#if HARDENING_PATCH +#include "hardened_globals.h" +#include "php_syslog.h" +#endif + #endif /* ZEND_H */ /* diff -Naur php-4.4.0RC2/Zend/zend_hash.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend_hash.c --- php-4.4.0RC2/Zend/zend_hash.c 2005-04-28 09:34:32.000000000 +0200 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_hash.c 2005-07-09 08:53:20.679594888 +0200 @@ -26,6 +26,17 @@ # include #endif +#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 HANDLE_NUMERIC(key, length, func) { \ register char *tmp=key; \ \ @@ -175,6 +186,9 @@ { uint i = 3; Bucket **tmp; +#if HARDENING_PATCH_HASH_PROTECT + TSRMLS_FETCH(); +#endif SET_INCONSISTENT(HT_OK); @@ -184,6 +198,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->pListHead = NULL; ht->pListTail = NULL; @@ -259,6 +280,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); @@ -327,6 +351,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); @@ -402,6 +429,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); @@ -450,7 +480,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; @@ -460,6 +490,7 @@ HANDLE_UNBLOCK_INTERRUPTIONS(); return SUCCESS; } + zend_error(E_ERROR, "zend_hash_do_resize - out of memory"); return FAILURE; } return SUCCESS; @@ -524,6 +555,9 @@ ht->pInternalPointer = p->pListNext; } if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } if (!p->pDataPtr) { @@ -553,6 +587,9 @@ q = p; p = p->pListNext; if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(q->pData); } if (!q->pDataPtr && q->pData) { @@ -579,6 +616,9 @@ q = p; p = p->pListNext; if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(q->pData); } if (!q->pDataPtr && q->pData) { @@ -608,6 +648,9 @@ HANDLE_BLOCK_INTERRUPTIONS(); if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } if (!p->pDataPtr) { diff -Naur php-4.4.0RC2/Zend/zend_hash.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_hash.h --- php-4.4.0RC2/Zend/zend_hash.h 2002-12-31 17:23:03.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_hash.h 2005-07-09 08:53:20.680594736 +0200 @@ -54,6 +54,9 @@ } Bucket; typedef struct _hashtable { +#if HARDENING_PATCH_HASH_PROTECT + unsigned int canary; +#endif uint nTableSize; uint nTableMask; uint nNumOfElements; diff -Naur php-4.4.0RC2/Zend/zend_ini.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_ini.h --- php-4.4.0RC2/Zend/zend_ini.h 2005-01-09 18:00:16.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_ini.h 2005-07-09 08:53:20.680594736 +0200 @@ -174,6 +174,7 @@ /* Standard message handlers */ BEGIN_EXTERN_C() ZEND_API ZEND_INI_MH(OnUpdateBool); +#define OnUpdateLong OnUpdateInt ZEND_API ZEND_INI_MH(OnUpdateInt); ZEND_API ZEND_INI_MH(OnUpdateReal); ZEND_API ZEND_INI_MH(OnUpdateString); diff -Naur php-4.4.0RC2/Zend/zend_llist.c hardening-patch-4.4.0RC2-0.3.2/Zend/zend_llist.c --- php-4.4.0RC2/Zend/zend_llist.c 2002-12-31 17:23:04.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_llist.c 2005-07-09 08:53:20.681594584 +0200 @@ -21,9 +21,34 @@ #include "zend.h" #include "zend_llist.h" #include "zend_qsort.h" +#include "zend_globals.h" + +#define CHECK_LIST_CANARY(list) \ + if (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) \ + if (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 (!HG(ll_canary_inited)) { + HG(canary_3) = zend_canary(); + HG(canary_4) = zend_canary(); + HG(ll_canary_inited) = 1; + } + l->canary_h = HG(canary_3); + l->canary_t = HG(canary_4); +#endif l->head = NULL; l->tail = NULL; l->count = 0; @@ -37,6 +62,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 = HG(canary_3); +#endif tmp->prev = l->tail; tmp->next = NULL; if (l->tail) { @@ -55,6 +85,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 = HG(canary_3); +#endif tmp->next = l->head; tmp->prev = NULL; if (l->head) { @@ -91,10 +126,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) +#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; @@ -106,7 +151,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) +#endif next = current->next; if (l->dtor) { l->dtor(current->data); @@ -131,7 +183,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) +#endif if (l->tail->prev) { l->tail->prev->next = NULL; } @@ -157,9 +216,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) +#endif zend_llist_add_element(dst, ptr->data); ptr = ptr->next; } @@ -170,11 +236,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) +#endif next = element->next; if (func(element->data)) { DEL_LLIST_ELEMENT(element, l); +#if HARDENING_PATCH_LL_PROTECT + element->canary = 0; +#endif } element = next; } @@ -185,7 +261,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) +#endif func(element->data TSRMLS_CC); } } @@ -197,6 +279,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; } @@ -206,6 +291,9 @@ ptr = &elements[0]; for (element=l->head; element; element=element->next) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(element) +#endif *ptr++ = element; } @@ -228,7 +316,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) +#endif func(element->data, arg TSRMLS_CC); } } @@ -239,8 +333,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) +#endif func(element->data, num_args, args TSRMLS_CC); } va_end(args); @@ -249,6 +349,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; } @@ -256,8 +360,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) +#endif return (*current)->data; } else { return NULL; @@ -269,8 +380,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) +#endif return (*current)->data; } else { return NULL; @@ -282,9 +400,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) +#endif *current = (*current)->next; if (*current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(*current) +#endif return (*current)->data; } } @@ -296,9 +424,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) +#endif *current = (*current)->prev; if (*current) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(*current) +#endif return (*current)->data; } } diff -Naur php-4.4.0RC2/Zend/zend_llist.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_llist.h --- php-4.4.0RC2/Zend/zend_llist.h 2002-12-31 17:23:04.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_llist.h 2005-07-09 08:53:20.681594584 +0200 @@ -24,6 +24,9 @@ #include typedef struct _zend_llist_element { +#if HARDENING_PATCH_LL_PROTECT + unsigned int canary; +#endif struct _zend_llist_element *next; struct _zend_llist_element *prev; char data[1]; /* Needs to always be last in the struct */ @@ -36,6 +39,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 size; @@ -43,6 +49,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 -Naur php-4.4.0RC2/Zend/zend_modules.h hardening-patch-4.4.0RC2-0.3.2/Zend/zend_modules.h --- php-4.4.0RC2/Zend/zend_modules.h 2002-12-31 17:23:04.000000000 +0100 +++ hardening-patch-4.4.0RC2-0.3.2/Zend/zend_modules.h 2005-07-09 08:53:20.682594432 +0200 @@ -34,7 +34,7 @@ ZEND_API extern unsigned char second_arg_force_ref[]; ZEND_API extern unsigned char third_arg_force_ref[]; -#define ZEND_MODULE_API_NO 20020429 +#define ZEND_MODULE_API_NO 1020050626 #ifdef ZTS #define USING_ZTS 1 #else