diff -Naur php-5.0.4/acinclude.m4 hardening-patch-5.0.4-0.3.2/acinclude.m4 --- php-5.0.4/acinclude.m4 2005-01-25 14:02:45.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/acinclude.m4 2005-06-26 20:59:20.000000000 +0200 @@ -1169,6 +1169,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-5.0.4/configure hardening-patch-5.0.4-0.3.2/configure --- php-5.0.4/configure 2005-04-03 11:42:50.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/configure 2005-06-28 15:47:07.000000000 +0200 @@ -401,6 +401,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: " @@ -857,6 +867,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." @@ -2749,6 +2761,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 @@ -17390,6 +17553,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:17395: checking for declared timezone" >&5 if eval "test \"`echo '$''{'ac_cv_declared_timezone'+set}'`\" = set"; then @@ -86878,6 +87097,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 @@ -97351,7 +97829,7 @@ php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \ strlcat.c mergesort.c reentrancy.c php_variables.c php_ticks.c \ network.c php_open_temporary_file.c php_logos.c \ - output.c ; do + output.c hardening_patch.c ; do IFS=. set $ac_src @@ -97579,7 +98057,7 @@ zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \ zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \ zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ - zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c; do + zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_canary.c; do IFS=. set $ac_src diff -Naur php-5.0.4/configure.in hardening-patch-5.0.4-0.3.2/configure.in --- php-5.0.4/configure.in 2005-03-30 23:43:12.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/configure.in 2005-06-26 22:28:34.000000000 +0200 @@ -235,7 +235,7 @@ sinclude(Zend/acinclude.m4) sinclude(Zend/Zend.m4) sinclude(TSRM/tsrm.m4) - +sinclude(main/hardening_patch.m4) divert(2) @@ -620,6 +620,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 @@ -1262,7 +1263,7 @@ php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \ strlcat.c mergesort.c reentrancy.c php_variables.c php_ticks.c \ network.c php_open_temporary_file.c php_logos.c \ - output.c ) + output.c hardening_patch.c ) PHP_ADD_SOURCES(main/streams, streams.c cast.c memory.c filter.c \ plain_wrapper.c userspace.c transports.c xp_socket.c mmap.c) @@ -1280,7 +1281,7 @@ zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \ zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \ zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ - zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c) + zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_canary.c ) if test -r "$abs_srcdir/Zend/zend_objects.c"; then PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c zend_mm.c \ diff -Naur php-5.0.4/ext/fbsql/php_fbsql.c hardening-patch-5.0.4-0.3.2/ext/fbsql/php_fbsql.c --- php-5.0.4/ext/fbsql/php_fbsql.c 2005-02-09 20:32:45.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/fbsql/php_fbsql.c 2005-07-03 20:50:08.000000000 +0200 @@ -1852,8 +1852,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-5.0.4/ext/mysql/php_mysql.c hardening-patch-5.0.4-0.3.2/ext/mysql/php_mysql.c --- php-5.0.4/ext/mysql/php_mysql.c 2005-02-22 15:59:30.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/mysql/php_mysql.c 2005-07-03 11:04:29.000000000 +0200 @@ -1221,6 +1221,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); @@ -1271,6 +1273,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 @@ -1281,6 +1290,13 @@ php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn)); } } + copy_query = estrdup(Z_STRVAL_PP(query)); + for (i=0; copy_query[i]; i++) if (copy_query[i] < 32) copy_query[i]='.'; + php_security_log(S_SQL, "MySQL error: %s - query: %s", mysql_error(&mysql->conn), copy_query); + efree(copy_query); + if (HG(hphp_sql_bailout_on_error)) { + zend_bailout(); + } RETURN_FALSE; } #endif diff -Naur php-5.0.4/ext/mysqli/mysqli_nonapi.c hardening-patch-5.0.4-0.3.2/ext/mysqli/mysqli_nonapi.c --- php-5.0.4/ext/mysqli/mysqli_nonapi.c 2005-02-25 00:59:49.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/mysqli/mysqli_nonapi.c 2005-07-09 08:40:55.890819992 +0200 @@ -228,6 +228,17 @@ if (mysql_real_query(mysql->mysql, query, query_len)) { char s_error[MYSQL_ERRMSG_SIZE], s_sqlstate[SQLSTATE_LENGTH+1]; unsigned int s_errno; +#if HARDENING_PATCH + char *query_copy = estrdup(query); + int i; + + for (i=0; query_copy[i]; i++) if (query_copy[i]<32) query_copy[i]='.'; + php_security_log(S_SQL, "MySQLi error: %s - query: %s", mysql->mysql->net.last_errno, query_copy); + efree(query_copy); + if (HG(hphp_sql_bailout_on_error)) { + zend_bailout(); + } +#endif MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); /* we have to save error information, cause @@ -268,6 +279,17 @@ MYSQLI_DISABLE_MQ; if (mysql_real_query(mysql->mysql, query, query_len)) { +#if HARDENING_PATCH + char *query_copy = estrdup(query); + int i; + + for (i=0; query_copy[i]; i++) if (query_copy[i]<32) query_copy[i]='.'; + php_security_log(S_SQL, "MySQLi error: %s - query: %s", mysql->mysql->net.last_errno, query_copy); + efree(query_copy); + if (HG(hphp_sql_bailout_on_error)) { + zend_bailout(); + } +#endif MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } diff -Naur php-5.0.4/ext/pgsql/pgsql.c hardening-patch-5.0.4-0.3.2/ext/pgsql/pgsql.c --- php-5.0.4/ext/pgsql/pgsql.c 2004-05-12 18:49:47.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/ext/pgsql/pgsql.c 2005-07-03 21:16:44.000000000 +0200 @@ -1076,10 +1076,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-5.0.4/ext/sqlite/sqlite.c hardening-patch-5.0.4-0.3.2/ext/sqlite/sqlite.c --- php-5.0.4/ext/sqlite/sqlite.c 2004-09-26 03:41:40.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/ext/sqlite/sqlite.c 2005-07-03 20:52:00.000000000 +0200 @@ -1473,6 +1473,19 @@ db->last_err_code = ret; if (ret != SQLITE_OK) { +#if HARDENING_PATCH + char *query_copy; + int i; + + query_copy = estrdup(sql); + for (i=0; query_copy[i]; i++) if (query_copy[i]<32) query_copy[i]='.'; + php_security_log(S_SQL, "SQLite error: %s - query: %s", errtext, query_copy); + efree(query_copy); + if (HG(hphp_sql_bailout_on_error)) { + sqlite_freemem(errtext); + zend_bailout(); + } +#endif php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); sqlite_freemem(errtext); goto terminate; diff -Naur php-5.0.4/ext/standard/array.c hardening-patch-5.0.4-0.3.2/ext/standard/array.c --- php-5.0.4/ext/standard/array.c 2005-03-12 11:12:49.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/standard/array.c 2005-06-27 00:46:39.000000000 +0200 @@ -1255,6 +1255,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-5.0.4/ext/standard/basic_functions.c hardening-patch-5.0.4-0.3.2/ext/standard/basic_functions.c --- php-5.0.4/ext/standard/basic_functions.c 2005-03-10 13:10:57.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/standard/basic_functions.c 2005-06-27 00:47:11.000000000 +0200 @@ -617,7 +617,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 @@ -3124,6 +3124,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-5.0.4/ext/standard/file.c hardening-patch-5.0.4-0.3.2/ext/standard/file.c --- php-5.0.4/ext/standard/file.c 2005-03-27 17:53:30.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/ext/standard/file.c 2005-06-26 20:59:20.000000000 +0200 @@ -2044,7 +2044,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-5.0.4/ext/standard/file.h hardening-patch-5.0.4-0.3.2/ext/standard/file.h --- php-5.0.4/ext/standard/file.h 2004-06-21 23:08:05.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/ext/standard/file.h 2005-06-26 20:59:20.000000000 +0200 @@ -60,7 +60,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); PHP_FUNCTION(fnmatch); #endif PHP_NAMED_FUNCTION(php_if_ftruncate); diff -Naur php-5.0.4/ext/standard/ftp_fopen_wrapper.c hardening-patch-5.0.4-0.3.2/ext/standard/ftp_fopen_wrapper.c --- php-5.0.4/ext/standard/ftp_fopen_wrapper.c 2005-03-21 09:42:34.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/standard/ftp_fopen_wrapper.c 2005-06-27 10:41:04.000000000 +0200 @@ -113,7 +113,7 @@ { php_stream *stream = NULL, *reuseid = NULL; php_url *resource = NULL; - int result, use_ssl, use_ssl_on_data = 0; + int result, use_ssl, use_ssl_on_data = 0, tmp_len; char *scratch; char tmp_line[512]; @@ -206,10 +206,25 @@ #endif } +#define PHP_FTP_CNTRL_CHK(val, val_len, err_msg) { \ + unsigned char *s = val, *e = s + val_len; \ + while (s < e) { \ + if (iscntrl(*s)) { \ + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, err_msg, val); \ + goto connect_errexit; \ + } \ + s++; \ + } \ +} + /* send the user name */ php_stream_write_string(stream, "USER "); if (resource->user != NULL) { - php_raw_url_decode(resource->user, strlen(resource->user)); + unsigned char *s, *e; + tmp_len = php_raw_url_decode(resource->user, strlen(resource->user)); + + PHP_FTP_CNTRL_CHK(resource->user, tmp_len, "Invalid login %s") + php_stream_write_string(stream, resource->user); } else { php_stream_write_string(stream, "anonymous"); @@ -225,7 +240,10 @@ php_stream_write_string(stream, "PASS "); if (resource->pass != NULL) { - php_raw_url_decode(resource->pass, strlen(resource->pass)); + tmp_len = php_raw_url_decode(resource->pass, strlen(resource->pass)); + + PHP_FTP_CNTRL_CHK(resource->pass, tmp_len, "Invalid password %s") + php_stream_write_string(stream, resource->pass); } else { /* if the user has configured who they are, diff -Naur php-5.0.4/ext/standard/head.c hardening-patch-5.0.4-0.3.2/ext/standard/head.c --- php-5.0.4/ext/standard/head.c 2005-01-07 22:16:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/standard/head.c 2005-07-05 18:33:34.000000000 +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 && (i"); } +#if HARDENING_PATCH + if (!sapi_module.phpinfo_as_text) { + php_printf("

PHP Version %s with Hardening-Patch %s

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

PHP Version %s

\n", PHP_VERSION); } else { php_info_print_table_row(2, "PHP Version", PHP_VERSION); - } + } +#endif php_info_print_box_end(); php_info_print_table_start(); php_info_print_table_row(2, "System", php_uname ); diff -Naur php-5.0.4/ext/standard/syslog.c hardening-patch-5.0.4-0.3.2/ext/standard/syslog.c --- php-5.0.4/ext/standard/syslog.c 2004-07-30 16:39:18.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/ext/standard/syslog.c 2005-07-06 13:19:50.000000000 +0200 @@ -42,6 +42,7 @@ */ PHP_MINIT_FUNCTION(syslog) { +#if !HARDENING_PATCH /* error levels */ REGISTER_LONG_CONSTANT("LOG_EMERG", LOG_EMERG, CONST_CS | CONST_PERSISTENT); /* system unusable */ REGISTER_LONG_CONSTANT("LOG_ALERT", LOG_ALERT, CONST_CS | CONST_PERSISTENT); /* immediate action required */ @@ -97,7 +98,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-5.0.4/ext/varfilter/config.m4 hardening-patch-5.0.4-0.3.2/ext/varfilter/config.m4 --- php-5.0.4/ext/varfilter/config.m4 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/varfilter/config.m4 2005-07-06 13:12:40.000000000 +0200 @@ -0,0 +1,11 @@ +dnl +dnl $Id: config.m4,v 1.1 2004/11/14 13:27:16 ionic Exp $ +dnl + +PHP_ARG_ENABLE(varfilter, whether to enable Hardening-Patch's variable filter, +[ --disable-varfilter Disable Hardening-Patch's variable filter], yes) + +if test "$PHP_VARFILTER" != "no"; then + AC_DEFINE(HAVE_VARFILTER, 1, [ ]) + PHP_NEW_EXTENSION(varfilter, varfilter.c, $ext_shared) +fi diff -Naur php-5.0.4/ext/varfilter/CREDITS hardening-patch-5.0.4-0.3.2/ext/varfilter/CREDITS --- php-5.0.4/ext/varfilter/CREDITS 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/varfilter/CREDITS 2005-07-06 13:12:40.000000000 +0200 @@ -0,0 +1,2 @@ +varfilter +Stefan Esser \ Kein Zeilenumbruch am Dateiende. diff -Naur php-5.0.4/ext/varfilter/php_varfilter.h hardening-patch-5.0.4-0.3.2/ext/varfilter/php_varfilter.h --- php-5.0.4/ext/varfilter/php_varfilter.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/varfilter/php_varfilter.h 2005-07-06 13:12:40.000000000 +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-5.0.4/ext/varfilter/varfilter.c hardening-patch-5.0.4-0.3.2/ext/varfilter/varfilter.c --- php-5.0.4/ext/varfilter/varfilter.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/ext/varfilter/varfilter.c 2005-07-09 08:46:50.126967912 +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-5.0.4/main/fopen_wrappers.c hardening-patch-5.0.4-0.3.2/main/fopen_wrappers.c --- php-5.0.4/main/fopen_wrappers.c 2005-03-11 07:55:22.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/fopen_wrappers.c 2005-06-26 20:59:20.000000000 +0200 @@ -163,6 +163,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-5.0.4/main/hardened_globals.h hardening-patch-5.0.4-0.3.2/main/hardened_globals.h --- php-5.0.4/main/hardened_globals.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/hardened_globals.h 2005-07-05 18:31:23.000000000 +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-5.0.4/main/hardening_patch.c hardening-patch-5.0.4-0.3.2/main/hardening_patch.c --- php-5.0.4/main/hardening_patch.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/hardening_patch.c 2005-07-03 10:39:30.000000000 +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-5.0.4/main/hardening_patch.h hardening-patch-5.0.4-0.3.2/main/hardening_patch.h --- php-5.0.4/main/hardening_patch.h 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/hardening_patch.h 2005-07-09 08:47:01.529234504 +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-5.0.4/main/hardening_patch.m4 hardening-patch-5.0.4-0.3.2/main/hardening_patch.m4 --- php-5.0.4/main/hardening_patch.m4 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/hardening_patch.m4 2005-06-26 21:42:36.000000000 +0200 @@ -0,0 +1,95 @@ +dnl +dnl $Id: hardening_patch.m4,v 1.1 2004/11/14 13:24:24 ionic Exp $ +dnl +dnl This file contains Hardening Patch for PHP specific autoconf functions. +dnl + +AC_ARG_ENABLE(hardening-patch-mm-protect, +[ --disable-hardening-patch-mm-protect Disable the Memory Manager protection.],[ + DO_HARDENING_PATCH_MM_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_MM_PROTECT=yes +]) + +AC_ARG_ENABLE(hardening-patch-ll-protect, +[ --disable-hardening-patch-ll-protect Disable the Linked List protection.],[ + DO_HARDENING_PATCH_LL_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_LL_PROTECT=yes +]) + +AC_ARG_ENABLE(hardening-patch-inc-protect, +[ --disable-hardening-patch-inc-protect Disable include/require protection.],[ + DO_HARDENING_PATCH_INC_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_INC_PROTECT=yes +]) + +AC_ARG_ENABLE(hardening-patch-fmt-protect, +[ --disable-hardening-patch-fmt-protect Disable format string protection.],[ + DO_HARDENING_PATCH_FMT_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_FMT_PROTECT=yes +]) + +AC_ARG_ENABLE(hardening-patch-hash-protect, +[ --disable-hardening-patch-hash-protect Disable HashTable destructor protection.],[ + DO_HARDENING_PATCH_HASH_PROTECT=$enableval +],[ + DO_HARDENING_PATCH_HASH_PROTECT=yes +]) + +AC_MSG_CHECKING(whether to protect the Zend Memory Manager) +AC_MSG_RESULT($DO_HARDENING_PATCH_MM_PROTECT) + +AC_MSG_CHECKING(whether to protect the Zend Linked Lists) +AC_MSG_RESULT($DO_HARDENING_PATCH_LL_PROTECT) + +AC_MSG_CHECKING(whether to protect include/require statements) +AC_MSG_RESULT($DO_HARDENING_PATCH_INC_PROTECT) + +AC_MSG_CHECKING(whether to protect PHP Format String functions) +AC_MSG_RESULT($DO_HARDENING_PATCH_FMT_PROTECT) + +AC_MSG_CHECKING(whether to protect the destructor of Zend HashTables) +AC_MSG_RESULT($DO_HARDENING_PATCH_HASH_PROTECT) + + +AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + + +if test "$DO_HARDENING_PATCH_MM_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_MM_PROTECT, 1, [Memory Manager Protection]) +else + AC_DEFINE(HARDENING_PATCH_MM_PROTECT, 0, [Memory Manager Protection]) +fi + +if test "$DO_HARDENING_PATCH_LL_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_LL_PROTECT, 1, [Linked List Protection]) +else + AC_DEFINE(HARDENING_PATCH_LL_PROTECT, 0, [Linked List Protection]) +fi + +if test "$DO_HARDENING_PATCH_INC_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_INC_PROTECT, 1, [Include/Require Protection]) +else + AC_DEFINE(HARDENING_PATCH_INC_PROTECT, 0, [Include/Require Protection]) +fi + +if test "$DO_HARDENING_PATCH_FMT_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_FMT_PROTECT, 1, [Fmt String Protection]) +else + AC_DEFINE(HARDENING_PATCH_FMT_PROTECT, 0, [Fmt String Protection]) +fi + +if test "$DO_HARDENING_PATCH_HASH_PROTECT" = "yes"; then +dnl AC_DEFINE(HARDENING_PATCH, 1, [Hardening Patch]) + AC_DEFINE(HARDENING_PATCH_HASH_PROTECT, 1, [HashTable DTOR Protection]) +else + AC_DEFINE(HARDENING_PATCH_HASH_PROTECT, 0, [HashTable DTOR Protection]) +fi + diff -Naur php-5.0.4/main/main.c hardening-patch-5.0.4-0.3.2/main/main.c --- php-5.0.4/main/main.c 2005-03-24 02:11:35.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/main.c 2005-06-26 23:26:26.000000000 +0200 @@ -92,6 +92,10 @@ #include "SAPI.h" #include "rfc1867.h" +#if HARDENING_PATCH +#include "hardened_globals.h" +#endif + /* }}} */ #ifndef ZTS @@ -116,10 +120,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)); } @@ -1313,6 +1340,10 @@ tsrm_ls = ts_resource(0); #endif +#if HARDENING_PATCH + hardened_startup(); +#endif + module_shutdown = 0; module_startup = 1; sapi_initialize_empty_request(TSRMLS_C); @@ -1326,6 +1357,12 @@ php_output_startup(); +#if HARDENING_PATCH_INC_PROTECT + zuf.is_valid_include = php_is_valid_include; +#endif +#if HARDENING_PATCH + zuf.security_log_function = php_security_log; +#endif zuf.error_function = php_error_cb; zuf.printf_function = php_printf; zuf.write_function = php_body_write_wrapper; @@ -1429,6 +1466,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); php_output_register_constants(TSRMLS_C); php_rfc1867_register_constants(TSRMLS_C); diff -Naur php-5.0.4/main/php_config.h.in hardening-patch-5.0.4-0.3.2/main/php_config.h.in --- php-5.0.4/main/php_config.h.in 2005-04-03 11:42:53.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/main/php_config.h.in 2005-06-26 22:25:22.000000000 +0200 @@ -746,6 +746,39 @@ /* hardcode for each of the cross compiler host */ #undef PHP_UNAME +/* Hardening-Patch for PHP */ +#undef HARDENING_PATCH + +/* Memory Manager Protection */ +#undef HARDENING_PATCH_MM_PROTECT + +/* Memory Manager Protection */ +#undef HARDENING_PATCH_MM_PROTECT + +/* Linked List Protection */ +#undef HARDENING_PATCH_LL_PROTECT + +/* Linked List Protection */ +#undef HARDENING_PATCH_LL_PROTECT + +/* Include/Require Protection */ +#undef HARDENING_PATCH_INC_PROTECT + +/* Include/Require Protection */ +#undef HARDENING_PATCH_INC_PROTECT + +/* Fmt String Protection */ +#undef HARDENING_PATCH_FMT_PROTECT + +/* Fmt String Protection */ +#undef HARDENING_PATCH_FMT_PROTECT + +/* HashTable DTOR Protection */ +#undef HARDENING_PATCH_HASH_PROTECT + +/* HashTable DTOR Protection */ +#undef HARDENING_PATCH_HASH_PROTECT + /* Whether you have AOLserver */ #undef HAVE_AOLSERVER @@ -1077,6 +1110,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-5.0.4/main/php.h hardening-patch-5.0.4-0.3.2/main/php.h --- php-5.0.4/main/php.h 2005-03-14 10:41:39.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/php.h 2005-06-28 14:49:57.000000000 +0200 @@ -26,7 +26,7 @@ #include #endif -#define PHP_API_VERSION 20031224 +#define PHP_API_VERSION 1020050627 #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 @@ -341,6 +349,7 @@ #define PHP_FUNCTION ZEND_FUNCTION #define PHP_METHOD ZEND_METHOD +#define PHP_STATIC_FE ZEND_STATIC_FE #define PHP_NAMED_FE ZEND_NAMED_FE #define PHP_FE ZEND_FE #define PHP_FALIAS ZEND_FALIAS @@ -446,6 +455,10 @@ #endif #endif /* !XtOffsetOf */ +#if HARDENING_PATCH +#include "hardening_patch.h" +#endif + #endif /* diff -Naur php-5.0.4/main/php_variables.c hardening-patch-5.0.4-0.3.2/main/php_variables.c --- php-5.0.4/main/php_variables.c 2005-02-17 05:46:10.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/php_variables.c 2005-06-27 01:01:54.000000000 +0200 @@ -493,7 +493,7 @@ */ static inline void php_register_server_variables(TSRMLS_D) { - zval *array_ptr=NULL; + zval *array_ptr=NULL, *vptr; /* turn off magic_quotes while importing server variables */ int magic_quotes_gpc = PG(magic_quotes_gpc); @@ -506,6 +506,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-5.0.4/main/rfc1867.c hardening-patch-5.0.4-0.3.2/main/rfc1867.c --- php-5.0.4/main/rfc1867.c 2005-02-15 01:26:35.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/rfc1867.c 2005-06-26 23:13:12.000000000 +0200 @@ -131,6 +131,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) { @@ -140,6 +141,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) @@ -849,6 +851,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); @@ -988,6 +991,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)) { @@ -1001,6 +1009,11 @@ #endif cancel_upload = UPLOAD_ERROR_B; } else if (blen > 0) { + + if (sapi_module.upload_content_filter && sapi_module.upload_content_filter(offset, buff, blen, &blen TSRMLS_CC)==FAILURE) { + cancel_upload = UPLOAD_ERROR_F; + } + wlen = fwrite(buff, 1, blen, fp); if (wlen < blen) { @@ -1024,6 +1037,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-5.0.4/main/SAPI.c hardening-patch-5.0.4-0.3.2/main/SAPI.c --- php-5.0.4/main/SAPI.c 2005-02-22 15:46:15.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/SAPI.c 2005-06-26 23:18:17.000000000 +0200 @@ -821,6 +821,30 @@ zend_hash_del(&known_post_content_types, post_entry->content_type, post_entry->content_type_len+1); } +SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC)) +{ + sapi_module.input_filter = input_filter; + return SUCCESS; +} + +SAPI_API int sapi_register_pre_upload_filter(unsigned int (*pre_upload_filter)(char *varname, char *filename TSRMLS_DC)) +{ + sapi_module.pre_upload_filter = pre_upload_filter; + return SUCCESS; +} + +SAPI_API int sapi_register_upload_content_filter(unsigned int (*upload_content_filter)(unsigned long offset, char *buffer, unsigned int buffer_len, unsigned int *new_buffer_len TSRMLS_DC)) +{ + sapi_module.upload_content_filter = upload_content_filter; + return SUCCESS; +} + +SAPI_API int sapi_register_post_upload_filter(unsigned int (*post_upload_filter)(char *tmpfilename TSRMLS_DC)) +{ + sapi_module.post_upload_filter = post_upload_filter; + return SUCCESS; +} + SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D)) { @@ -835,11 +859,6 @@ return SUCCESS; } -SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC)) -{ - sapi_module.input_filter = input_filter; - return SUCCESS; -} SAPI_API int sapi_flush(TSRMLS_D) { diff -Naur php-5.0.4/main/SAPI.h hardening-patch-5.0.4-0.3.2/main/SAPI.h --- php-5.0.4/main/SAPI.h 2004-01-08 18:33:04.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/SAPI.h 2005-06-26 23:16:27.000000000 +0200 @@ -103,9 +103,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; @@ -183,6 +184,10 @@ SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC)); SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC)); +SAPI_API int sapi_register_pre_upload_filter(unsigned int (*pre_upload_filter)(char *varname, char *filename TSRMLS_DC)); +SAPI_API int sapi_register_upload_content_filter(unsigned int (*upload_content_filter)(unsigned long offset, char *buffer, unsigned int buffer_len, unsigned int *new_buffer_len TSRMLS_DC)); +SAPI_API int sapi_register_post_upload_filter(unsigned int (*post_upload_filter)(char *tmpfilename TSRMLS_DC)); + SAPI_API int sapi_flush(TSRMLS_D); SAPI_API struct stat *sapi_get_stat(TSRMLS_D); SAPI_API char *sapi_getenv(char *name, size_t name_len TSRMLS_DC); @@ -245,6 +250,10 @@ 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; @@ -270,7 +279,11 @@ #define SAPI_DEFAULT_MIMETYPE "text/html" #define SAPI_DEFAULT_CHARSET "" +#if HARDENING_PATCH +#define SAPI_PHP_VERSION_HEADER "X-Powered-By: PHP/" PHP_VERSION " with Hardening-Patch" +#else #define SAPI_PHP_VERSION_HEADER "X-Powered-By: PHP/" PHP_VERSION +#endif #define SAPI_POST_READER_FUNC(post_reader) void post_reader(TSRMLS_D) #define SAPI_POST_HANDLER_FUNC(post_handler) void post_handler(char *content_type_dup, void *arg TSRMLS_DC) @@ -278,6 +291,10 @@ #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) + BEGIN_EXTERN_C() SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data); SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader); diff -Naur php-5.0.4/main/snprintf.c hardening-patch-5.0.4-0.3.2/main/snprintf.c --- php-5.0.4/main/snprintf.c 2004-11-16 00:14:40.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/main/snprintf.c 2005-06-26 22:07:19.000000000 +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-5.0.4/main/spprintf.c hardening-patch-5.0.4-0.3.2/main/spprintf.c --- php-5.0.4/main/spprintf.c 2004-04-16 01:04:49.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/main/spprintf.c 2005-06-26 22:03:49.000000000 +0200 @@ -630,7 +630,11 @@ case 'n': +#if HARDENING_PATCH_FMT_PROTECT + php_security_log(S_MISC, "'n' specifier within format string"); +#else *(va_arg(ap, int *)) = xbuf->len; +#endif break; /* diff -Naur php-5.0.4/pear/go-pear-list.php hardening-patch-5.0.4-0.3.2/pear/go-pear-list.php --- php-5.0.4/pear/go-pear-list.php 2005-03-18 02:59:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/pear/go-pear-list.php 2005-07-02 00:15:21.000000000 +0200 @@ -8,7 +8,7 @@ $packages = array( // required packages for the installer "PEAR" => "1.3.5", -"XML_RPC" => "1.2.2", +"XML_RPC" => "1.3.1", "Console_Getopt" => "1.2", "Archive_Tar" => "1.3.1", diff -Naur php-5.0.4/pear/packages/XML_RPC-1.2.2.tar hardening-patch-5.0.4-0.3.2/pear/packages/XML_RPC-1.2.2.tar --- php-5.0.4/pear/packages/XML_RPC-1.2.2.tar 2005-03-28 19:05:50.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/pear/packages/XML_RPC-1.2.2.tar 1970-01-01 01:00:00.000000000 +0100 @@ -1,3393 +0,0 @@ -package2.xml100666 0 0 24326 10213112551 6327 - - XML_RPC - pear.php.net - PHP implementation of the XML-RPC protocol - A PEAR-ified version of Useful Inc's XML-RPC for PHP. - -It has support for HTTP/HTTPS transport, proxies and authentication. - - Stig Bakken - ssb - stig@php.net - no - - - Daniel Convissor - danielc - danielc@php.net - yes - - 2005-03-07 - - - 1.2.2 - 1.2.0 - - - stable - stable - - PHP License - * When using a proxy, add the protocol to the Request-URI, making it an "absoluteURI" as per the HTTP 1.0 spec. Bug 3679. - - - - - - - - - - - - - - - - - - - - - PEAR - pear.php.net - 1.4.0a1 - 1.4.0a4 - - - - - 4.2.0 - 6.0.0 - - - 1.4.0a1 - - - - - - - - 1.2.1 - 1.2.0 - - - stable - stable - - 2005-03-01 - PHP License - * Add isset() check before examining the dispatch map. Bug 3658. - - - - 1.2.0 - 1.2.0 - - - stable - stable - - 2005-02-27 - PHP License - * Provide the "stable" release. -* Add package2.xml for compatibility with PEAR 1.4.0. -* For changes since 1.1.0, see the changelogs for the various RC releases. - - - - 1.2.0RC7 - 1.2.0RC7 - - - beta - beta - - 2005-02-22 - PHP License - * Add the setSendEncoding() method and $send_encoding - property to XML_RPC_Message. Request 3537. -* Allow class methods to be mapped using either syntax: - 'function' => 'hello::sayHello', - or - 'function' => array('hello', 'sayhello'), - Bug 3363. -* Use 8192 instead of 32768 for bytes in fread() - in parseResponseFile(). Bug 3340. - - - - 1.2.0RC6 - 1.2.0RC6 - - - beta - beta - - 2005-01-25 - PHP License - * Don't put the protocol in the Host field of the POST data. (danielc) - - - - 1.2.0RC5 - 1.2.0RC5 - - - beta - beta - - 2005-01-24 - PHP License - * If $port is 443 but a protocol isn't specified in $server, assume ssl:// is the protocol. - - - - 1.2.0RC4 - 1.2.0RC4 - - - beta - beta - - 2005-01-24 - PHP License - * When a connection attempt fails, have the method return 0. (danielc) -* Move the protocol/port checking/switching and the property settings from sendPayloadHTTP10() to the XML_RPC_Client constructor. (danielc) -* Add tests for setting the client properties. (danielc) -* Remove $GLOBALS['XML_RPC_twoslash'] since it's not used. (danielc) -* Bundle the tests with the package. (danielc) - - - - 1.2.0RC3 - 1.2.0RC3 - - - beta - beta - - 2005-01-19 - PHP License - * ssl uses port 443, not 445. - - - - 1.2.0RC2 - 1.2.0RC2 - - - beta - beta - - 2005-01-11 - PHP License - * Handle ssl:// in the $server string. (danielc) -* Also default to port 445 for ssl:// requests as well. (danielc) -* Enhance debugging in the server. (danielc) - - - - 1.2.0RC1 - 1.2.0RC1 - - - beta - beta - - 2004-12-30 - PHP License - * Make things work with SSL. Bug 2489. (nkukard lbsd net) -* Allow array function callbacks (Matt Kane) -* Some minor speed-ups (Matt Kane) -* Add Dump.php to the package (Christian Weiske) -* Replace all line endings with \r\n. Had only done replacements on \n. Bug 2521. (danielc) -* Silence fsockopen() errors. Bug 1714. (danielc) -* Encode empty arrays as an array. Bug 1493. (danielc) -* Eliminate undefined index notice when submitting empty arrays to XML_RPC_Encode(). Bug 1819. (danielc) -* Speed up check for enumerated arrays in XML_RPC_Encode(). (danielc) -* Prepend "XML_RPC_" to ERROR_NON_NUMERIC_FOUND, eliminating problem when eval()'ing error messages. (danielc) -* Use XML_RPC_Base::raiseError() instead of PEAR::raiseError() in XML_RPC_ee() because PEAR.php is lazy loaded. (danielc) -* Allow raiseError() to be called statically. (danielc) -* Stop double escaping of character entities. Bug 987. (danielc) - NOTICE: the following have been removed: - * XML_RPC_dh() - * $GLOBALS['XML_RPC_entities'] - * XML_RPC_entity_decode() - * XML_RPC_lookup_entity() -* Determine the XML's encoding via the encoding attribute in the XML declaration. Bug 52. (danielc) - - - - 1.1.0 - 1.1.0 - - - stable - stable - - 2004-03-15 - PHP License - * Added support for sequential arrays to XML_RPC_encode() (mroch) -* Cleaned up new XML_RPC_encode() changes a bit (mroch, pierre) -* Remove "require_once 'PEAR.php'", include only when needed to raise an error -* Replace echo and error_log() with raiseError() (mroch) -* Make all classes extend XML_RPC_Base, which will handle common functions (mroch) -* be tolerant of junk after methodResponse (Luca Mariano, mroch) -* Silent notice even in the error log (pierre) -* fix include of shared xml extension on win32 (pierre) - - - - 1.0.4 - 1.0.4 - - - stable - stable - - 2002-10-02 - PHP License - * added HTTP proxy authorization support (thanks to Arnaud Limbourg) - - - - 1.0.3 - 1.0.3 - - - stable - stable - - 2002-05-19 - PHP License - * fix bug when parsing responses with boolean types - - - - 1.0.2 - 1.0.2 - - - stable - stable - - 2002-04-16 - PHP License - * E_ALL fixes -* fix HTTP response header parsing - - - - 1.0.1 - 1.0.1 - - - stable - stable - - 2001-09-25 - PHP License - This is a PEAR-ified version of Useful Inc's 1.0.1 release. -Includes an urgent security fix identified by Dan Libby <dan@libby.com>. - - -XML_RPC-1.2.2/tests/protoport.php100666 0 0 25543 10213112550 11656 - * @copyright 2005 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License - * @version CVS: $Id: protoport.php,v 1.4 2005/01/24 17:48:47 danielc Exp $ - * @link http://pear.php.net/package/XML_RPC - * @since File available since Release 1.2 - */ - -/* - * If the package version number is found in the left hand - * portion of the if() expression below, that means this file has - * come from the PEAR installer. Therefore, let's test the - * installed version of XML_RPC which should be in the include path. - * - * If the version has not been substituted in the if() expression, - * this file has likely come from a CVS checkout or a .tar file. - * Therefore, we'll assume the tests should use the version of - * XML_RPC that has come from there as well. - */ -if ('1.2.2' != '@'.'package_version'.'@') { - /** - * Get the needed class from the PEAR installation - */ - require_once 'XML/RPC.php'; -} else { - /** - * Get the needed class from the parent directory - */ - require_once '../RPC.php'; -} - -/** - * Compare the test result to the expected result - * - * If the test fails, echo out the results. - * - * @param array $expect the array of object properties you expect - * from the test - * @param object $actual the object results from the test - * @param string $test_name the name of the test - * - * @return void - */ -function compare($expect, $actual, $test_name) { - $actual = get_object_vars($actual); - if (count(array_diff($actual, $expect))) { - echo "$test_name failed.\nExpect: "; - print_r($expect); - echo "Actual: "; - print_r($actual); - echo "\n"; - } -} - -if (php_sapi_name() != 'cli') { - echo "
\n";
-}
-
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 80,
-    'proxy' => '',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'theserver');
-compare($x, $c, 'defaults');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 80,
-    'proxy' => '',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'http://theserver');
-compare($x, $c, 'defaults with http');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 443,
-    'proxy' => '',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'https://theserver');
-compare($x, $c, 'defaults with https');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 443,
-    'proxy' => '',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'ssl://theserver');
-compare($x, $c, 'defaults with ssl');
-
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 65,
-    'proxy' => '',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'theserver', 65);
-compare($x, $c, 'port 65');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 65,
-    'proxy' => '',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'http://theserver', 65);
-compare($x, $c, 'port 65 with http');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 65,
-    'proxy' => '',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'https://theserver', 65);
-compare($x, $c, 'port 65 with https');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 65,
-    'proxy' => '',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'ssl://theserver', 65);
-compare($x, $c, 'port 65 with ssl');
-
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 80,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'theserver', 0,
-                        'theproxy');
-compare($x, $c, 'defaults proxy');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 80,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 8080,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'http://theserver', 0,
-                        'http://theproxy');
-compare($x, $c, 'defaults with http proxy');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 443,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'ssl://',
-    'proxy_port' => 443,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'https://theserver', 0,
-                        'https://theproxy');
-compare($x, $c, 'defaults with https proxy');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 443,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'ssl://',
-    'proxy_port' => 443,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'ssl://theserver', 0,
-                        'ssl://theproxy');
-compare($x, $c, 'defaults with ssl proxy');
-
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 65,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 6565,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'theserver', 65,
-                        'theproxy', 6565);
-compare($x, $c, 'port 65 proxy 6565');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 65,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'http://',
-    'proxy_port' => 6565,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'http://theserver', 65,
-                        'http://theproxy', 6565);
-compare($x, $c, 'port 65 with http proxy 6565');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 65,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'ssl://',
-    'proxy_port' => 6565,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'https://theserver', 65,
-                        'https://theproxy', 6565);
-compare($x, $c, 'port 65 with https proxy 6565');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 65,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'ssl://',
-    'proxy_port' => 6565,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'ssl://theserver', 65,
-                        'ssl://theproxy', 6565);
-compare($x, $c, 'port 65 with ssl proxy 6565');
-
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'ssl://',
-    'port' => 443,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'ssl://',
-    'proxy_port' => 443,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'theserver', 443,
-                        'theproxy', 443);
-compare($x, $c, 'port 443 no protocol and proxy port 443 no protocol');
-
-$x = array(
-    'path' => 'thepath',
-    'server' => 'theserver',
-    'protocol' => 'http://',
-    'port' => 80,
-    'proxy' => 'theproxy',
-    'proxy_protocol' => 'ssl://',
-    'proxy_port' => 6565,
-    'proxy_user' => '',
-    'proxy_pass' => '',
-    'errno' => 0,
-    'errstring' => '',
-    'debug' => 0,
-    'username' => '',
-    'password' => '',
-);
-$c = new XML_RPC_Client('thepath', 'theserver', 0,
-                        'ssl://theproxy', 6565);
-compare($x, $c, 'port 443 no protocol and proxy port 443 no protocol');
-XML_RPC-1.2.2/tests/test_Dump.php100666      0      0        3042 10213112550  11520 new XML_RPC_Value('das ist der Titel', 'string'),
-    'startDate'=>new XML_RPC_Value(mktime(0,0,0,13,11,2004), 'dateTime.iso8601'),
-    'endDate'  =>new XML_RPC_Value(mktime(0,0,0,15,11,2004), 'dateTime.iso8601'),
-    'error'    =>'string',
-    'arkey'    => new XML_RPC_Value( array(
-        new XML_RPC_Value('simple string'),
-        new XML_RPC_Value(12345, 'int')
-        ), 'array')
-    )
-    ,'struct');
-
-XML_RPC_Dump($val);
-
-echo '==============' . "\r\n";
-$val2 = new XML_RPC_Value(44353, 'int');
-XML_RPC_Dump($val2);
-
-echo '==============' . "\r\n";
-$val3 = new XML_RPC_Value('this should be a string', 'string');
-XML_RPC_Dump($val3);
-
-echo '==============' . "\r\n";
-$val4 = new XML_RPC_Value(true, 'boolean');
-XML_RPC_Dump($val4);
-XML_RPC-1.2.2/Dump.php100666      0      0       12074 10213112550   7344 
- * @version    CVS: $Id: Dump.php,v 1.7 2005/01/24 03:47:55 danielc Exp $
- * @link       http://pear.php.net/package/XML_RPC
- */
-
-
-/**
- * Pull in the XML_RPC class
- */
-require_once 'XML/RPC.php';
-
-
-/**
- * Generates the dump of the XML_RPC_Value and echoes it
- *
- * @param object $value  the XML_RPC_Value object to dump
- *
- * @return void
- */
-function XML_RPC_Dump($value)
-{
-    $dumper = new XML_RPC_Dump();
-    echo $dumper->generateDump($value);
-}
-
-
-/**
- * Class which generates a dump of a XML_RPC_Value object
- *
- * @category   Web Services
- * @package    XML_RPC
- * @author     Christian Weiske 
- * @version    Release: 1.2.2
- * @link       http://pear.php.net/package/XML_RPC
- */
-class XML_RPC_Dump
-{
-    /**
-     * The indentation array cache
-     * @var array
-     */
-    var $arIndent      = array();
-
-    /**
-     * The spaces used for indenting the XML
-     * @var string
-     */
-    var $strBaseIndent = '    ';
-
-    /**
-     * Returns the dump in XML format without printing it out
-     *
-     * @param object $value   the XML_RPC_Value object to dump
-     * @param int    $nLevel  the level of indentation
-     *
-     * @return string  the dump
-     */
-    function generateDump($value, $nLevel = 0)
-    {
-        if (!is_object($value) && get_class($value) != 'xml_rpc_value') {
-            require_once 'PEAR.php';
-            PEAR::raiseError('Tried to dump non-XML_RPC_Value variable' . "\r\n",
-                             0, PEAR_ERROR_PRINT);
-            if (is_object($value)) {
-                $strType = get_class($value);
-            } else {
-                $strType = gettype($value);
-            }
-            return $this->getIndent($nLevel) . 'NOT A XML_RPC_Value: '
-                   . $strType . "\r\n";
-        }
-
-        switch ($value->kindOf()) {
-        case 'struct':
-            $ret = $this->genStruct($value, $nLevel);
-            break;
-        case 'array':
-            $ret = $this->genArray($value, $nLevel);
-            break;
-        case 'scalar':
-            $ret = $this->genScalar($value->scalarval(), $nLevel);
-            break;
-        default:
-            require_once 'PEAR.php';
-            PEAR::raiseError('Illegal type "' . $value->kindOf()
-                             . '" in XML_RPC_Value' . "\r\n", 0,
-                             PEAR_ERROR_PRINT);
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Returns the scalar value dump
-     *
-     * @param object $value   the scalar XML_RPC_Value object to dump
-     * @param int    $nLevel  the level of indentation
-     *
-     * @return string  Dumped version of the scalar value
-     */
-    function genScalar($value, $nLevel)
-    {
-        if (gettype($value) == 'object') {
-            $strClass = ' ' . get_class($value);
-        } else {
-            $strClass = '';
-        }
-        return $this->getIndent($nLevel) . gettype($value) . $strClass
-               . ' ' . $value . "\r\n";
-    }
-
-    /**
-     * Returns the dump of a struct
-     *
-     * @param object $value   the struct XML_RPC_Value object to dump
-     * @param int    $nLevel  the level of indentation
-     *
-     * @return string  Dumped version of the scalar value
-     */
-    function genStruct($value, $nLevel)
-    {
-        $value->structreset();
-        $strOutput = $this->getIndent($nLevel) . 'struct' . "\r\n";
-        while (list($key, $keyval) = $value->structeach()) {
-            $strOutput .= $this->getIndent($nLevel + 1) . $key . "\r\n";
-            $strOutput .= $this->generateDump($keyval, $nLevel + 2);
-        }
-        return $strOutput;
-    }
-
-    /**
-     * Returns the dump of an array
-     *
-     * @param object $value   the array XML_RPC_Value object to dump
-     * @param int    $nLevel  the level of indentation
-     *
-     * @return string  Dumped version of the scalar value
-     */
-    function genArray($value, $nLevel)
-    {
-        $nSize     = $value->arraysize();
-        $strOutput = $this->getIndent($nLevel) . 'array' . "\r\n";
-        for($nA = 0; $nA < $nSize; $nA++) {
-            $strOutput .= $this->getIndent($nLevel + 1) . $nA . "\r\n";
-            $strOutput .= $this->generateDump($value->arraymem($nA),
-                                              $nLevel + 2);
-        }
-        return $strOutput;
-    }
-
-    /**
-     * Returns the indent for a specific level and caches it for faster use
-     *
-     * @param int $nLevel  the level
-     *
-     * @return string  the indented string
-     */
-    function getIndent($nLevel)
-    {
-        if (!isset($this->arIndent[$nLevel])) {
-            $this->arIndent[$nLevel] = str_repeat($this->strBaseIndent, $nLevel);
-        }
-        return $this->arIndent[$nLevel];
-    }
-}
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * c-hanging-comment-ender-p: nil
- * End:
- */
-
-?>
-XML_RPC-1.2.2/RPC.php100666      0      0      141000 10213112550   7073 
- * @author     Stig Bakken 
- * @author     Martin Jansen 
- * @author     Daniel Convissor 
- * @copyright  1999-2001 Edd Dumbill
- * @version    CVS: $Id: RPC.php,v 1.60 2005/03/07 17:45:08 danielc Exp $
- * @link       http://pear.php.net/package/XML_RPC
- */
-
-
-if (!function_exists('xml_parser_create')) {
-    // Win 32 fix. From: "Leo West" 
-    if ($WINDIR) {
-        dl('php_xml.dll');
-    } else {
-        dl('xml.so');
-    }
-}
-
-/**#@+
- * Error constants
- */
-define('XML_RPC_ERROR_INVALID_TYPE',        101);
-define('XML_RPC_ERROR_NON_NUMERIC_FOUND',   102);
-define('XML_RPC_ERROR_CONNECTION_FAILED',   103);
-define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104);
-/**#@-*/
-
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_I4']
- */
-$GLOBALS['XML_RPC_I4'] = 'i4';
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_Int']
- */
-$GLOBALS['XML_RPC_Int'] = 'int';
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_Boolean']
- */
-$GLOBALS['XML_RPC_Boolean'] = 'boolean';
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_Double']
- */
-$GLOBALS['XML_RPC_Double'] = 'double';
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_String']
- */
-$GLOBALS['XML_RPC_String'] = 'string';
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_DateTime']
- */
-$GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601';
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_Base64']
- */
-$GLOBALS['XML_RPC_Base64'] = 'base64';
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_Array']
- */
-$GLOBALS['XML_RPC_Array'] = 'array';
-
-/**
- * Data types
- * @global string $GLOBALS['XML_RPC_Struct']
- */
-$GLOBALS['XML_RPC_Struct'] = 'struct';
-
-
-/**
- * Data type meta-types
- * @global array $GLOBALS['XML_RPC_Types']
- */
-$GLOBALS['XML_RPC_Types'] = array(
-    $GLOBALS['XML_RPC_I4']       => 1,
-    $GLOBALS['XML_RPC_Int']      => 1,
-    $GLOBALS['XML_RPC_Boolean']  => 1,
-    $GLOBALS['XML_RPC_String']   => 1,
-    $GLOBALS['XML_RPC_Double']   => 1,
-    $GLOBALS['XML_RPC_DateTime'] => 1,
-    $GLOBALS['XML_RPC_Base64']   => 1,
-    $GLOBALS['XML_RPC_Array']    => 2,
-    $GLOBALS['XML_RPC_Struct']   => 3,
-);
-
-
-/**
- * Error message numbers
- * @global array $GLOBALS['XML_RPC_err']
- */
-$GLOBALS['XML_RPC_err'] = array(
-    'unknown_method'      => 1,
-    'invalid_return'      => 2,
-    'incorrect_params'    => 3,
-    'introspect_unknown'  => 4,
-    'http_error'          => 5,
-);
-
-/**
- * Error message strings
- * @global array $GLOBALS['XML_RPC_str']
- */
-$GLOBALS['XML_RPC_str'] = array(
-    'unknown_method'      => 'Unknown method',
-    'invalid_return'      => 'Invalid return payload: enable debugging to examine incoming payload',
-    'incorrect_params'    => 'Incorrect parameters passed to method',
-    'introspect_unknown'  => 'Can\'t introspect: method unknown',
-    'http_error'          => 'Didn\'t receive 200 OK from remote server.',
-);
-
-
-/**
- * Default XML encoding (ISO-8859-1, UTF-8 or US-ASCII)
- * @global string $GLOBALS['XML_RPC_defencoding']
- */
-$GLOBALS['XML_RPC_defencoding'] = 'UTF-8';
-
-/**
- * User error codes start at 800
- * @global int $GLOBALS['XML_RPC_erruser']
- */
-$GLOBALS['XML_RPC_erruser'] = 800;
-
-/**
- * XML parse error codes start at 100
- * @global int $GLOBALS['XML_RPC_errxml']
- */
-$GLOBALS['XML_RPC_errxml'] = 100;
-
-
-/**
- * Compose backslashes for escaping regexp
- * @global string $GLOBALS['XML_RPC_backslash']
- */
-$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92);
-
-
-/**
- * Stores state during parsing
- *
- * quick explanation of components:
- *   + st     = builds up a string for evaluation
- *   + ac     = accumulates values
- *   + qt     = decides if quotes are needed for evaluation
- *   + cm     = denotes struct or array (comma needed)
- *   + isf    = indicates a fault
- *   + lv     = indicates "looking for a value": implements the logic
- *               to allow values with no types to be strings
- *   + params = stores parameters in method calls
- *   + method = stores method name
- *
- * @global array $GLOBALS['XML_RPC_xh']
- */
-$GLOBALS['XML_RPC_xh'] = array();
-
-
-/**
- * Start element handler for the XML parser
- *
- * @return void
- */
-function XML_RPC_se($parser, $name, $attrs)
-{
-    global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String;
-
-    switch ($name) {
-    case 'STRUCT':
-    case 'ARRAY':
-        $XML_RPC_xh[$parser]['st'] .= 'array(';
-        $XML_RPC_xh[$parser]['cm']++;
-        // this last line turns quoting off
-        // this means if we get an empty array we'll
-        // simply get a bit of whitespace in the eval
-        $XML_RPC_xh[$parser]['qt'] = 0;
-        break;
-
-    case 'NAME':
-        $XML_RPC_xh[$parser]['st'] .= "'";
-        $XML_RPC_xh[$parser]['ac'] = '';
-        break;
-
-    case 'FAULT':
-        $XML_RPC_xh[$parser]['isf'] = 1;
-        break;
-
-    case 'PARAM':
-        $XML_RPC_xh[$parser]['st'] = '';
-        break;
-
-    case 'VALUE':
-        $XML_RPC_xh[$parser]['st'] .= 'new XML_RPC_Value(';
-        $XML_RPC_xh[$parser]['lv'] = 1;
-        $XML_RPC_xh[$parser]['vt'] = $XML_RPC_String;
-        $XML_RPC_xh[$parser]['ac'] = '';
-        $XML_RPC_xh[$parser]['qt'] = 0;
-        // look for a value: if this is still 1 by the
-        // time we reach the first data segment then the type is string
-        // by implication and we need to add in a quote
-        break;
-
-    case 'I4':
-    case 'INT':
-    case 'STRING':
-    case 'BOOLEAN':
-    case 'DOUBLE':
-    case 'DATETIME.ISO8601':
-    case 'BASE64':
-        $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator
-
-        if ($name == 'DATETIME.ISO8601' || $name == 'STRING') {
-            $XML_RPC_xh[$parser]['qt'] = 1;
-
-            if ($name == 'DATETIME.ISO8601') {
-                $XML_RPC_xh[$parser]['vt'] = $XML_RPC_DateTime;
-            }
-
-        } elseif ($name == 'BASE64') {
-            $XML_RPC_xh[$parser]['qt'] = 2;
-        } else {
-            // No quoting is required here -- but
-            // at the end of the element we must check
-            // for data format errors.
-            $XML_RPC_xh[$parser]['qt'] = 0;
-        }
-        break;
-
-    case 'MEMBER':
-        $XML_RPC_xh[$parser]['ac'] = '';
-    }
-
-    if ($name != 'VALUE') {
-        $XML_RPC_xh[$parser]['lv'] = 0;
-    }
-}
-
-/**
- * End element handler for the XML parser
- *
- * @return void
- */
-function XML_RPC_ee($parser, $name)
-{
-    global $XML_RPC_xh, $XML_RPC_Types, $XML_RPC_String;
-
-    switch ($name) {
-    case 'STRUCT':
-    case 'ARRAY':
-        if ($XML_RPC_xh[$parser]['cm']
-            && substr($XML_RPC_xh[$parser]['st'], -1) == ',')
-        {
-            $XML_RPC_xh[$parser]['st'] = substr($XML_RPC_xh[$parser]['st'], 0, -1);
-        }
-
-        $XML_RPC_xh[$parser]['st'] .= ')';
-        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
-        $XML_RPC_xh[$parser]['cm']--;
-        break;
-
-    case 'NAME':
-        $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'] . "' => ";
-        break;
-
-    case 'BOOLEAN':
-        // special case here: we translate boolean 1 or 0 into PHP
-        // constants true or false
-        if ($XML_RPC_xh[$parser]['ac'] == '1') {
-            $XML_RPC_xh[$parser]['ac'] = 'true';
-        } else {
-            $XML_RPC_xh[$parser]['ac'] = 'false';
-        }
-
-        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
-        // Drop through intentionally.
-
-    case 'I4':
-    case 'INT':
-    case 'STRING':
-    case 'DOUBLE':
-    case 'DATETIME.ISO8601':
-    case 'BASE64':
-        if ($XML_RPC_xh[$parser]['qt'] == 1) {
-            // we use double quotes rather than single so backslashification works OK
-            $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"';
-        } elseif ($XML_RPC_xh[$parser]['qt'] == 2) {
-            $XML_RPC_xh[$parser]['st'] .= "base64_decode('"
-                                        . $XML_RPC_xh[$parser]['ac'] . "')";
-        } elseif ($name == 'BOOLEAN') {
-            $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
-        } else {
-            // we have an I4, INT or a DOUBLE
-            // we must check that only 0123456789-. are characters here
-            if (!ereg("^[+-]?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
-                XML_RPC_Base::raiseError('Non-numeric value received in INT or DOUBLE',
-                                         XML_RPC_ERROR_NON_NUMERIC_FOUND);
-                $XML_RPC_xh[$parser]['st'] .= 'XML_RPC_ERROR_NON_NUMERIC_FOUND';
-            } else {
-                // it's ok, add it on
-                $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
-            }
-        }
-
-        $XML_RPC_xh[$parser]['ac'] = '';
-        $XML_RPC_xh[$parser]['qt'] = 0;
-        $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value
-        break;
-
-    case 'VALUE':
-        // deal with a string value
-        if (strlen($XML_RPC_xh[$parser]['ac']) > 0 &&
-            $XML_RPC_xh[$parser]['vt'] == $XML_RPC_String) {
-
-            $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"';
-        }
-
-        // This if () detects if no scalar was inside 
-        // and pads an empty "".
-        if ($XML_RPC_xh[$parser]['st'][strlen($XML_RPC_xh[$parser]['st'])-1] == '(') {
-            $XML_RPC_xh[$parser]['st'] .= '""';
-        }
-        $XML_RPC_xh[$parser]['st'] .= ", '" . $XML_RPC_xh[$parser]['vt'] . "')";
-        if ($XML_RPC_xh[$parser]['cm']) {
-            $XML_RPC_xh[$parser]['st'] .= ',';
-        }
-        break;
-
-    case 'MEMBER':
-        $XML_RPC_xh[$parser]['ac'] = '';
-        $XML_RPC_xh[$parser]['qt'] = 0;
-        break;
-
-    case 'DATA':
-        $XML_RPC_xh[$parser]['ac'] = '';
-        $XML_RPC_xh[$parser]['qt'] = 0;
-        break;
-
-    case 'PARAM':
-        $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['st'];
-        break;
-
-    case 'METHODNAME':
-        $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '',
-                                                      $XML_RPC_xh[$parser]['ac']);
-        break;
-
-    case 'BOOLEAN':
-        // special case here: we translate boolean 1 or 0 into PHP
-        // constants true or false
-        if ($XML_RPC_xh[$parser]['ac'] == '1') {
-            $XML_RPC_xh[$parser]['ac'] = 'true';
-        } else {
-            $XML_RPC_xh[$parser]['ac'] = 'false';
-        }
-
-        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
-    }
-
-    // if it's a valid type name, set the type
-    if (isset($XML_RPC_Types[strtolower($name)])) {
-        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
-    }
-}
-
-/**
- * Character data handler for the XML parser
- *
- * @return void
- */
-function XML_RPC_cd($parser, $data)
-{
-    global $XML_RPC_xh, $XML_RPC_backslash;
-
-    if ($XML_RPC_xh[$parser]['lv'] != 3) {
-        // "lookforvalue==3" means that we've found an entire value
-        // and should discard any further character data
-
-        if ($XML_RPC_xh[$parser]['lv'] == 1) {
-            // if we've found text and we're just in a  then
-            // turn quoting on, as this will be a string
-            $XML_RPC_xh[$parser]['qt'] = 1;
-            // and say we've found a value
-            $XML_RPC_xh[$parser]['lv'] = 2;
-        }
-
-        // replace characters that eval would
-        // do special things with
-        if (!isset($XML_RPC_xh[$parser]['ac'])) {
-            $XML_RPC_xh[$parser]['ac'] = '';
-        }
-        $XML_RPC_xh[$parser]['ac'] .= str_replace('$', '\$',
-            str_replace('"', '\"', str_replace(chr(92),
-            $XML_RPC_backslash, $data)));
-    }
-}
-
-/**
- * Base class
- *
- * This class provides common functions for all of the XML_RPC classes.
- *
- * @category   Web Services
- * @package    XML_RPC
- * @author     Edd Dumbill 
- * @author     Stig Bakken 
- * @author     Martin Jansen 
- * @copyright  1999-2001 Edd Dumbill
- * @version    Release: 1.2.2
- * @link       http://pear.php.net/package/XML_RPC
- */
-class XML_RPC_Base {
-
-    /**
-     * PEAR Error handling
-     *
-     * @return object  PEAR_Error object
-     */
-    function raiseError($msg, $code)
-    {
-        include_once 'PEAR.php';
-        if (is_object(@$this)) {
-            return PEAR::raiseError(get_class($this) . ': ' . $msg, $code);
-        } else {
-            return PEAR::raiseError('XML_RPC: ' . $msg, $code);
-        }
-    }
-
-    /**
-     * Tell whether something is a PEAR_Error object
-     *
-     * @param mixed $value  the item to check
-     *
-     * @return bool  whether $value is a PEAR_Error object or not
-     *
-     * @access public
-     */
-    function isError($value)
-    {
-        return is_a($value, 'PEAR_Error');
-    }
-}
-
-/**
- *
- *
- * @category   Web Services
- * @package    XML_RPC
- * @author     Edd Dumbill 
- * @author     Stig Bakken 
- * @author     Martin Jansen 
- * @author     Daniel Convissor 
- * @copyright  1999-2001 Edd Dumbill
- * @version    Release: 1.2.2
- * @link       http://pear.php.net/package/XML_RPC
- */
-class XML_RPC_Client extends XML_RPC_Base {
-
-    /**
-     * The path and name of the RPC server script you want the request to go to
-     * @var string
-     */
-    var $path = '';
-
-    /**
-     * The name of the remote server to connect to
-     * @var string
-     */
-    var $server = '';
-
-    /**
-     * The protocol to use in contacting the remote server
-     * @var string
-     */
-    var $protocol = 'http://';
-
-    /**
-     * The port for connecting to the remote server
-     *
-     * The default is 80 for http:// connections
-     * and 443 for https:// and ssl:// connections.
-     *
-     * @var integer
-     */
-    var $port = 80;
-
-    /**
-     * A user name for accessing the RPC server
-     * @var string
-     * @see XML_RPC_Client::setCredentials()
-     */
-    var $username = '';
-
-    /**
-     * A password for accessing the RPC server
-     * @var string
-     * @see XML_RPC_Client::setCredentials()
-     */
-    var $password = '';
-
-    /**
-     * The name of the proxy server to use, if any
-     * @var string
-     */
-    var $proxy = '';
-
-    /**
-     * The protocol to use in contacting the proxy server, if any
-     * @var string
-     */
-    var $proxy_protocol = 'http://';
-
-    /**
-     * The port for connecting to the proxy server
-     *
-     * The default is 8080 for http:// connections
-     * and 443 for https:// and ssl:// connections.
-     *
-     * @var integer
-     */
-    var $proxy_port = 8080;
-
-    /**
-     * A user name for accessing the proxy server
-     * @var string
-     */
-    var $proxy_user = '';
-
-    /**
-     * A password for accessing the proxy server
-     * @var string
-     */
-    var $proxy_pass = '';
-
-    /**
-     * The error number, if any
-     * @var integer
-     */
-    var $errno = 0;
-
-    /**
-     * The error message, if any
-     * @var string
-     */
-    var $errstring = '';
-
-    /**
-     * The current debug mode (1 = on, 0 = off)
-     * @var integer
-     */
-    var $debug = 0;
-
-
-    /**
-     * Sets the object's properties
-     *
-     * @param string  $path        the path and name of the RPC server script
-     *                              you want the request to go to
-     * @param string  $server      the URL of the remote server to connect to.
-     *                              If this parameter doesn't specify a
-     *                              protocol and $port is 443, ssl:// is
-     *                              assumed.
-     * @param integer $port        a port for connecting to the remote server.
-     *                              Defaults to 80 for http:// connections and
-     *                              443 for https:// and ssl:// connections.
-     * @param string  $proxy       the URL of the proxy server to use, if any.
-     *                              If this parameter doesn't specify a
-     *                              protocol and $port is 443, ssl:// is
-     *                              assumed.
-     * @param integer $proxy_port  a port for connecting to the remote server.
-     *                              Defaults to 8080 for http:// connections and
-     *                              443 for https:// and ssl:// connections.
-     * @param string  $proxy_user  a user name for accessing the proxy server
-     * @param string  $proxy_pass  a password for accessing the proxy server
-     *
-     * @return void
-     */
-    function XML_RPC_Client($path, $server, $port = 0,
-                            $proxy = '', $proxy_port = 0,
-                            $proxy_user = '', $proxy_pass = '')
-    {
-        $this->path       = $path;
-        $this->proxy_user = $proxy_user;
-        $this->proxy_pass = $proxy_pass;
-
-        preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match);
-        if ($match[1] == '') {
-            if ($port == 443) {
-                $this->server   = $match[2];
-                $this->protocol = 'ssl://';
-                $this->port     = 443;
-            } else {
-                $this->server = $match[2];
-                if ($port) {
-                    $this->port = $port;
-                }
-            }
-        } elseif ($match[1] == 'http://') {
-            $this->server = $match[2];
-            if ($port) {
-                $this->port = $port;
-            }
-        } else {
-            $this->server   = $match[2];
-            $this->protocol = 'ssl://';
-            if ($port) {
-                $this->port = $port;
-            } else {
-                $this->port = 443;
-            }
-        }
-
-        if ($proxy) {
-            preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match);
-            if ($match[1] == '') {
-                if ($proxy_port == 443) {
-                    $this->proxy          = $match[2];
-                    $this->proxy_protocol = 'ssl://';
-                    $this->proxy_port     = 443;
-                } else {
-                    $this->proxy = $match[2];
-                    if ($proxy_port) {
-                        $this->proxy_port = $proxy_port;
-                    }
-                }
-            } elseif ($match[1] == 'http://') {
-                $this->proxy = $match[2];
-                if ($proxy_port) {
-                    $this->proxy_port = $proxy_port;
-                }
-            } else {
-                $this->proxy          = $match[2];
-                $this->proxy_protocol = 'ssl://';
-                if ($proxy_port) {
-                    $this->proxy_port = $proxy_port;
-                } else {
-                    $this->proxy_port = 443;
-                }
-            }
-        }
-    }
-
-    /**
-     * Change the current debug mode
-     *
-     * @param int $in  where 1 = on, 0 = off
-     *
-     * @return void
-     */
-    function setDebug($in)
-    {
-        if ($in) {
-            $this->debug = 1;
-        } else {
-            $this->debug = 0;
-        }
-    }
-
-    /**
-     * Set username and password properties for connecting to the RPC server
-     *
-     * @param string $u  the user name
-     * @param string $p  the password
-     *
-     * @return void
-     *
-     * @see XML_RPC_Client::$username, XML_RPC_Client::$password
-     */
-    function setCredentials($u, $p)
-    {
-        $this->username = $u;
-        $this->password = $p;
-    }
-
-    /**
-     * Transmit the RPC request via HTTP 1.0 protocol
-     *
-     * @param object $msg       the XML_RPC_Message object
-     * @param int    $timeout   how many seconds to wait for the request
-     *
-     * @return object  an XML_RPC_Response object.  0 is returned if any
-     *                  problems happen.
-     *
-     * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(),
-     *      XML_RPC_Client::setCredentials()
-     */
-    function send($msg, $timeout = 0)
-    {
-        $msg->debug = $this->debug;
-        return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
-                                        $timeout, $this->username,
-                                        $this->password);
-    }
-
-    /**
-     * Transmit the RPC request via HTTP 1.0 protocol
-     *
-     * Requests should be sent using XML_RPC_Client send() rather than
-     * calling this method directly.
-     *
-     * @param object $msg       the XML_RPC_Message object
-     * @param string $server    the server to send the request to
-     * @param int    $port      the server port send the request to
-     * @param int    $timeout   how many seconds to wait for the request
-     *                           before giving up
-     * @param string $username  a user name for accessing the RPC server
-     * @param string $password  a password for accessing the RPC server
-     *
-     * @return object  an XML_RPC_Response object.  0 is returned if any
-     *                  problems happen.
-     *
-     * @see XML_RPC_Client::send()
-     */
-    function sendPayloadHTTP10($msg, $server, $port, $timeout = 0,
-                               $username = '', $password = '')
-    {
-        /*
-         * If we're using a proxy open a socket to the proxy server
-         * instead to the xml-rpc server
-         */
-        if ($this->proxy) {
-            if ($this->proxy_protocol == 'http://') {
-                $protocol = '';
-            } else {
-                $protocol = $this->proxy_protocol;
-            }
-            if ($timeout > 0) {
-                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
-                                 $this->errno, $this->errstr, $timeout);
-            } else {
-                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
-                                 $this->errno, $this->errstr);
-            }
-        } else {
-            if ($this->protocol == 'http://') {
-                $protocol = '';
-            } else {
-                $protocol = $this->protocol;
-            }
-            if ($timeout > 0) {
-                $fp = @fsockopen($protocol . $server, $port,
-                                 $this->errno, $this->errstr, $timeout);
-            } else {
-                $fp = @fsockopen($protocol . $server, $port,
-                                 $this->errno, $this->errstr);
-            }
-        }
-
-        /*
-         * Just raising the error without returning it is strange,
-         * but keep it here for backwards compatibility.
-         */
-        if (!$fp && $this->proxy) {
-            $this->raiseError('Connection to proxy server '
-                              . $this->proxy . ':' . $this->proxy_port
-                              . ' failed. ' . $this->errstr,
-                              XML_RPC_ERROR_CONNECTION_FAILED);
-            return 0;
-        } elseif (!$fp) {
-            $this->raiseError('Connection to RPC server '
-                              . $server . ':' . $port
-                              . ' failed. ' . $this->errstr,
-                              XML_RPC_ERROR_CONNECTION_FAILED);
-            return 0;
-        }
-
-        // Only create the payload if it was not created previously
-        if (empty($msg->payload)) {
-            $msg->createPayload();
-        }
-
-        // thanks to Grant Rauscher  for this
-        $credentials = '';
-        if ($username != '') {
-            $credentials = 'Authorization: Basic ' .
-                base64_encode($username . ':' . $password) . "\r\n";
-        }
-
-        if ($this->proxy) {
-            $op = 'POST ' . $this->protocol . $server;
-            if ($this->proxy_port) {
-                $op .= ':' . $this->port;
-            }
-        } else {
-           $op = 'POST ';
-        }
-
-        $op .= $this->path. " HTTP/1.0\r\n" .
-               "User-Agent: PEAR XML_RPC\r\n" .
-               'Host: ' . $server . "\r\n";
-        if ($this->proxy && $this->proxy_user != '') {
-            $op .= 'Proxy-Authorization: Basic ' .
-                base64_encode($this->proxy_user . ':' . $this->proxy_pass) .
-                "\r\n";
-        }
-        $op .= $credentials .
-               "Content-Type: text/xml\r\n" .
-               'Content-Length: ' . strlen($msg->payload) . "\r\n\r\n" .
-               $msg->payload;
-
-        if (!fputs($fp, $op, strlen($op))) {
-            $this->errstr = 'Write error';
-            return 0;
-        }
-        $resp = $msg->parseResponseFile($fp);
-        fclose($fp);
-        return $resp;
-    }
-}
-
-/**
- *
- *
- * @category   Web Services
- * @package    XML_RPC
- * @author     Edd Dumbill 
- * @author     Stig Bakken 
- * @author     Martin Jansen 
- * @copyright  1999-2001 Edd Dumbill
- * @version    Release: 1.2.2
- * @link       http://pear.php.net/package/XML_RPC
- */
-class XML_RPC_Response extends XML_RPC_Base
-{
-    var $xv;
-    var $fn;
-    var $fs;
-    var $hdrs;
-
-    /**
-     * @return void
-     */
-    function XML_RPC_Response($val, $fcode = 0, $fstr = '')
-    {
-        if ($fcode != 0) {
-            $this->fn = $fcode;
-            $this->fs = htmlspecialchars($fstr);
-        } else {
-            $this->xv = $val;
-        }
-    }
-
-    /**
-     * @return int  the error code
-     */
-    function faultCode()
-    {
-        if (isset($this->fn)) {
-            return $this->fn;
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * @return string  the error string
-     */
-    function faultString()
-    {
-        return $this->fs;
-    }
-
-    /**
-     * @return mixed  the value
-     */
-    function value()
-    {
-        return $this->xv;
-    }
-
-    /**
-     * @return string  the error message in XML format
-     */
-    function serialize()
-    {
-        $rs = "\n";
-        if ($this->fn) {
-            $rs .= "
-  
-    
-      
-        faultCode
-        " . $this->fn . "
-      
-      
-        faultString
-        " . $this->fs . "
-      
-    
-  
-";
-        } else {
-            $rs .= "\n\n" . $this->xv->serialize() .
-        "\n";
-        }
-        $rs .= "\n";
-        return $rs;
-    }
-}
-
-/**
- *
- *
- * @category   Web Services
- * @package    XML_RPC
- * @author     Edd Dumbill 
- * @author     Stig Bakken 
- * @author     Martin Jansen 
- * @author     Daniel Convissor 
- * @copyright  1999-2001 Edd Dumbill
- * @version    Release: 1.2.2
- * @link       http://pear.php.net/package/XML_RPC
- */
-class XML_RPC_Message extends XML_RPC_Base
-{
-    /**
-     * The current debug mode (1 = on, 0 = off)
-     * @var integer
-     */
-    var $debug = 0;
-
-    /**
-     * The encoding to be used for outgoing messages
-     *
-     * Defaults to the value of $GLOBALS['XML_RPC_defencoding']
-     *
-     * @var string
-     * @see XML_RPC_Message::setSendEncoding(),
-     *      $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header()
-     */
-    var $send_encoding = '';
-
-    /**
-     * The method presently being evaluated
-     * @var string
-     */
-    var $methodname = '';
-
-    /**
-     * @var array
-     */
-    var $params = array();
-
-    /**
-     * The XML message being generated
-     * @var string
-     */
-    var $payload = '';
-
-    /**
-     * @return void
-     */
-    function XML_RPC_Message($meth, $pars = 0)
-    {
-        $this->methodname = $meth;
-        if (is_array($pars) && sizeof($pars) > 0) {
-            for ($i = 0; $i < sizeof($pars); $i++) {
-                $this->addParam($pars[$i]);
-            }
-        }
-    }
-
-    /**
-     * Produces the XML declaration including the encoding attribute
-     *
-     * The encoding is determined by this class' $send_encoding
-     * property.  If the $send_encoding property is not set, use
-     * $GLOBALS['XML_RPC_defencoding'].
-     *
-     * @return string  the XML declaration and  element
-     *
-     * @see XML_RPC_Message::setSendEncoding(),
-     *      XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding']
-     */
-    function xml_header()
-    {
-        global $XML_RPC_defencoding;
-        if (!$this->send_encoding) {
-            $this->send_encoding = $XML_RPC_defencoding;
-        }
-        return 'send_encoding . '"?>'
-               . "\n\n";
-    }
-
-    /**
-     * @return string  the closing  tag
-     */
-    function xml_footer()
-    {
-        return "\n";
-    }
-
-    /**
-     * @return void
-     *
-     * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer()
-     */
-    function createPayload()
-    {
-        $this->payload = $this->xml_header();
-        $this->payload .= '' . $this->methodname . "\n";
-        $this->payload .= "\n";
-        for ($i = 0; $i < sizeof($this->params); $i++) {
-            $p = $this->params[$i];
-            $this->payload .= "\n" . $p->serialize() . "\n";
-        }
-        $this->payload .= "\n";
-        $this->payload .= $this->xml_footer();
-        $this->payload = ereg_replace("[\r\n]+", "\r\n", $this->payload);
-    }
-
-    /**
-     * @return string  the name of the method
-     */
-    function method($meth = '')
-    {
-        if ($meth != '') {
-            $this->methodname = $meth;
-        }
-        return $this->methodname;
-    }
-
-    /**
-     * @return string  the payload
-     */
-    function serialize()
-    {
-        $this->createPayload();
-        return $this->payload;
-    }
-
-    /**
-     * @return void
-     */
-    function addParam($par)
-    {
-        $this->params[] = $par;
-    }
-
-    /**
-     * @return void
-     */
-    function getParam($i)
-    {
-        return $this->params[$i];
-    }
-
-    /**
-     * @return int  the number of parameters
-     */
-    function getNumParams()
-    {
-        return sizeof($this->params);
-    }
-
-    /**
-     * Sets the XML declaration's encoding attribute
-     *
-     * @param string $type  the encoding type (ISO-8859-1, UTF-8 or US-ASCII)
-     *
-     * @return void
-     *
-     * @see XML_RPC_Message::$send_encoding, XML_RPC_Message::xml_header()
-     * @since Method available since Release 1.2.0
-     */
-    function setSendEncoding($type)
-    {
-        $this->send_encoding = $type;
-    }
-
-    /**
-     * Determine the XML's encoding via the encoding attribute
-     * in the XML declaration
-     *
-     * If the encoding parameter is not set or is not ISO-8859-1, UTF-8
-     * or US-ASCII, $XML_RPC_defencoding will be returned.
-     *
-     * @param string $data  the XML that will be parsed
-     *
-     * @return string  the encoding to be used
-     *
-     * @link   http://php.net/xml_parser_create
-     * @since  Method available since Release 1.2.0
-     */
-    function getEncoding($data)
-    {
-        global $XML_RPC_defencoding;
-
-        if (preg_match('/<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]/i',
-                       $data, $match))
-        {
-            $match[1] = trim(strtoupper($match[1]));
-            switch ($match[1]) {
-                case 'ISO-8859-1':
-                case 'UTF-8':
-                case 'US-ASCII':
-                    return $match[1];
-                    break;
-
-                default:
-                    return $XML_RPC_defencoding;
-            }
-        } else {
-            return $XML_RPC_defencoding;
-        }
-    }
-
-    /**
-     * @return object  a new XML_RPC_Response object
-     */
-    function parseResponseFile($fp)
-    {
-        $ipd = '';
-        while ($data = @fread($fp, 8192)) {
-            $ipd .= $data;
-        }
-        return $this->parseResponse($ipd);
-    }
-
-    /**
-     * @return object  a new XML_RPC_Response object
-     */
-    function parseResponse($data = '')
-    {
-        global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding;
-
-        $encoding = $this->getEncoding($data);
-        $parser = xml_parser_create($encoding);
-
-        $XML_RPC_xh[$parser] = array();
-
-        $XML_RPC_xh[$parser]['st'] = '';
-        $XML_RPC_xh[$parser]['cm'] = 0;
-        $XML_RPC_xh[$parser]['isf'] = 0;
-        $XML_RPC_xh[$parser]['ac'] = '';
-        $XML_RPC_xh[$parser]['qt'] = '';
-
-        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
-        xml_set_element_handler($parser, 'XML_RPC_se', 'XML_RPC_ee');
-        xml_set_character_data_handler($parser, 'XML_RPC_cd');
-
-        $hdrfnd = 0;
-        if ($this->debug) {
-            print "
---GOT---\n";
-            print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data;
-            print "\n---END---\n
"; - } - - // see if we got an HTTP 200 OK, else bomb - // but only do this if we're using the HTTP protocol. - if (ereg('^HTTP', $data) && - !ereg('^HTTP/[0-9\.]+ 200 ', $data)) { - $errstr = substr($data, 0, strpos($data, "\n") - 1); - error_log('HTTP error, got response: ' . $errstr); - $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'], - $XML_RPC_str['http_error'] . ' (' . - $errstr . ')'); - xml_parser_free($parser); - return $r; - } - // gotta get rid of headers here - - - if ((!$hdrfnd) && ($brpos = strpos($data,"\r\n\r\n"))) { - $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos); - $data = substr($data, $brpos + 4); - $hdrfnd = 1; - } - - /* - * be tolerant of junk after methodResponse - * (e.g. javascript automatically inserted by free hosts) - * thanks to Luca Mariano - */ - $data = substr($data, 0, strpos($data, "") + 17); - - if (!xml_parse($parser, $data, sizeof($data))) { - // thanks to Peter Kocks - if ((xml_get_current_line_number($parser)) == 1) { - $errstr = 'XML error at line 1, check URL'; - } else { - $errstr = sprintf('XML error: %s at line %d', - xml_error_string(xml_get_error_code($parser)), - xml_get_current_line_number($parser)); - } - error_log($errstr); - $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], - $XML_RPC_str['invalid_return']); - xml_parser_free($parser); - return $r; - } - xml_parser_free($parser); - if ($this->debug) { - print '
---EVALING---[' .
-            strlen($XML_RPC_xh[$parser]['st']) . " chars]---\n" .
-            htmlspecialchars($XML_RPC_xh[$parser]['st']) . ";\n---END---
"; - } - if (strlen($XML_RPC_xh[$parser]['st']) == 0) { - // then something odd has happened - // and it's time to generate a client side error - // indicating something odd went on - $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], - $XML_RPC_str['invalid_return']); - } else { - eval('$v=' . $XML_RPC_xh[$parser]['st'] . '; $allOK=1;'); - if ($XML_RPC_xh[$parser]['isf']) { - $f = $v->structmem('faultCode'); - $fs = $v->structmem('faultString'); - $r = new XML_RPC_Response($v, $f->scalarval(), - $fs->scalarval()); - } else { - $r = new XML_RPC_Response($v); - } - } - $r->hdrs = split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]); - return $r; - } -} - -/** - * - * - * @category Web Services - * @package XML_RPC - * @author Edd Dumbill - * @author Stig Bakken - * @author Martin Jansen - * @copyright 1999-2001 Edd Dumbill - * @version Release: 1.2.2 - * @link http://pear.php.net/package/XML_RPC - */ -class XML_RPC_Value extends XML_RPC_Base -{ - var $me = array(); - var $mytype = 0; - - /** - * @return void - */ - function XML_RPC_Value($val = -1, $type = '') - { - global $XML_RPC_Types; - $this->me = array(); - $this->mytype = 0; - if ($val != -1 || $type != '') { - if ($type == '') { - $type = 'string'; - } - if (!array_key_exists($type, $XML_RPC_Types)) { - // XXX - // need some way to report this error - } elseif ($XML_RPC_Types[$type] == 1) { - $this->addScalar($val, $type); - } elseif ($XML_RPC_Types[$type] == 2) { - $this->addArray($val); - } elseif ($XML_RPC_Types[$type] == 3) { - $this->addStruct($val); - } - } - } - - /** - * @return int returns 1 if successful or 0 if there are problems - */ - function addScalar($val, $type = 'string') - { - global $XML_RPC_Types, $XML_RPC_Boolean; - - if ($this->mytype == 1) { - $this->raiseError('Scalar can have only one value', - XML_RPC_ERROR_INVALID_TYPE); - return 0; - } - $typeof = $XML_RPC_Types[$type]; - if ($typeof != 1) { - $this->raiseError("Not a scalar type (${typeof})", - XML_RPC_ERROR_INVALID_TYPE); - return 0; - } - - if ($type == $XML_RPC_Boolean) { - if (strcasecmp($val, 'true') == 0 - || $val == 1 - || ($val == true && strcasecmp($val, 'false'))) - { - $val = 1; - } else { - $val = 0; - } - } - - if ($this->mytype == 2) { - // we're adding to an array here - $ar = $this->me['array']; - $ar[] = new XML_RPC_Value($val, $type); - $this->me['array'] = $ar; - } else { - // a scalar, so set the value and remember we're scalar - $this->me[$type] = $val; - $this->mytype = $typeof; - } - return 1; - } - - /** - * @return int returns 1 if successful or 0 if there are problems - */ - function addArray($vals) - { - global $XML_RPC_Types; - if ($this->mytype != 0) { - $this->raiseError( - 'Already initialized as a [' . $this->kindOf() . ']', - XML_RPC_ERROR_ALREADY_INITIALIZED); - return 0; - } - $this->mytype = $XML_RPC_Types['array']; - $this->me['array'] = $vals; - return 1; - } - - /** - * @return int returns 1 if successful or 0 if there are problems - */ - function addStruct($vals) - { - global $XML_RPC_Types; - if ($this->mytype != 0) { - $this->raiseError( - 'Already initialized as a [' . $this->kindOf() . ']', - XML_RPC_ERROR_ALREADY_INITIALIZED); - return 0; - } - $this->mytype = $XML_RPC_Types['struct']; - $this->me['struct'] = $vals; - return 1; - } - - /** - * @return void - */ - function dump($ar) - { - reset($ar); - while (list($key, $val) = each($ar)) { - echo "$key => $val
"; - if ($key == 'array') { - while (list($key2, $val2) = each($val)) { - echo "-- $key2 => $val2
"; - } - } - } - } - - /** - * @return string the data type of the current value - */ - function kindOf() - { - switch ($this->mytype) { - case 3: - return 'struct'; - - case 2: - return 'array'; - - case 1: - return 'scalar'; - - default: - return 'undef'; - } - } - - /** - * @return string the data in XML format - */ - function serializedata($typ, $val) - { - $rs = ''; - global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean; - if (!array_key_exists($typ, $XML_RPC_Types)) { - // XXX - // need some way to report this error - return; - } - switch ($XML_RPC_Types[$typ]) { - case 3: - // struct - $rs .= "\n"; - reset($val); - while (list($key2, $val2) = each($val)) { - $rs .= "${key2}\n"; - $rs .= $this->serializeval($val2); - $rs .= "\n"; - } - $rs .= ''; - break; - - case 2: - // array - $rs .= "\n\n"; - for ($i = 0; $i < sizeof($val); $i++) { - $rs .= $this->serializeval($val[$i]); - } - $rs .= "\n"; - break; - - case 1: - switch ($typ) { - case $XML_RPC_Base64: - $rs .= "<${typ}>" . base64_encode($val) . ""; - break; - case $XML_RPC_Boolean: - $rs .= "<${typ}>" . ($val ? '1' : '0') . ""; - break; - case $XML_RPC_String: - $rs .= "<${typ}>" . htmlspecialchars($val). ""; - break; - default: - $rs .= "<${typ}>${val}"; - } - } - return $rs; - } - - /** - * @return string the data in XML format - */ - function serialize() - { - return $this->serializeval($this); - } - - /** - * @return string the data in XML format - */ - function serializeval($o) - { - $rs = ''; - $ar = $o->me; - reset($ar); - list($typ, $val) = each($ar); - $rs .= ''; - $rs .= $this->serializedata($typ, $val); - $rs .= "\n"; - return $rs; - } - - /** - * @return mixed the contents of the element requested - */ - function structmem($m) - { - return $this->me['struct'][$m]; - } - - /** - * @return void - */ - function structreset() - { - reset($this->me['struct']); - } - - /** - * @return the key/value pair of the struct's current element - */ - function structeach() - { - return each($this->me['struct']); - } - - /** - * @return mixed the current value - */ - function getval() { - // UNSTABLE - global $XML_RPC_BOOLEAN, $XML_RPC_Base64; - - reset($this->me); - list($a, $b) = each($this->me); - - // contributed by I Sofer, 2001-03-24 - // add support for nested arrays to scalarval - // i've created a new method here, so as to - // preserve back compatibility - - if (is_array($b)) { - foreach ($b as $id => $cont) { - $b[$id] = $cont->scalarval(); - } - } - - // add support for structures directly encoding php objects - if (is_object($b)) { - $t = get_object_vars($b); - foreach ($t as $id => $cont) { - $t[$id] = $cont->scalarval(); - } - foreach ($t as $id => $cont) { - eval('$b->'.$id.' = $cont;'); - } - } - - // end contrib - return $b; - } - - /** - * @return mixed - */ - function scalarval() - { - global $XML_RPC_Boolean, $XML_RPC_Base64; - reset($this->me); - list($a, $b) = each($this->me); - return $b; - } - - /** - * @return string - */ - function scalartyp() - { - global $XML_RPC_I4, $XML_RPC_Int; - reset($this->me); - list($a, $b) = each($this->me); - if ($a == $XML_RPC_I4) { - $a = $XML_RPC_Int; - } - return $a; - } - - /** - * @return mixed the struct's current element - */ - function arraymem($m) - { - return $this->me['array'][$m]; - } - - /** - * @return int the number of elements in the array - */ - function arraysize() - { - reset($this->me); - list($a, $b) = each($this->me); - return sizeof($b); - } -} - -/** - * Return an ISO8601 encoded string - * - * While timezones ought to be supported, the XML-RPC spec says: - * - * "Don't assume a timezone. It should be specified by the server in its - * documentation what assumptions it makes about timezones." - * - * This routine always assumes localtime unless $utc is set to 1, in which - * case UTC is assumed and an adjustment for locale is made when encoding. - * - * @return string the formatted date - */ -function XML_RPC_iso8601_encode($timet, $utc = 0) { - if (!$utc) { - $t = strftime('%Y%m%dT%H:%M:%S', $timet); - } else { - if (function_exists('gmstrftime')) { - // gmstrftime doesn't exist in some versions - // of PHP - $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet); - } else { - $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z')); - } - } - return $t; -} - -/** - * Convert a datetime string into a Unix timestamp - * - * While timezones ought to be supported, the XML-RPC spec says: - * - * "Don't assume a timezone. It should be specified by the server in its - * documentation what assumptions it makes about timezones." - * - * This routine always assumes localtime unless $utc is set to 1, in which - * case UTC is assumed and an adjustment for locale is made when encoding. - * - * @return int the unix timestamp of the date submitted - */ -function XML_RPC_iso8601_decode($idate, $utc = 0) { - $t = 0; - if (ereg('([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})', $idate, $regs)) { - if ($utc) { - $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); - } else { - $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); - } - } - return $t; -} - -/** - * Takes a message in PHP XML_RPC object format and translates it into - * native PHP types - * - * @return mixed - * - * @author Dan Libby - */ -function XML_RPC_decode($XML_RPC_val) -{ - $kind = $XML_RPC_val->kindOf(); - - if ($kind == 'scalar') { - return $XML_RPC_val->scalarval(); - - } elseif ($kind == 'array') { - $size = $XML_RPC_val->arraysize(); - $arr = array(); - for ($i = 0; $i < $size; $i++) { - $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i)); - } - return $arr; - - } elseif ($kind == 'struct') { - $XML_RPC_val->structreset(); - $arr = array(); - while (list($key, $value) = $XML_RPC_val->structeach()) { - $arr[$key] = XML_RPC_decode($value); - } - return $arr; - } -} - -/** - * Takes native php types and encodes them into XML_RPC PHP object format - * - * Feature creep -- could support more types via optional type argument. - * - * @return string - * - * @author Dan Libby - */ -function XML_RPC_encode($php_val) { - global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double, $XML_RPC_String, - $XML_RPC_Array, $XML_RPC_Struct; - - $type = gettype($php_val); - $XML_RPC_val = new XML_RPC_Value; - - switch ($type) { - case 'array': - if (empty($php_val)) { - $XML_RPC_val->addArray($php_val); - break; - } - $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1)); - if (empty($tmp)) { - $arr = array(); - foreach ($php_val as $k => $v) { - $arr[$k] = XML_RPC_encode($v); - } - $XML_RPC_val->addArray($arr); - break; - } - // fall though if it's not an enumerated array - - case 'object': - $arr = array(); - foreach ($php_val as $k => $v) { - $arr[$k] = XML_RPC_encode($v); - } - $XML_RPC_val->addStruct($arr); - break; - - case 'integer': - $XML_RPC_val->addScalar($php_val, $XML_RPC_Int); - break; - - case 'double': - $XML_RPC_val->addScalar($php_val, $XML_RPC_Double); - break; - - case 'string': - case 'NULL': - $XML_RPC_val->addScalar($php_val, $XML_RPC_String); - break; - - case 'boolean': - // Add support for encoding/decoding of booleans, since they - // are supported in PHP - // by - $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean); - break; - - case 'unknown type': - default: - $XML_RPC_val = false; - } - return $XML_RPC_val; -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * c-hanging-comment-ender-p: nil - * End: - */ - -?> -XML_RPC-1.2.2/Server.php100666 0 0 36235 10213112551 7713 - * @author Stig Bakken - * @author Martin Jansen - * @copyright 1999-2001 Edd Dumbill - * @version CVS: $Id: Server.php,v 1.17 2005/03/01 17:09:49 danielc Exp $ - * @link http://pear.php.net/package/XML_RPC - */ - - -/** - * Pull in the XML_RPC class - */ -require_once 'XML/RPC.php'; - - -/** - * listMethods: either a string, or nothing - * @global array $GLOBALS['XML_RPC_Server_listMethods_sig'] - */ -$GLOBALS['XML_RPC_Server_listMethods_sig'] = array( - array($GLOBALS['XML_RPC_Array'], - $GLOBALS['XML_RPC_String'] - ), - array($GLOBALS['XML_RPC_Array']) -); - -/** - * @global string $GLOBALS['XML_RPC_Server_listMethods_doc'] - */ -$GLOBALS['XML_RPC_Server_listMethods_doc'] = 'This method lists all the' - . ' methods that the XML-RPC server knows how to dispatch'; - -/** - * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig'] - */ -$GLOBALS['XML_RPC_Server_methodSignature_sig'] = array( - array($GLOBALS['XML_RPC_Array'], - $GLOBALS['XML_RPC_String'] - ) -); - -/** - * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc'] - */ -$GLOBALS['XML_RPC_Server_methodSignature_doc'] = 'Returns an array of known' - . ' signatures (an array of arrays) for the method name passed. If' - . ' no signatures are known, returns a none-array (test for type !=' - . ' array to detect missing signature)'; - -/** - * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig'] - */ -$GLOBALS['XML_RPC_Server_methodHelp_sig'] = array( - array($GLOBALS['XML_RPC_String'], - $GLOBALS['XML_RPC_String'] - ) -); - -/** - * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc'] - */ -$GLOBALS['XML_RPC_Server_methodHelp_doc'] = 'Returns help text if defined' - . ' for the method passed, otherwise returns an empty string'; - -/** - * @global array $GLOBALS['XML_RPC_Server_dmap'] - */ -$GLOBALS['XML_RPC_Server_dmap'] = array( - 'system.listMethods' => array( - 'function' => 'XML_RPC_Server_listMethods', - 'signature' => $GLOBALS['XML_RPC_Server_listMethods_sig'], - 'docstring' => $GLOBALS['XML_RPC_Server_listMethods_doc'] - ), - 'system.methodHelp' => array( - 'function' => 'XML_RPC_Server_methodHelp', - 'signature' => $GLOBALS['XML_RPC_Server_methodHelp_sig'], - 'docstring' => $GLOBALS['XML_RPC_Server_methodHelp_doc'] - ), - 'system.methodSignature' => array( - 'function' => 'XML_RPC_Server_methodSignature', - 'signature' => $GLOBALS['XML_RPC_Server_methodSignature_sig'], - 'docstring' => $GLOBALS['XML_RPC_Server_methodSignature_doc'] - ) -); - -/** - * @global string $GLOBALS['XML_RPC_Server_debuginfo'] - */ -$GLOBALS['XML_RPC_Server_debuginfo'] = ''; - - -/** - * Lists all the methods that the XML-RPC server knows how to dispatch - * - * @return object a new XML_RPC_Response object - */ -function XML_RPC_Server_listMethods($server, $m) -{ - global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; - - $v = new XML_RPC_Value(); - $dmap = $server->dmap; - $outAr = array(); - for (reset($dmap); list($key, $val) = each($dmap); ) { - $outAr[] = new XML_RPC_Value($key, 'string'); - } - $dmap = $XML_RPC_Server_dmap; - for (reset($dmap); list($key, $val) = each($dmap); ) { - $outAr[] = new XML_RPC_Value($key, 'string'); - } - $v->addArray($outAr); - return new XML_RPC_Response($v); -} - -/** - * Returns an array of known signatures (an array of arrays) - * for the given method - * - * If no signatures are known, returns a none-array - * (test for type != array to detect missing signature) - * - * @return object a new XML_RPC_Response object - */ -function XML_RPC_Server_methodSignature($server, $m) -{ - global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; - - $methName = $m->getParam(0); - $methName = $methName->scalarval(); - if (strpos($methName, 'system.') === 0) { - $dmap = $XML_RPC_Server_dmap; - $sysCall = 1; - } else { - $dmap = $server->dmap; - $sysCall = 0; - } - // print "\n"; - if (isset($dmap[$methName])) { - if ($dmap[$methName]['signature']) { - $sigs = array(); - $thesigs = $dmap[$methName]['signature']; - for ($i = 0; $i < sizeof($thesigs); $i++) { - $cursig = array(); - $inSig = $thesigs[$i]; - for ($j = 0; $j < sizeof($inSig); $j++) { - $cursig[] = new XML_RPC_Value($inSig[$j], 'string'); - } - $sigs[] = new XML_RPC_Value($cursig, 'array'); - } - $r = new XML_RPC_Response(new XML_RPC_Value($sigs, 'array')); - } else { - $r = new XML_RPC_Response(new XML_RPC_Value('undef', 'string')); - } - } else { - $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'], - $XML_RPC_str['introspect_unknown']); - } - return $r; -} - -/** - * Returns help text if defined for the method passed, otherwise returns - * an empty string - * - * @return object a new XML_RPC_Response object - */ -function XML_RPC_Server_methodHelp($server, $m) -{ - global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; - - $methName = $m->getParam(0); - $methName = $methName->scalarval(); - if (strpos($methName, 'system.') === 0) { - $dmap = $XML_RPC_Server_dmap; - $sysCall = 1; - } else { - $dmap = $server->dmap; - $sysCall = 0; - } - // print "\n"; - if (isset($dmap[$methName])) { - if ($dmap[$methName]['docstring']) { - $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']), - 'string'); - } else { - $r = new XML_RPC_Response(new XML_RPC_Value('', 'string')); - } - } else { - $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'], - $XML_RPC_str['introspect_unknown']); - } - return $r; -} - -/** - * @return void - */ -function XML_RPC_Server_debugmsg($m) -{ - global $XML_RPC_Server_debuginfo; - $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n"; -} - - -/** - * - * - * @category Web Services - * @package XML_RPC - * @author Edd Dumbill - * @author Stig Bakken - * @author Martin Jansen - * @copyright 1999-2001 Edd Dumbill - * @version Release: 1.2.2 - * @link http://pear.php.net/package/XML_RPC - */ -class XML_RPC_Server -{ - var $dmap = array(); - var $encoding = ''; - var $debug = 0; - - /** - * @return void - */ - function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0) - { - global $HTTP_RAW_POST_DATA; - - if ($debug) { - $this->debug = 1; - } else { - $this->debug = 0; - } - - // dispMap is a despatch array of methods - // mapped to function names and signatures - // if a method - // doesn't appear in the map then an unknown - // method error is generated - $this->dmap = $dispMap; - if ($serviceNow) { - $this->service(); - } - } - - /** - * @return string the debug information if debug debug mode is on - */ - function serializeDebug() - { - global $XML_RPC_Server_debuginfo, $HTTP_RAW_POST_DATA; - - if ($this->debug) { - XML_RPC_Server_debugmsg('vvv POST DATA RECEIVED BY SERVER vvv' . "\n" - . $HTTP_RAW_POST_DATA - . "\n" . '^^^ END POST DATA ^^^'); - } - - if ($XML_RPC_Server_debuginfo != '') { - return "\n"; - } else { - return ''; - } - } - - /** - * Print out the result - * - * The encoding and content-type are determined by - * XML_RPC_Message::getEncoding() - * - * @return void - * - * @see XML_RPC_Message::getEncoding() - */ - function service() - { - $r = $this->parseRequest(); - $payload = 'encoding . '"?>' . "\n" - . $this->serializeDebug() - . $r->serialize(); - header('Content-Length: ' . strlen($payload)); - header('Content-Type: text/xml; charset=' . $this->encoding); - print $payload; - } - - /** - * @return array - */ - function verifySignature($in, $sig) - { - for ($i = 0; $i < sizeof($sig); $i++) { - // check each possible signature in turn - $cursig = $sig[$i]; - if (sizeof($cursig) == $in->getNumParams() + 1) { - $itsOK = 1; - for ($n = 0; $n < $in->getNumParams(); $n++) { - $p = $in->getParam($n); - // print "\n"; - if ($p->kindOf() == 'scalar') { - $pt = $p->scalartyp(); - } else { - $pt = $p->kindOf(); - } - // $n+1 as first type of sig is return type - if ($pt != $cursig[$n+1]) { - $itsOK = 0; - $pno = $n+1; - $wanted = $cursig[$n+1]; - $got = $pt; - break; - } - } - if ($itsOK) { - return array(1); - } - } - } - return array(0, "Wanted ${wanted}, got ${got} at param ${pno})"); - } - - /** - * @return object a new XML_RPC_Response object - */ - function parseRequest($data = '') - { - global $XML_RPC_xh, $HTTP_RAW_POST_DATA, - $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml, - $XML_RPC_defencoding, $XML_RPC_Server_dmap; - - if ($data == '') { - $data = $HTTP_RAW_POST_DATA; - } - - $this->encoding = XML_RPC_Message::getEncoding($data); - $parser = xml_parser_create($this->encoding); - - $XML_RPC_xh[$parser] = array(); - $XML_RPC_xh[$parser]['st'] = ''; - $XML_RPC_xh[$parser]['cm'] = 0; - $XML_RPC_xh[$parser]['isf'] = 0; - $XML_RPC_xh[$parser]['params'] = array(); - $XML_RPC_xh[$parser]['method'] = ''; - - $plist = ''; - - // decompose incoming XML into request structure - - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); - xml_set_element_handler($parser, 'XML_RPC_se', 'XML_RPC_ee'); - xml_set_character_data_handler($parser, 'XML_RPC_cd'); - if (!xml_parse($parser, $data, 1)) { - // return XML error as a faultCode - $r = new XML_RPC_Response(0, - $XML_RPC_errxml+xml_get_error_code($parser), - sprintf('XML error: %s at line %d', - xml_error_string(xml_get_error_code($parser)), - xml_get_current_line_number($parser))); - xml_parser_free($parser); - } else { - xml_parser_free($parser); - $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']); - // now add parameters in - for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) { - // print '\n"; - $plist .= "$i - " . $XML_RPC_xh[$parser]['params'][$i] . " \n"; - eval('$m->addParam(' . $XML_RPC_xh[$parser]['params'][$i] . ');'); - } - XML_RPC_Server_debugmsg($plist); - - // now to deal with the method - $methName = $XML_RPC_xh[$parser]['method']; - if (strpos($methName, 'system.') === 0) { - $dmap = $XML_RPC_Server_dmap; - $sysCall = 1; - } else { - $dmap = $this->dmap; - $sysCall = 0; - } - - if (isset($dmap[$methName]['function']) - && is_string($dmap[$methName]['function']) - && strpos($dmap[$methName]['function'], '::') !== false) - { - $dmap[$methName]['function'] = - explode('::', $dmap[$methName]['function']); - } - - if (isset($dmap[$methName]['function']) - && is_callable($dmap[$methName]['function'])) - { - // dispatch if exists - if (isset($dmap[$methName]['signature'])) { - $sr = $this->verifySignature($m, - $dmap[$methName]['signature'] ); - } - if ( (!isset($dmap[$methName]['signature'])) || $sr[0]) { - // if no signature or correct signature - if ($sysCall) { - $r = call_user_func($dmap[$methName]['function'], $this, $m); - } else { - $r = call_user_func($dmap[$methName]['function'], $m); - } - } else { - $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'], - $XML_RPC_str['incorrect_params'] - . ': ' . $sr[1]); - } - } else { - // else prepare error response - $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'], - $XML_RPC_str['unknown_method']); - } - } - return $r; - } - - /** - * Echos back the input packet as a string value - * - * @return void - * - * Useful for debugging. - */ - function echoInput() { - global $HTTP_RAW_POST_DATA; - - $r = new XML_RPC_Response(0); - $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string'); - print $r->serialize(); - } -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * c-hanging-comment-ender-p: nil - * End: - */ - -?> -package.xml100666 0 0 15556 10213112551 6252 - - - XML_RPC - PHP implementation of the XML-RPC protocol - A PEAR-ified version of Useful Inc's XML-RPC for PHP. - -It has support for HTTP/HTTPS transport, proxies and authentication. - - - - ssb - Stig Bakken - stig@php.net - lead - - - danielc - Daniel Convissor - danielc@php.net - lead - - - - 1.2.2 - 2005-03-07 - PHP License - stable - * When using a proxy, add the protocol to the Request-URI, making it an "absoluteURI" as per the HTTP 1.0 spec. Bug 3679. - - - - - - - - - - - - - - - - - - - - - - 1.2.1 - 2005-03-01 - stable - * Add isset() check before examining the dispatch map. Bug 3658. - - - - 1.2.0 - 2005-02-27 - stable - * Provide the "stable" release. -* Add package2.xml for compatibility with PEAR 1.4.0. -* For changes since 1.1.0, see the changelogs for the various RC releases. - - - - 1.2.0RC7 - 2005-02-22 - beta - * Add the setSendEncoding() method and $send_encoding - property to XML_RPC_Message. Request 3537. -* Allow class methods to be mapped using either syntax: - 'function' => 'hello::sayHello', - or - 'function' => array('hello', 'sayhello'), - Bug 3363. -* Use 8192 instead of 32768 for bytes in fread() - in parseResponseFile(). Bug 3340. - - - - 1.2.0RC6 - 2005-01-25 - beta - * Don't put the protocol in the Host field of the POST data. (danielc) - - - - 1.2.0RC5 - 2005-01-24 - beta - * If $port is 443 but a protocol isn't specified in $server, assume ssl:// is the protocol. - - - - 1.2.0RC4 - 2005-01-24 - beta - * When a connection attempt fails, have the method return 0. (danielc) -* Move the protocol/port checking/switching and the property settings from sendPayloadHTTP10() to the XML_RPC_Client constructor. (danielc) -* Add tests for setting the client properties. (danielc) -* Remove $GLOBALS['XML_RPC_twoslash'] since it's not used. (danielc) -* Bundle the tests with the package. (danielc) - - - - 1.2.0RC3 - 2005-01-19 - beta - * ssl uses port 443, not 445. - - - - 1.2.0RC2 - 2005-01-11 - beta - * Handle ssl:// in the $server string. (danielc) -* Also default to port 445 for ssl:// requests as well. (danielc) -* Enhance debugging in the server. (danielc) - - - - 1.2.0RC1 - 2004-12-30 - beta - * Make things work with SSL. Bug 2489. (nkukard lbsd net) -* Allow array function callbacks (Matt Kane) -* Some minor speed-ups (Matt Kane) -* Add Dump.php to the package (Christian Weiske) -* Replace all line endings with \r\n. Had only done replacements on \n. Bug 2521. (danielc) -* Silence fsockopen() errors. Bug 1714. (danielc) -* Encode empty arrays as an array. Bug 1493. (danielc) -* Eliminate undefined index notice when submitting empty arrays to XML_RPC_Encode(). Bug 1819. (danielc) -* Speed up check for enumerated arrays in XML_RPC_Encode(). (danielc) -* Prepend "XML_RPC_" to ERROR_NON_NUMERIC_FOUND, eliminating problem when eval()'ing error messages. (danielc) -* Use XML_RPC_Base::raiseError() instead of PEAR::raiseError() in XML_RPC_ee() because PEAR.php is lazy loaded. (danielc) -* Allow raiseError() to be called statically. (danielc) -* Stop double escaping of character entities. Bug 987. (danielc) - NOTICE: the following have been removed: - * XML_RPC_dh() - * $GLOBALS['XML_RPC_entities'] - * XML_RPC_entity_decode() - * XML_RPC_lookup_entity() -* Determine the XML's encoding via the encoding attribute in the XML declaration. Bug 52. (danielc) - - - - 1.1.0 - 2004-03-15 - stable - * Added support for sequential arrays to XML_RPC_encode() (mroch) -* Cleaned up new XML_RPC_encode() changes a bit (mroch, pierre) -* Remove "require_once 'PEAR.php'", include only when needed to raise an error -* Replace echo and error_log() with raiseError() (mroch) -* Make all classes extend XML_RPC_Base, which will handle common functions (mroch) -* be tolerant of junk after methodResponse (Luca Mariano, mroch) -* Silent notice even in the error log (pierre) -* fix include of shared xml extension on win32 (pierre) - - - - 1.0.4 - 2002-10-02 - stable - * added HTTP proxy authorization support (thanks to Arnaud Limbourg) - - - - 1.0.3 - 2002-05-19 - stable - * fix bug when parsing responses with boolean types - - - - 1.0.2 - 2002-04-16 - stable - * E_ALL fixes -* fix HTTP response header parsing - - - - 1.0.1 - 2001-09-25 - stable - This is a PEAR-ified version of Useful Inc's 1.0.1 release. -Includes an urgent security fix identified by Dan Libby <dan@libby.com>. - - - - - \ Kein Zeilenumbruch am Dateiende. diff -Naur php-5.0.4/pear/packages/XML_RPC-1.3.1.tar hardening-patch-5.0.4-0.3.2/pear/packages/XML_RPC-1.3.1.tar --- php-5.0.4/pear/packages/XML_RPC-1.3.1.tar 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/pear/packages/XML_RPC-1.3.1.tar 2005-06-29 16:04:30.000000000 +0200 @@ -0,0 +1,3733 @@ +package2.xml100644 1750 144 32557 10260516576 6517 + + XML_RPC + pear.php.net + PHP implementation of the XML-RPC protocol + A PEAR-ified version of Useful Inc's XML-RPC for PHP. + +It has support for HTTP/HTTPS transport, proxies and authentication. + + Stig Bakken + ssb + stig@php.net + no + + + Daniel Convissor + danielc + danielc@php.net + yes + + 2005-06-29 + + + 1.3.1 + 1.3.0 + + + stable + stable + + PHP License + * Security fix. Update highly recommended! + + + + + + + + + + + + + + + + + + + + + PEAR + pear.php.net + 1.4.0a1 + 1.4.0a12 + + + + + 4.2.0 + 6.0.0 + + + 1.4.0a1 + + + + + + + + 1.3.0RC3 + 1.3.0 + + + beta + stable + + 2005-05-10 + PHP License + * When verifying requests against function signatures, if the number of parameters don't match, provide an appropriate message. NOTE: this resolves a path disclosure vulnerability. (Refines the changes made in the last commit.) Bug 4231. +* XML_RPC_Message::getParam() now returns an XML_RPC_Response object upon error. Changed from Release 1.3.0RC2. +* Add the XML_RPC_Value::isValue() method. For testing if an item is an XML_RPC_Value object. +* If XML_RPC_Client::send() is given an incorrect $msg parameter, raise an error with the new XML_RPC_ERROR_PROGRAMMING code and return 0. +* Improve cross-platform operation by using PEAR::loadExtension() instead of dl(). +* Use <br /> instead of <br> in XML_RPC_Value::dump(). + + + + 1.3.0RC2 + 1.3.0 + + + beta + beta + + 2005-05-05 + PHP License + * If XML_RPC_Message::getParam() is given an incorrect parameter, raise an error with the new XML_RPC_ERROR_INCORRECT_PARAMS code and return FALSE. +* Handle improper requests to XML_RPC_Server::verifySignature(). Bug 4231. +* Try to allow HTTP 100 responses if followed by a 200 response. Bug 4116. +* Help Delphi users by making RPCMETHODNAME an alias for METHODNAME. Request 4205. + + + + 1.3.0RC1 + 1.3.0 + + + beta + beta + + 2005-04-07 + PHP License + * Improve timeout handling for situations where connection to server is made but no response is not received in time. Accomplished via stream_set_timeout(). Request 3963. +* Add Fault Code 6: "The requested method didn't return an XML_RPC_Response object." Request 4032. +* Add the createServerPayload() and createServerHeaders() methods and the $server_payload and $server_headers properties. Request 3121. +* As in earlier versions, if the $serviceNow parameter to XML_RPC_Server() is 0, no data will be returned, but now the new $server_payload and $server_headers properties will be set. +* Convert the parser handle to an integer before using it as an index for $XML_RPC_xh[$parser]. Reduces E_STRICT notices. Bug 3782. +* Add createHeaders() method and $headers property to XML_RPC_Client to make testing easier. + + + + 1.2.2 + 1.2.0 + + + stable + stable + + 2005-03-07 + PHP License + * When using a proxy, add the protocol to the Request-URI, making it an "absoluteURI" as per the HTTP 1.0 spec. Bug 3679. + + + + 1.2.1 + 1.2.0 + + + stable + stable + + 2005-03-01 + PHP License + * Add isset() check before examining the dispatch map. Bug 3658. + + + + 1.2.0 + 1.2.0 + + + stable + stable + + 2005-02-27 + PHP License + * Provide the "stable" release. +* Add package2.xml for compatibility with PEAR 1.4.0. +* For changes since 1.1.0, see the changelogs for the various RC releases. + + + + 1.2.0RC7 + 1.2.0RC7 + + + beta + beta + + 2005-02-22 + PHP License + * Add the setSendEncoding() method and $send_encoding + property to XML_RPC_Message. Request 3537. +* Allow class methods to be mapped using either syntax: + 'function' => 'hello::sayHello', + or + 'function' => array('hello', 'sayhello'), + Bug 3363. +* Use 8192 instead of 32768 for bytes in fread() + in parseResponseFile(). Bug 3340. + + + + 1.2.0RC6 + 1.2.0RC6 + + + beta + beta + + 2005-01-25 + PHP License + * Don't put the protocol in the Host field of the POST data. (danielc) + + + + 1.2.0RC5 + 1.2.0RC5 + + + beta + beta + + 2005-01-24 + PHP License + * If $port is 443 but a protocol isn't specified in $server, assume ssl:// is the protocol. + + + + 1.2.0RC4 + 1.2.0RC4 + + + beta + beta + + 2005-01-24 + PHP License + * When a connection attempt fails, have the method return 0. (danielc) +* Move the protocol/port checking/switching and the property settings from sendPayloadHTTP10() to the XML_RPC_Client constructor. (danielc) +* Add tests for setting the client properties. (danielc) +* Remove $GLOBALS['XML_RPC_twoslash'] since it's not used. (danielc) +* Bundle the tests with the package. (danielc) + + + + 1.2.0RC3 + 1.2.0RC3 + + + beta + beta + + 2005-01-19 + PHP License + * ssl uses port 443, not 445. + + + + 1.2.0RC2 + 1.2.0RC2 + + + beta + beta + + 2005-01-11 + PHP License + * Handle ssl:// in the $server string. (danielc) +* Also default to port 445 for ssl:// requests as well. (danielc) +* Enhance debugging in the server. (danielc) + + + + 1.2.0RC1 + 1.2.0RC1 + + + beta + beta + + 2004-12-30 + PHP License + * Make things work with SSL. Bug 2489. (nkukard lbsd net) +* Allow array function callbacks (Matt Kane) +* Some minor speed-ups (Matt Kane) +* Add Dump.php to the package (Christian Weiske) +* Replace all line endings with \r\n. Had only done replacements on \n. Bug 2521. (danielc) +* Silence fsockopen() errors. Bug 1714. (danielc) +* Encode empty arrays as an array. Bug 1493. (danielc) +* Eliminate undefined index notice when submitting empty arrays to XML_RPC_Encode(). Bug 1819. (danielc) +* Speed up check for enumerated arrays in XML_RPC_Encode(). (danielc) +* Prepend "XML_RPC_" to ERROR_NON_NUMERIC_FOUND, eliminating problem when eval()'ing error messages. (danielc) +* Use XML_RPC_Base::raiseError() instead of PEAR::raiseError() in XML_RPC_ee() because PEAR.php is lazy loaded. (danielc) +* Allow raiseError() to be called statically. (danielc) +* Stop double escaping of character entities. Bug 987. (danielc) + NOTICE: the following have been removed: + * XML_RPC_dh() + * $GLOBALS['XML_RPC_entities'] + * XML_RPC_entity_decode() + * XML_RPC_lookup_entity() +* Determine the XML's encoding via the encoding attribute in the XML declaration. Bug 52. (danielc) + + + + 1.1.0 + 1.1.0 + + + stable + stable + + 2004-03-15 + PHP License + * Added support for sequential arrays to XML_RPC_encode() (mroch) +* Cleaned up new XML_RPC_encode() changes a bit (mroch, pierre) +* Remove "require_once 'PEAR.php'", include only when needed to raise an error +* Replace echo and error_log() with raiseError() (mroch) +* Make all classes extend XML_RPC_Base, which will handle common functions (mroch) +* be tolerant of junk after methodResponse (Luca Mariano, mroch) +* Silent notice even in the error log (pierre) +* fix include of shared xml extension on win32 (pierre) + + + + 1.0.4 + 1.0.4 + + + stable + stable + + 2002-10-02 + PHP License + * added HTTP proxy authorization support (thanks to Arnaud Limbourg) + + + + 1.0.3 + 1.0.3 + + + stable + stable + + 2002-05-19 + PHP License + * fix bug when parsing responses with boolean types + + + + 1.0.2 + 1.0.2 + + + stable + stable + + 2002-04-16 + PHP License + * E_ALL fixes +* fix HTTP response header parsing + + + + 1.0.1 + 1.0.1 + + + stable + stable + + 2001-09-25 + PHP License + This is a PEAR-ified version of Useful Inc's 1.0.1 release. +Includes an urgent security fix identified by Dan Libby <dan@libby.com>. + + + +XML_RPC-1.3.1/tests/protoport.php100644 1750 144 25543 10260516576 12042 + * @copyright 2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License + * @version CVS: $Id: protoport.php,v 1.4 2005/01/24 17:48:47 danielc Exp $ + * @link http://pear.php.net/package/XML_RPC + * @since File available since Release 1.2 + */ + +/* + * If the package version number is found in the left hand + * portion of the if() expression below, that means this file has + * come from the PEAR installer. Therefore, let's test the + * installed version of XML_RPC which should be in the include path. + * + * If the version has not been substituted in the if() expression, + * this file has likely come from a CVS checkout or a .tar file. + * Therefore, we'll assume the tests should use the version of + * XML_RPC that has come from there as well. + */ +if ('1.3.1' != '@'.'package_version'.'@') { + /** + * Get the needed class from the PEAR installation + */ + require_once 'XML/RPC.php'; +} else { + /** + * Get the needed class from the parent directory + */ + require_once '../RPC.php'; +} + +/** + * Compare the test result to the expected result + * + * If the test fails, echo out the results. + * + * @param array $expect the array of object properties you expect + * from the test + * @param object $actual the object results from the test + * @param string $test_name the name of the test + * + * @return void + */ +function compare($expect, $actual, $test_name) { + $actual = get_object_vars($actual); + if (count(array_diff($actual, $expect))) { + echo "$test_name failed.\nExpect: "; + print_r($expect); + echo "Actual: "; + print_r($actual); + echo "\n"; + } +} + +if (php_sapi_name() != 'cli') { + echo "
\n";
+}
+
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 80,
+    'proxy' => '',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'theserver');
+compare($x, $c, 'defaults');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 80,
+    'proxy' => '',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'http://theserver');
+compare($x, $c, 'defaults with http');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 443,
+    'proxy' => '',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'https://theserver');
+compare($x, $c, 'defaults with https');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 443,
+    'proxy' => '',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'ssl://theserver');
+compare($x, $c, 'defaults with ssl');
+
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 65,
+    'proxy' => '',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'theserver', 65);
+compare($x, $c, 'port 65');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 65,
+    'proxy' => '',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'http://theserver', 65);
+compare($x, $c, 'port 65 with http');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 65,
+    'proxy' => '',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'https://theserver', 65);
+compare($x, $c, 'port 65 with https');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 65,
+    'proxy' => '',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'ssl://theserver', 65);
+compare($x, $c, 'port 65 with ssl');
+
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 80,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'theserver', 0,
+                        'theproxy');
+compare($x, $c, 'defaults proxy');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 80,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 8080,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'http://theserver', 0,
+                        'http://theproxy');
+compare($x, $c, 'defaults with http proxy');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 443,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'ssl://',
+    'proxy_port' => 443,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'https://theserver', 0,
+                        'https://theproxy');
+compare($x, $c, 'defaults with https proxy');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 443,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'ssl://',
+    'proxy_port' => 443,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'ssl://theserver', 0,
+                        'ssl://theproxy');
+compare($x, $c, 'defaults with ssl proxy');
+
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 65,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 6565,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'theserver', 65,
+                        'theproxy', 6565);
+compare($x, $c, 'port 65 proxy 6565');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 65,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'http://',
+    'proxy_port' => 6565,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'http://theserver', 65,
+                        'http://theproxy', 6565);
+compare($x, $c, 'port 65 with http proxy 6565');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 65,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'ssl://',
+    'proxy_port' => 6565,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'https://theserver', 65,
+                        'https://theproxy', 6565);
+compare($x, $c, 'port 65 with https proxy 6565');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 65,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'ssl://',
+    'proxy_port' => 6565,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'ssl://theserver', 65,
+                        'ssl://theproxy', 6565);
+compare($x, $c, 'port 65 with ssl proxy 6565');
+
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'ssl://',
+    'port' => 443,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'ssl://',
+    'proxy_port' => 443,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'theserver', 443,
+                        'theproxy', 443);
+compare($x, $c, 'port 443 no protocol and proxy port 443 no protocol');
+
+$x = array(
+    'path' => 'thepath',
+    'server' => 'theserver',
+    'protocol' => 'http://',
+    'port' => 80,
+    'proxy' => 'theproxy',
+    'proxy_protocol' => 'ssl://',
+    'proxy_port' => 6565,
+    'proxy_user' => '',
+    'proxy_pass' => '',
+    'errno' => 0,
+    'errstring' => '',
+    'debug' => 0,
+    'username' => '',
+    'password' => '',
+);
+$c = new XML_RPC_Client('thepath', 'theserver', 0,
+                        'ssl://theproxy', 6565);
+compare($x, $c, 'port 443 no protocol and proxy port 443 no protocol');
+XML_RPC-1.3.1/tests/test_Dump.php100644   1750    144        3042 10260516576  11704 new XML_RPC_Value('das ist der Titel', 'string'),
+    'startDate'=>new XML_RPC_Value(mktime(0,0,0,13,11,2004), 'dateTime.iso8601'),
+    'endDate'  =>new XML_RPC_Value(mktime(0,0,0,15,11,2004), 'dateTime.iso8601'),
+    'error'    =>'string',
+    'arkey'    => new XML_RPC_Value( array(
+        new XML_RPC_Value('simple string'),
+        new XML_RPC_Value(12345, 'int')
+        ), 'array')
+    )
+    ,'struct');
+
+XML_RPC_Dump($val);
+
+echo '==============' . "\r\n";
+$val2 = new XML_RPC_Value(44353, 'int');
+XML_RPC_Dump($val2);
+
+echo '==============' . "\r\n";
+$val3 = new XML_RPC_Value('this should be a string', 'string');
+XML_RPC_Dump($val3);
+
+echo '==============' . "\r\n";
+$val4 = new XML_RPC_Value(true, 'boolean');
+XML_RPC_Dump($val4);
+XML_RPC-1.3.1/Dump.php100644   1750    144       12074 10260516576   7530 
+ * @version    CVS: $Id: Dump.php,v 1.7 2005/01/24 03:47:55 danielc Exp $
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+
+
+/**
+ * Pull in the XML_RPC class
+ */
+require_once 'XML/RPC.php';
+
+
+/**
+ * Generates the dump of the XML_RPC_Value and echoes it
+ *
+ * @param object $value  the XML_RPC_Value object to dump
+ *
+ * @return void
+ */
+function XML_RPC_Dump($value)
+{
+    $dumper = new XML_RPC_Dump();
+    echo $dumper->generateDump($value);
+}
+
+
+/**
+ * Class which generates a dump of a XML_RPC_Value object
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Christian Weiske 
+ * @version    Release: 1.3.1
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Dump
+{
+    /**
+     * The indentation array cache
+     * @var array
+     */
+    var $arIndent      = array();
+
+    /**
+     * The spaces used for indenting the XML
+     * @var string
+     */
+    var $strBaseIndent = '    ';
+
+    /**
+     * Returns the dump in XML format without printing it out
+     *
+     * @param object $value   the XML_RPC_Value object to dump
+     * @param int    $nLevel  the level of indentation
+     *
+     * @return string  the dump
+     */
+    function generateDump($value, $nLevel = 0)
+    {
+        if (!is_object($value) && get_class($value) != 'xml_rpc_value') {
+            require_once 'PEAR.php';
+            PEAR::raiseError('Tried to dump non-XML_RPC_Value variable' . "\r\n",
+                             0, PEAR_ERROR_PRINT);
+            if (is_object($value)) {
+                $strType = get_class($value);
+            } else {
+                $strType = gettype($value);
+            }
+            return $this->getIndent($nLevel) . 'NOT A XML_RPC_Value: '
+                   . $strType . "\r\n";
+        }
+
+        switch ($value->kindOf()) {
+        case 'struct':
+            $ret = $this->genStruct($value, $nLevel);
+            break;
+        case 'array':
+            $ret = $this->genArray($value, $nLevel);
+            break;
+        case 'scalar':
+            $ret = $this->genScalar($value->scalarval(), $nLevel);
+            break;
+        default:
+            require_once 'PEAR.php';
+            PEAR::raiseError('Illegal type "' . $value->kindOf()
+                             . '" in XML_RPC_Value' . "\r\n", 0,
+                             PEAR_ERROR_PRINT);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Returns the scalar value dump
+     *
+     * @param object $value   the scalar XML_RPC_Value object to dump
+     * @param int    $nLevel  the level of indentation
+     *
+     * @return string  Dumped version of the scalar value
+     */
+    function genScalar($value, $nLevel)
+    {
+        if (gettype($value) == 'object') {
+            $strClass = ' ' . get_class($value);
+        } else {
+            $strClass = '';
+        }
+        return $this->getIndent($nLevel) . gettype($value) . $strClass
+               . ' ' . $value . "\r\n";
+    }
+
+    /**
+     * Returns the dump of a struct
+     *
+     * @param object $value   the struct XML_RPC_Value object to dump
+     * @param int    $nLevel  the level of indentation
+     *
+     * @return string  Dumped version of the scalar value
+     */
+    function genStruct($value, $nLevel)
+    {
+        $value->structreset();
+        $strOutput = $this->getIndent($nLevel) . 'struct' . "\r\n";
+        while (list($key, $keyval) = $value->structeach()) {
+            $strOutput .= $this->getIndent($nLevel + 1) . $key . "\r\n";
+            $strOutput .= $this->generateDump($keyval, $nLevel + 2);
+        }
+        return $strOutput;
+    }
+
+    /**
+     * Returns the dump of an array
+     *
+     * @param object $value   the array XML_RPC_Value object to dump
+     * @param int    $nLevel  the level of indentation
+     *
+     * @return string  Dumped version of the scalar value
+     */
+    function genArray($value, $nLevel)
+    {
+        $nSize     = $value->arraysize();
+        $strOutput = $this->getIndent($nLevel) . 'array' . "\r\n";
+        for($nA = 0; $nA < $nSize; $nA++) {
+            $strOutput .= $this->getIndent($nLevel + 1) . $nA . "\r\n";
+            $strOutput .= $this->generateDump($value->arraymem($nA),
+                                              $nLevel + 2);
+        }
+        return $strOutput;
+    }
+
+    /**
+     * Returns the indent for a specific level and caches it for faster use
+     *
+     * @param int $nLevel  the level
+     *
+     * @return string  the indented string
+     */
+    function getIndent($nLevel)
+    {
+        if (!isset($this->arIndent[$nLevel])) {
+            $this->arIndent[$nLevel] = str_repeat($this->strBaseIndent, $nLevel);
+        }
+        return $this->arIndent[$nLevel];
+    }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * c-hanging-comment-ender-p: nil
+ * End:
+ */
+
+?>
+XML_RPC-1.3.1/RPC.php100644   1750    144      150065 10260516576   7272 
+ * @author     Stig Bakken 
+ * @author     Martin Jansen 
+ * @author     Daniel Convissor 
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    CVS: $Id: RPC.php,v 1.74 2005/05/09 20:51:54 danielc Exp $
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+
+
+if (!function_exists('xml_parser_create')) {
+    PEAR::loadExtension('xml');
+}
+
+/**#@+
+ * Error constants
+ */
+/**
+ * Parameter values don't match parameter types
+ */
+define('XML_RPC_ERROR_INVALID_TYPE', 101);
+/**
+ * Parameter declared to be numeric but the values are not
+ */
+define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102);
+/**
+ * Communication error
+ */
+define('XML_RPC_ERROR_CONNECTION_FAILED', 103);
+/**
+ * The array or struct has already been started
+ */
+define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104);
+/**
+ * Incorrect parameters submitted
+ */
+define('XML_RPC_ERROR_INCORRECT_PARAMS', 105);
+/**
+ * Programming error by developer
+ */
+define('XML_RPC_ERROR_PROGRAMMING', 106);
+/**#@-*/
+
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_I4']
+ */
+$GLOBALS['XML_RPC_I4'] = 'i4';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Int']
+ */
+$GLOBALS['XML_RPC_Int'] = 'int';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Boolean']
+ */
+$GLOBALS['XML_RPC_Boolean'] = 'boolean';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Double']
+ */
+$GLOBALS['XML_RPC_Double'] = 'double';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_String']
+ */
+$GLOBALS['XML_RPC_String'] = 'string';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_DateTime']
+ */
+$GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Base64']
+ */
+$GLOBALS['XML_RPC_Base64'] = 'base64';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Array']
+ */
+$GLOBALS['XML_RPC_Array'] = 'array';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Struct']
+ */
+$GLOBALS['XML_RPC_Struct'] = 'struct';
+
+
+/**
+ * Data type meta-types
+ * @global array $GLOBALS['XML_RPC_Types']
+ */
+$GLOBALS['XML_RPC_Types'] = array(
+    $GLOBALS['XML_RPC_I4']       => 1,
+    $GLOBALS['XML_RPC_Int']      => 1,
+    $GLOBALS['XML_RPC_Boolean']  => 1,
+    $GLOBALS['XML_RPC_String']   => 1,
+    $GLOBALS['XML_RPC_Double']   => 1,
+    $GLOBALS['XML_RPC_DateTime'] => 1,
+    $GLOBALS['XML_RPC_Base64']   => 1,
+    $GLOBALS['XML_RPC_Array']    => 2,
+    $GLOBALS['XML_RPC_Struct']   => 3,
+);
+
+
+/**
+ * Error message numbers
+ * @global array $GLOBALS['XML_RPC_err']
+ */
+$GLOBALS['XML_RPC_err'] = array(
+    'unknown_method'      => 1,
+    'invalid_return'      => 2,
+    'incorrect_params'    => 3,
+    'introspect_unknown'  => 4,
+    'http_error'          => 5,
+    'not_response_object' => 6,
+);
+
+/**
+ * Error message strings
+ * @global array $GLOBALS['XML_RPC_str']
+ */
+$GLOBALS['XML_RPC_str'] = array(
+    'unknown_method'      => 'Unknown method',
+    'invalid_return'      => 'Invalid return payload: enable debugging to examine incoming payload',
+    'incorrect_params'    => 'Incorrect parameters passed to method',
+    'introspect_unknown'  => 'Can\'t introspect: method unknown',
+    'http_error'          => 'Didn\'t receive 200 OK from remote server.',
+    'not_response_object' => 'The requested method didn\'t return an XML_RPC_Response object.',
+);
+
+
+/**
+ * Default XML encoding (ISO-8859-1, UTF-8 or US-ASCII)
+ * @global string $GLOBALS['XML_RPC_defencoding']
+ */
+$GLOBALS['XML_RPC_defencoding'] = 'UTF-8';
+
+/**
+ * User error codes start at 800
+ * @global int $GLOBALS['XML_RPC_erruser']
+ */
+$GLOBALS['XML_RPC_erruser'] = 800;
+
+/**
+ * XML parse error codes start at 100
+ * @global int $GLOBALS['XML_RPC_errxml']
+ */
+$GLOBALS['XML_RPC_errxml'] = 100;
+
+
+/**
+ * Compose backslashes for escaping regexp
+ * @global string $GLOBALS['XML_RPC_backslash']
+ */
+$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92);
+
+
+/**
+ * Stores state during parsing
+ *
+ * quick explanation of components:
+ *   + st     = builds up a string for evaluation
+ *   + ac     = accumulates values
+ *   + qt     = decides if quotes are needed for evaluation
+ *   + cm     = denotes struct or array (comma needed)
+ *   + isf    = indicates a fault
+ *   + lv     = indicates "looking for a value": implements the logic
+ *               to allow values with no types to be strings
+ *   + params = stores parameters in method calls
+ *   + method = stores method name
+ *
+ * @global array $GLOBALS['XML_RPC_xh']
+ */
+$GLOBALS['XML_RPC_xh'] = array();
+
+
+/**
+ * Start element handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_se($parser_resource, $name, $attrs)
+{
+    global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String;
+    $parser = (int) $parser_resource;
+
+    switch ($name) {
+    case 'STRUCT':
+    case 'ARRAY':
+        $XML_RPC_xh[$parser]['st'] .= 'array(';
+        $XML_RPC_xh[$parser]['cm']++;
+        // this last line turns quoting off
+        // this means if we get an empty array we'll
+        // simply get a bit of whitespace in the eval
+        $XML_RPC_xh[$parser]['qt'] = 0;
+        break;
+
+    case 'NAME':
+        $XML_RPC_xh[$parser]['st'] .= '"';
+        $XML_RPC_xh[$parser]['ac'] = '';
+        break;
+
+    case 'FAULT':
+        $XML_RPC_xh[$parser]['isf'] = 1;
+        break;
+
+    case 'PARAM':
+        $XML_RPC_xh[$parser]['st'] = '';
+        break;
+
+    case 'VALUE':
+        $XML_RPC_xh[$parser]['st'] .= 'new XML_RPC_Value(';
+        $XML_RPC_xh[$parser]['lv'] = 1;
+        $XML_RPC_xh[$parser]['vt'] = $XML_RPC_String;
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = 0;
+        // look for a value: if this is still 1 by the
+        // time we reach the first data segment then the type is string
+        // by implication and we need to add in a quote
+        break;
+
+    case 'I4':
+    case 'INT':
+    case 'STRING':
+    case 'BOOLEAN':
+    case 'DOUBLE':
+    case 'DATETIME.ISO8601':
+    case 'BASE64':
+        $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator
+
+        if ($name == 'DATETIME.ISO8601' || $name == 'STRING') {
+            $XML_RPC_xh[$parser]['qt'] = 1;
+
+            if ($name == 'DATETIME.ISO8601') {
+                $XML_RPC_xh[$parser]['vt'] = $XML_RPC_DateTime;
+            }
+
+        } elseif ($name == 'BASE64') {
+            $XML_RPC_xh[$parser]['qt'] = 2;
+        } else {
+            // No quoting is required here -- but
+            // at the end of the element we must check
+            // for data format errors.
+            $XML_RPC_xh[$parser]['qt'] = 0;
+        }
+        break;
+
+    case 'MEMBER':
+        $XML_RPC_xh[$parser]['ac'] = '';
+    }
+
+    if ($name != 'VALUE') {
+        $XML_RPC_xh[$parser]['lv'] = 0;
+    }
+}
+
+/**
+ * End element handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_ee($parser_resource, $name)
+{
+    global $XML_RPC_xh, $XML_RPC_Types, $XML_RPC_String;
+    $parser = (int) $parser_resource;
+
+    switch ($name) {
+    case 'STRUCT':
+    case 'ARRAY':
+        if ($XML_RPC_xh[$parser]['cm']
+            && substr($XML_RPC_xh[$parser]['st'], -1) == ',')
+        {
+            $XML_RPC_xh[$parser]['st'] = substr($XML_RPC_xh[$parser]['st'], 0, -1);
+        }
+
+        $XML_RPC_xh[$parser]['st'] .= ')';
+        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+        $XML_RPC_xh[$parser]['cm']--;
+        break;
+
+    case 'NAME':
+        $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'] . '" => ';
+        break;
+
+    case 'BOOLEAN':
+        // special case here: we translate boolean 1 or 0 into PHP
+        // constants true or false
+        if ($XML_RPC_xh[$parser]['ac'] == '1') {
+            $XML_RPC_xh[$parser]['ac'] = 'true';
+        } else {
+            $XML_RPC_xh[$parser]['ac'] = 'false';
+        }
+
+        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+        // Drop through intentionally.
+
+    case 'I4':
+    case 'INT':
+    case 'STRING':
+    case 'DOUBLE':
+    case 'DATETIME.ISO8601':
+    case 'BASE64':
+        if ($XML_RPC_xh[$parser]['qt'] == 1) {
+            // we use double quotes rather than single so backslashification works OK
+            $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"';
+        } elseif ($XML_RPC_xh[$parser]['qt'] == 2) {
+            $XML_RPC_xh[$parser]['st'] .= 'base64_decode("'
+                                        . $XML_RPC_xh[$parser]['ac'] . '")';
+        } elseif ($name == 'BOOLEAN') {
+            $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
+        } else {
+            // we have an I4, INT or a DOUBLE
+            // we must check that only 0123456789-. are characters here
+            if (!ereg("^[+-]?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
+                XML_RPC_Base::raiseError('Non-numeric value received in INT or DOUBLE',
+                                         XML_RPC_ERROR_NON_NUMERIC_FOUND);
+                $XML_RPC_xh[$parser]['st'] .= 'XML_RPC_ERROR_NON_NUMERIC_FOUND';
+            } else {
+                // it's ok, add it on
+                $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
+            }
+        }
+
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = 0;
+        $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value
+        break;
+
+    case 'VALUE':
+        // deal with a string value
+        if (strlen($XML_RPC_xh[$parser]['ac']) > 0 &&
+            $XML_RPC_xh[$parser]['vt'] == $XML_RPC_String) {
+
+            $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"';
+        }
+
+        // This if () detects if no scalar was inside 
+        // and pads an empty "".
+        if ($XML_RPC_xh[$parser]['st'][strlen($XML_RPC_xh[$parser]['st'])-1] == '(') {
+            $XML_RPC_xh[$parser]['st'] .= '""';
+        }
+        $XML_RPC_xh[$parser]['st'] .= ", '" . $XML_RPC_xh[$parser]['vt'] . "')";
+        if ($XML_RPC_xh[$parser]['cm']) {
+            $XML_RPC_xh[$parser]['st'] .= ',';
+        }
+        break;
+
+    case 'MEMBER':
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = 0;
+        break;
+
+    case 'DATA':
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = 0;
+        break;
+
+    case 'PARAM':
+        $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['st'];
+        break;
+
+    case 'METHODNAME':
+    case 'RPCMETHODNAME':
+        $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '',
+                                                      $XML_RPC_xh[$parser]['ac']);
+        break;
+    }
+
+    // if it's a valid type name, set the type
+    if (isset($XML_RPC_Types[strtolower($name)])) {
+        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+    }
+}
+
+/**
+ * Character data handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_cd($parser_resource, $data)
+{
+    global $XML_RPC_xh, $XML_RPC_backslash;
+    $parser = (int) $parser_resource;
+
+    if ($XML_RPC_xh[$parser]['lv'] != 3) {
+        // "lookforvalue==3" means that we've found an entire value
+        // and should discard any further character data
+
+        if ($XML_RPC_xh[$parser]['lv'] == 1) {
+            // if we've found text and we're just in a  then
+            // turn quoting on, as this will be a string
+            $XML_RPC_xh[$parser]['qt'] = 1;
+            // and say we've found a value
+            $XML_RPC_xh[$parser]['lv'] = 2;
+        }
+
+        // replace characters that eval would
+        // do special things with
+        if (!isset($XML_RPC_xh[$parser]['ac'])) {
+            $XML_RPC_xh[$parser]['ac'] = '';
+        }
+        $XML_RPC_xh[$parser]['ac'] .= str_replace('$', '\$',
+            str_replace('"', '\"', str_replace(chr(92),
+            $XML_RPC_backslash, $data)));
+    }
+}
+
+/**
+ * The common methods and properties for all of the XML_RPC classes
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill 
+ * @author     Stig Bakken 
+ * @author     Martin Jansen 
+ * @author     Daniel Convissor 
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.3.1
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Base {
+
+    /**
+     * PEAR Error handling
+     *
+     * @return object  PEAR_Error object
+     */
+    function raiseError($msg, $code)
+    {
+        include_once 'PEAR.php';
+        if (is_object(@$this)) {
+            return PEAR::raiseError(get_class($this) . ': ' . $msg, $code);
+        } else {
+            return PEAR::raiseError('XML_RPC: ' . $msg, $code);
+        }
+    }
+
+    /**
+     * Tell whether something is a PEAR_Error object
+     *
+     * @param mixed $value  the item to check
+     *
+     * @return bool  whether $value is a PEAR_Error object or not
+     *
+     * @access public
+     */
+    function isError($value)
+    {
+        return is_a($value, 'PEAR_Error');
+    }
+}
+
+/**
+ * The methods and properties for submitting XML RPC requests
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill 
+ * @author     Stig Bakken 
+ * @author     Martin Jansen 
+ * @author     Daniel Convissor 
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.3.1
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Client extends XML_RPC_Base {
+
+    /**
+     * The path and name of the RPC server script you want the request to go to
+     * @var string
+     */
+    var $path = '';
+
+    /**
+     * The name of the remote server to connect to
+     * @var string
+     */
+    var $server = '';
+
+    /**
+     * The protocol to use in contacting the remote server
+     * @var string
+     */
+    var $protocol = 'http://';
+
+    /**
+     * The port for connecting to the remote server
+     *
+     * The default is 80 for http:// connections
+     * and 443 for https:// and ssl:// connections.
+     *
+     * @var integer
+     */
+    var $port = 80;
+
+    /**
+     * A user name for accessing the RPC server
+     * @var string
+     * @see XML_RPC_Client::setCredentials()
+     */
+    var $username = '';
+
+    /**
+     * A password for accessing the RPC server
+     * @var string
+     * @see XML_RPC_Client::setCredentials()
+     */
+    var $password = '';
+
+    /**
+     * The name of the proxy server to use, if any
+     * @var string
+     */
+    var $proxy = '';
+
+    /**
+     * The protocol to use in contacting the proxy server, if any
+     * @var string
+     */
+    var $proxy_protocol = 'http://';
+
+    /**
+     * The port for connecting to the proxy server
+     *
+     * The default is 8080 for http:// connections
+     * and 443 for https:// and ssl:// connections.
+     *
+     * @var integer
+     */
+    var $proxy_port = 8080;
+
+    /**
+     * A user name for accessing the proxy server
+     * @var string
+     */
+    var $proxy_user = '';
+
+    /**
+     * A password for accessing the proxy server
+     * @var string
+     */
+    var $proxy_pass = '';
+
+    /**
+     * The error number, if any
+     * @var integer
+     */
+    var $errno = 0;
+
+    /**
+     * The error message, if any
+     * @var string
+     */
+    var $errstring = '';
+
+    /**
+     * The current debug mode (1 = on, 0 = off)
+     * @var integer
+     */
+    var $debug = 0;
+
+    /**
+     * The HTTP headers for the current request.
+     * @var string
+     */
+    var $headers = '';
+
+
+    /**
+     * Sets the object's properties
+     *
+     * @param string  $path        the path and name of the RPC server script
+     *                              you want the request to go to
+     * @param string  $server      the URL of the remote server to connect to.
+     *                              If this parameter doesn't specify a
+     *                              protocol and $port is 443, ssl:// is
+     *                              assumed.
+     * @param integer $port        a port for connecting to the remote server.
+     *                              Defaults to 80 for http:// connections and
+     *                              443 for https:// and ssl:// connections.
+     * @param string  $proxy       the URL of the proxy server to use, if any.
+     *                              If this parameter doesn't specify a
+     *                              protocol and $port is 443, ssl:// is
+     *                              assumed.
+     * @param integer $proxy_port  a port for connecting to the remote server.
+     *                              Defaults to 8080 for http:// connections and
+     *                              443 for https:// and ssl:// connections.
+     * @param string  $proxy_user  a user name for accessing the proxy server
+     * @param string  $proxy_pass  a password for accessing the proxy server
+     *
+     * @return void
+     */
+    function XML_RPC_Client($path, $server, $port = 0,
+                            $proxy = '', $proxy_port = 0,
+                            $proxy_user = '', $proxy_pass = '')
+    {
+        $this->path       = $path;
+        $this->proxy_user = $proxy_user;
+        $this->proxy_pass = $proxy_pass;
+
+        preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match);
+        if ($match[1] == '') {
+            if ($port == 443) {
+                $this->server   = $match[2];
+                $this->protocol = 'ssl://';
+                $this->port     = 443;
+            } else {
+                $this->server = $match[2];
+                if ($port) {
+                    $this->port = $port;
+                }
+            }
+        } elseif ($match[1] == 'http://') {
+            $this->server = $match[2];
+            if ($port) {
+                $this->port = $port;
+            }
+        } else {
+            $this->server   = $match[2];
+            $this->protocol = 'ssl://';
+            if ($port) {
+                $this->port = $port;
+            } else {
+                $this->port = 443;
+            }
+        }
+
+        if ($proxy) {
+            preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match);
+            if ($match[1] == '') {
+                if ($proxy_port == 443) {
+                    $this->proxy          = $match[2];
+                    $this->proxy_protocol = 'ssl://';
+                    $this->proxy_port     = 443;
+                } else {
+                    $this->proxy = $match[2];
+                    if ($proxy_port) {
+                        $this->proxy_port = $proxy_port;
+                    }
+                }
+            } elseif ($match[1] == 'http://') {
+                $this->proxy = $match[2];
+                if ($proxy_port) {
+                    $this->proxy_port = $proxy_port;
+                }
+            } else {
+                $this->proxy          = $match[2];
+                $this->proxy_protocol = 'ssl://';
+                if ($proxy_port) {
+                    $this->proxy_port = $proxy_port;
+                } else {
+                    $this->proxy_port = 443;
+                }
+            }
+        }
+    }
+
+    /**
+     * Change the current debug mode
+     *
+     * @param int $in  where 1 = on, 0 = off
+     *
+     * @return void
+     */
+    function setDebug($in)
+    {
+        if ($in) {
+            $this->debug = 1;
+        } else {
+            $this->debug = 0;
+        }
+    }
+
+    /**
+     * Set username and password properties for connecting to the RPC server
+     *
+     * @param string $u  the user name
+     * @param string $p  the password
+     *
+     * @return void
+     *
+     * @see XML_RPC_Client::$username, XML_RPC_Client::$password
+     */
+    function setCredentials($u, $p)
+    {
+        $this->username = $u;
+        $this->password = $p;
+    }
+
+    /**
+     * Transmit the RPC request via HTTP 1.0 protocol
+     *
+     * @param object $msg       the XML_RPC_Message object
+     * @param int    $timeout   how many seconds to wait for the request
+     *
+     * @return object  an XML_RPC_Response object.  0 is returned if any
+     *                  problems happen.
+     *
+     * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(),
+     *      XML_RPC_Client::setCredentials()
+     */
+    function send($msg, $timeout = 0)
+    {
+        if (strtolower(get_class($msg)) != 'xml_rpc_message') {
+            $this->errstr = 'send()\'s $msg parameter must be an'
+                          . ' XML_RPC_Message object.';
+            $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING);
+            return 0;
+        }
+        $msg->debug = $this->debug;
+        return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
+                                        $timeout, $this->username,
+                                        $this->password);
+    }
+
+    /**
+     * Transmit the RPC request via HTTP 1.0 protocol
+     *
+     * Requests should be sent using XML_RPC_Client send() rather than
+     * calling this method directly.
+     *
+     * @param object $msg       the XML_RPC_Message object
+     * @param string $server    the server to send the request to
+     * @param int    $port      the server port send the request to
+     * @param int    $timeout   how many seconds to wait for the request
+     *                           before giving up
+     * @param string $username  a user name for accessing the RPC server
+     * @param string $password  a password for accessing the RPC server
+     *
+     * @return object  an XML_RPC_Response object.  0 is returned if any
+     *                  problems happen.
+     *
+     * @access protected
+     * @see XML_RPC_Client::send()
+     */
+    function sendPayloadHTTP10($msg, $server, $port, $timeout = 0,
+                               $username = '', $password = '')
+    {
+        /*
+         * If we're using a proxy open a socket to the proxy server
+         * instead to the xml-rpc server
+         */
+        if ($this->proxy) {
+            if ($this->proxy_protocol == 'http://') {
+                $protocol = '';
+            } else {
+                $protocol = $this->proxy_protocol;
+            }
+            if ($timeout > 0) {
+                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
+                                 $this->errno, $this->errstr, $timeout);
+            } else {
+                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
+                                 $this->errno, $this->errstr);
+            }
+        } else {
+            if ($this->protocol == 'http://') {
+                $protocol = '';
+            } else {
+                $protocol = $this->protocol;
+            }
+            if ($timeout > 0) {
+                $fp = @fsockopen($protocol . $server, $port,
+                                 $this->errno, $this->errstr, $timeout);
+            } else {
+                $fp = @fsockopen($protocol . $server, $port,
+                                 $this->errno, $this->errstr);
+            }
+        }
+
+        /*
+         * Just raising the error without returning it is strange,
+         * but keep it here for backwards compatibility.
+         */
+        if (!$fp && $this->proxy) {
+            $this->raiseError('Connection to proxy server '
+                              . $this->proxy . ':' . $this->proxy_port
+                              . ' failed. ' . $this->errstr,
+                              XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
+        } elseif (!$fp) {
+            $this->raiseError('Connection to RPC server '
+                              . $server . ':' . $port
+                              . ' failed. ' . $this->errstr,
+                              XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
+        }
+
+        if ($timeout) {
+            stream_set_timeout($fp, $timeout);
+        }
+
+        // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly
+        if ($username != $this->username) {
+            $this->setCredentials($username, $password);
+        }
+
+        // Only create the payload if it was not created previously
+        if (empty($msg->payload)) {
+            $msg->createPayload();
+        }
+        $this->createHeaders($msg);
+
+        $op  = $this->headers . "\r\n\r\n";
+        $op .= $msg->payload;
+
+        if (!fputs($fp, $op, strlen($op))) {
+            $this->errstr = 'Write error';
+            return 0;
+        }
+        $resp = $msg->parseResponseFile($fp);
+
+        $meta = stream_get_meta_data($fp);
+        if ($meta['timed_out']) {
+            fclose($fp);
+            $this->errstr = 'RPC server did not send response before timeout.';
+            $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
+        }
+
+        fclose($fp);
+        return $resp;
+    }
+
+    /**
+     * Determines the HTTP headers and puts it in the $headers property
+     *
+     * @param object $msg       the XML_RPC_Message object
+     *
+     * @return boolean  TRUE if okay, FALSE if the message payload isn't set.
+     *
+     * @access protected
+     */
+    function createHeaders($msg)
+    {
+        if (empty($msg->payload)) {
+            return false;
+        }
+        if ($this->proxy) {
+            $this->headers = 'POST ' . $this->protocol . $this->server;
+            if ($this->proxy_port) {
+                $this->headers .= ':' . $this->port;
+            }
+        } else {
+           $this->headers = 'POST ';
+        }
+        $this->headers .= $this->path. " HTTP/1.0\r\n";
+        
+        $this->headers .= "User-Agent: PEAR XML_RPC\r\n";
+        $this->headers .= 'Host: ' . $this->server . "\r\n";
+
+        if ($this->proxy && $this->proxy_user) {
+            $this->headers .= 'Proxy-Authorization: Basic '
+                     . base64_encode("$this->proxy_user:$this->proxy_pass")
+                     . "\r\n";
+        }
+
+        // thanks to Grant Rauscher  for this
+        if ($this->username) {
+            $this->headers .= 'Authorization: Basic '
+                     . base64_encode("$this->username:$this->password")
+                     . "\r\n";
+        }
+
+        $this->headers .= "Content-Type: text/xml\r\n";
+        $this->headers .= 'Content-Length: ' . strlen($msg->payload);
+        return true;
+    }
+}
+
+/**
+ * The methods and properties for interpreting responses to XML RPC requests
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill 
+ * @author     Stig Bakken 
+ * @author     Martin Jansen 
+ * @author     Daniel Convissor 
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.3.1
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Response extends XML_RPC_Base
+{
+    var $xv;
+    var $fn;
+    var $fs;
+    var $hdrs;
+
+    /**
+     * @return void
+     */
+    function XML_RPC_Response($val, $fcode = 0, $fstr = '')
+    {
+        if ($fcode != 0) {
+            $this->fn = $fcode;
+            $this->fs = htmlspecialchars($fstr);
+        } else {
+            $this->xv = $val;
+        }
+    }
+
+    /**
+     * @return int  the error code
+     */
+    function faultCode()
+    {
+        if (isset($this->fn)) {
+            return $this->fn;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * @return string  the error string
+     */
+    function faultString()
+    {
+        return $this->fs;
+    }
+
+    /**
+     * @return mixed  the value
+     */
+    function value()
+    {
+        return $this->xv;
+    }
+
+    /**
+     * @return string  the error message in XML format
+     */
+    function serialize()
+    {
+        $rs = "\n";
+        if ($this->fn) {
+            $rs .= "
+  
+    
+      
+        faultCode
+        " . $this->fn . "
+      
+      
+        faultString
+        " . $this->fs . "
+      
+    
+  
+";
+        } else {
+            $rs .= "\n\n" . $this->xv->serialize() .
+        "\n";
+        }
+        $rs .= "\n";
+        return $rs;
+    }
+}
+
+/**
+ * The methods and properties for composing XML RPC messages
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill 
+ * @author     Stig Bakken 
+ * @author     Martin Jansen 
+ * @author     Daniel Convissor 
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.3.1
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Message extends XML_RPC_Base
+{
+    /**
+     * The current debug mode (1 = on, 0 = off)
+     * @var integer
+     */
+    var $debug = 0;
+
+    /**
+     * The encoding to be used for outgoing messages
+     *
+     * Defaults to the value of $GLOBALS['XML_RPC_defencoding']
+     *
+     * @var string
+     * @see XML_RPC_Message::setSendEncoding(),
+     *      $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header()
+     */
+    var $send_encoding = '';
+
+    /**
+     * The method presently being evaluated
+     * @var string
+     */
+    var $methodname = '';
+
+    /**
+     * @var array
+     */
+    var $params = array();
+
+    /**
+     * The XML message being generated
+     * @var string
+     */
+    var $payload = '';
+
+    /**
+     * @return void
+     */
+    function XML_RPC_Message($meth, $pars = 0)
+    {
+        $this->methodname = $meth;
+        if (is_array($pars) && sizeof($pars) > 0) {
+            for ($i = 0; $i < sizeof($pars); $i++) {
+                $this->addParam($pars[$i]);
+            }
+        }
+    }
+
+    /**
+     * Produces the XML declaration including the encoding attribute
+     *
+     * The encoding is determined by this class' $send_encoding
+     * property.  If the $send_encoding property is not set, use
+     * $GLOBALS['XML_RPC_defencoding'].
+     *
+     * @return string  the XML declaration and  element
+     *
+     * @see XML_RPC_Message::setSendEncoding(),
+     *      XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding']
+     */
+    function xml_header()
+    {
+        global $XML_RPC_defencoding;
+        if (!$this->send_encoding) {
+            $this->send_encoding = $XML_RPC_defencoding;
+        }
+        return 'send_encoding . '"?>'
+               . "\n\n";
+    }
+
+    /**
+     * @return string  the closing  tag
+     */
+    function xml_footer()
+    {
+        return "\n";
+    }
+
+    /**
+     * @return void
+     *
+     * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer()
+     */
+    function createPayload()
+    {
+        $this->payload = $this->xml_header();
+        $this->payload .= '' . $this->methodname . "\n";
+        $this->payload .= "\n";
+        for ($i = 0; $i < sizeof($this->params); $i++) {
+            $p = $this->params[$i];
+            $this->payload .= "\n" . $p->serialize() . "\n";
+        }
+        $this->payload .= "\n";
+        $this->payload .= $this->xml_footer();
+        $this->payload = ereg_replace("[\r\n]+", "\r\n", $this->payload);
+    }
+
+    /**
+     * @return string  the name of the method
+     */
+    function method($meth = '')
+    {
+        if ($meth != '') {
+            $this->methodname = $meth;
+        }
+        return $this->methodname;
+    }
+
+    /**
+     * @return string  the payload
+     */
+    function serialize()
+    {
+        $this->createPayload();
+        return $this->payload;
+    }
+
+    /**
+     * @return void
+     */
+    function addParam($par)
+    {
+        $this->params[] = $par;
+    }
+
+    /**
+     * Obtains an XML_RPC_Value object for the given parameter
+     *
+     * @param int $i  the index number of the parameter to obtain
+     *
+     * @return object  the XML_RPC_Value object.
+     *                  If the parameter doesn't exist, an XML_RPC_Response object.
+     *
+     * @since Returns XML_RPC_Response object on error since Release 1.3.0
+     */
+    function getParam($i)
+    {
+        global $XML_RPC_err, $XML_RPC_str;
+
+        if (isset($this->params[$i])) {
+            return $this->params[$i];
+        } else {
+            $this->raiseError('The submitted request did not contain this parameter',
+                              XML_RPC_ERROR_INCORRECT_PARAMS);
+            return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
+                                        $XML_RPC_str['incorrect_params']);
+        }
+    }
+
+    /**
+     * @return int  the number of parameters
+     */
+    function getNumParams()
+    {
+        return sizeof($this->params);
+    }
+
+    /**
+     * Sets the XML declaration's encoding attribute
+     *
+     * @param string $type  the encoding type (ISO-8859-1, UTF-8 or US-ASCII)
+     *
+     * @return void
+     *
+     * @see XML_RPC_Message::$send_encoding, XML_RPC_Message::xml_header()
+     * @since Method available since Release 1.2.0
+     */
+    function setSendEncoding($type)
+    {
+        $this->send_encoding = $type;
+    }
+
+    /**
+     * Determine the XML's encoding via the encoding attribute
+     * in the XML declaration
+     *
+     * If the encoding parameter is not set or is not ISO-8859-1, UTF-8
+     * or US-ASCII, $XML_RPC_defencoding will be returned.
+     *
+     * @param string $data  the XML that will be parsed
+     *
+     * @return string  the encoding to be used
+     *
+     * @link   http://php.net/xml_parser_create
+     * @since  Method available since Release 1.2.0
+     */
+    function getEncoding($data)
+    {
+        global $XML_RPC_defencoding;
+
+        if (preg_match('/<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]/i',
+                       $data, $match))
+        {
+            $match[1] = trim(strtoupper($match[1]));
+            switch ($match[1]) {
+                case 'ISO-8859-1':
+                case 'UTF-8':
+                case 'US-ASCII':
+                    return $match[1];
+                    break;
+
+                default:
+                    return $XML_RPC_defencoding;
+            }
+        } else {
+            return $XML_RPC_defencoding;
+        }
+    }
+
+    /**
+     * @return object  a new XML_RPC_Response object
+     */
+    function parseResponseFile($fp)
+    {
+        $ipd = '';
+        while ($data = @fread($fp, 8192)) {
+            $ipd .= $data;
+        }
+        return $this->parseResponse($ipd);
+    }
+
+    /**
+     * @return object  a new XML_RPC_Response object
+     */
+    function parseResponse($data = '')
+    {
+        global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding;
+
+        $encoding = $this->getEncoding($data);
+        $parser_resource = xml_parser_create($encoding);
+        $parser = (int) $parser_resource;
+
+        $XML_RPC_xh[$parser] = array();
+
+        $XML_RPC_xh[$parser]['st'] = '';
+        $XML_RPC_xh[$parser]['cm'] = 0;
+        $XML_RPC_xh[$parser]['isf'] = 0;
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = '';
+
+        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
+        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
+        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
+
+        $hdrfnd = 0;
+        if ($this->debug) {
+            print "
---GOT---\n";
+            print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data;
+            print "\n---END---\n
"; + } + + // See if response is a 200 or a 100 then a 200, else raise error. + // But only do this if we're using the HTTP protocol. + if (ereg('^HTTP', $data) && + !ereg('^HTTP/[0-9\.]+ 200 ', $data) && + !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Za-z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data)) + { + $errstr = substr($data, 0, strpos($data, "\n") - 1); + error_log('HTTP error, got response: ' . $errstr); + $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'], + $XML_RPC_str['http_error'] . ' (' . + $errstr . ')'); + xml_parser_free($parser_resource); + return $r; + } + // gotta get rid of headers here + + + if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) { + $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos); + $data = substr($data, $brpos + 4); + $hdrfnd = 1; + } + + /* + * be tolerant of junk after methodResponse + * (e.g. javascript automatically inserted by free hosts) + * thanks to Luca Mariano + */ + $data = substr($data, 0, strpos($data, "") + 17); + + if (!xml_parse($parser_resource, $data, sizeof($data))) { + // thanks to Peter Kocks + if (xml_get_current_line_number($parser_resource) == 1) { + $errstr = 'XML error at line 1, check URL'; + } else { + $errstr = sprintf('XML error: %s at line %d', + xml_error_string(xml_get_error_code($parser_resource)), + xml_get_current_line_number($parser_resource)); + } + error_log($errstr); + $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], + $XML_RPC_str['invalid_return']); + xml_parser_free($parser_resource); + return $r; + } + xml_parser_free($parser_resource); + if ($this->debug) { + print '
---EVALING---[' .
+            strlen($XML_RPC_xh[$parser]['st']) . " chars]---\n" .
+            htmlspecialchars($XML_RPC_xh[$parser]['st']) . ";\n---END---
"; + } + if (strlen($XML_RPC_xh[$parser]['st']) == 0) { + // then something odd has happened + // and it's time to generate a client side error + // indicating something odd went on + $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], + $XML_RPC_str['invalid_return']); + } else { + eval('$v=' . $XML_RPC_xh[$parser]['st'] . '; $allOK=1;'); + if ($XML_RPC_xh[$parser]['isf']) { + $f = $v->structmem('faultCode'); + $fs = $v->structmem('faultString'); + $r = new XML_RPC_Response($v, $f->scalarval(), + $fs->scalarval()); + } else { + $r = new XML_RPC_Response($v); + } + } + $r->hdrs = split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]); + return $r; + } +} + +/** + * The methods and properties that represent data in XML RPC format + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill + * @author Stig Bakken + * @author Martin Jansen + * @author Daniel Convissor + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version Release: 1.3.1 + * @link http://pear.php.net/package/XML_RPC + */ +class XML_RPC_Value extends XML_RPC_Base +{ + var $me = array(); + var $mytype = 0; + + /** + * @return void + */ + function XML_RPC_Value($val = -1, $type = '') + { + global $XML_RPC_Types; + $this->me = array(); + $this->mytype = 0; + if ($val != -1 || $type != '') { + if ($type == '') { + $type = 'string'; + } + if (!array_key_exists($type, $XML_RPC_Types)) { + // XXX + // need some way to report this error + } elseif ($XML_RPC_Types[$type] == 1) { + $this->addScalar($val, $type); + } elseif ($XML_RPC_Types[$type] == 2) { + $this->addArray($val); + } elseif ($XML_RPC_Types[$type] == 3) { + $this->addStruct($val); + } + } + } + + /** + * @return int returns 1 if successful or 0 if there are problems + */ + function addScalar($val, $type = 'string') + { + global $XML_RPC_Types, $XML_RPC_Boolean; + + if ($this->mytype == 1) { + $this->raiseError('Scalar can have only one value', + XML_RPC_ERROR_INVALID_TYPE); + return 0; + } + $typeof = $XML_RPC_Types[$type]; + if ($typeof != 1) { + $this->raiseError("Not a scalar type (${typeof})", + XML_RPC_ERROR_INVALID_TYPE); + return 0; + } + + if ($type == $XML_RPC_Boolean) { + if (strcasecmp($val, 'true') == 0 + || $val == 1 + || ($val == true && strcasecmp($val, 'false'))) + { + $val = 1; + } else { + $val = 0; + } + } + + if ($this->mytype == 2) { + // we're adding to an array here + $ar = $this->me['array']; + $ar[] = new XML_RPC_Value($val, $type); + $this->me['array'] = $ar; + } else { + // a scalar, so set the value and remember we're scalar + $this->me[$type] = $val; + $this->mytype = $typeof; + } + return 1; + } + + /** + * @return int returns 1 if successful or 0 if there are problems + */ + function addArray($vals) + { + global $XML_RPC_Types; + if ($this->mytype != 0) { + $this->raiseError( + 'Already initialized as a [' . $this->kindOf() . ']', + XML_RPC_ERROR_ALREADY_INITIALIZED); + return 0; + } + $this->mytype = $XML_RPC_Types['array']; + $this->me['array'] = $vals; + return 1; + } + + /** + * @return int returns 1 if successful or 0 if there are problems + */ + function addStruct($vals) + { + global $XML_RPC_Types; + if ($this->mytype != 0) { + $this->raiseError( + 'Already initialized as a [' . $this->kindOf() . ']', + XML_RPC_ERROR_ALREADY_INITIALIZED); + return 0; + } + $this->mytype = $XML_RPC_Types['struct']; + $this->me['struct'] = $vals; + return 1; + } + + /** + * @return void + */ + function dump($ar) + { + reset($ar); + foreach ($ar as $key => $val) { + echo "$key => $val
"; + if ($key == 'array') { + foreach ($val as $key2 => $val2) { + echo "-- $key2 => $val2
"; + } + } + } + } + + /** + * @return string the data type of the current value + */ + function kindOf() + { + switch ($this->mytype) { + case 3: + return 'struct'; + + case 2: + return 'array'; + + case 1: + return 'scalar'; + + default: + return 'undef'; + } + } + + /** + * @return string the data in XML format + */ + function serializedata($typ, $val) + { + $rs = ''; + global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean; + if (!array_key_exists($typ, $XML_RPC_Types)) { + // XXX + // need some way to report this error + return; + } + switch ($XML_RPC_Types[$typ]) { + case 3: + // struct + $rs .= "\n"; + reset($val); + foreach ($val as $key2 => $val2) { + $rs .= "${key2}\n"; + $rs .= $this->serializeval($val2); + $rs .= "\n"; + } + $rs .= ''; + break; + + case 2: + // array + $rs .= "\n\n"; + for ($i = 0; $i < sizeof($val); $i++) { + $rs .= $this->serializeval($val[$i]); + } + $rs .= "\n"; + break; + + case 1: + switch ($typ) { + case $XML_RPC_Base64: + $rs .= "<${typ}>" . base64_encode($val) . ""; + break; + case $XML_RPC_Boolean: + $rs .= "<${typ}>" . ($val ? '1' : '0') . ""; + break; + case $XML_RPC_String: + $rs .= "<${typ}>" . htmlspecialchars($val). ""; + break; + default: + $rs .= "<${typ}>${val}"; + } + } + return $rs; + } + + /** + * @return string the data in XML format + */ + function serialize() + { + return $this->serializeval($this); + } + + /** + * @return string the data in XML format + */ + function serializeval($o) + { + $rs = ''; + $ar = $o->me; + reset($ar); + list($typ, $val) = each($ar); + $rs .= ''; + $rs .= $this->serializedata($typ, $val); + $rs .= "\n"; + return $rs; + } + + /** + * @return mixed the contents of the element requested + */ + function structmem($m) + { + return $this->me['struct'][$m]; + } + + /** + * @return void + */ + function structreset() + { + reset($this->me['struct']); + } + + /** + * @return the key/value pair of the struct's current element + */ + function structeach() + { + return each($this->me['struct']); + } + + /** + * @return mixed the current value + */ + function getval() + { + // UNSTABLE + global $XML_RPC_BOOLEAN, $XML_RPC_Base64; + + reset($this->me); + $b = current($this->me); + + // contributed by I Sofer, 2001-03-24 + // add support for nested arrays to scalarval + // i've created a new method here, so as to + // preserve back compatibility + + if (is_array($b)) { + foreach ($b as $id => $cont) { + $b[$id] = $cont->scalarval(); + } + } + + // add support for structures directly encoding php objects + if (is_object($b)) { + $t = get_object_vars($b); + foreach ($t as $id => $cont) { + $t[$id] = $cont->scalarval(); + } + foreach ($t as $id => $cont) { + eval('$b->'.$id.' = $cont;'); + } + } + + // end contrib + return $b; + } + + /** + * @return mixed + */ + function scalarval() + { + global $XML_RPC_Boolean, $XML_RPC_Base64; + reset($this->me); + return current($this->me); + } + + /** + * @return string + */ + function scalartyp() + { + global $XML_RPC_I4, $XML_RPC_Int; + reset($this->me); + $a = key($this->me); + if ($a == $XML_RPC_I4) { + $a = $XML_RPC_Int; + } + return $a; + } + + /** + * @return mixed the struct's current element + */ + function arraymem($m) + { + return $this->me['array'][$m]; + } + + /** + * @return int the number of elements in the array + */ + function arraysize() + { + reset($this->me); + list($a, $b) = each($this->me); + return sizeof($b); + } + + /** + * Determines if the item submitted is an XML_RPC_Value object + * + * @param mixed $val the variable to be evaluated + * + * @return bool TRUE if the item is an XML_RPC_Value object + * + * @static + * @since Method available since Release 1.3.0 + */ + function isValue($val) + { + return (strtolower(get_class($val)) == 'xml_rpc_value'); + } +} + +/** + * Return an ISO8601 encoded string + * + * While timezones ought to be supported, the XML-RPC spec says: + * + * "Don't assume a timezone. It should be specified by the server in its + * documentation what assumptions it makes about timezones." + * + * This routine always assumes localtime unless $utc is set to 1, in which + * case UTC is assumed and an adjustment for locale is made when encoding. + * + * @return string the formatted date + */ +function XML_RPC_iso8601_encode($timet, $utc = 0) +{ + if (!$utc) { + $t = strftime('%Y%m%dT%H:%M:%S', $timet); + } else { + if (function_exists('gmstrftime')) { + // gmstrftime doesn't exist in some versions + // of PHP + $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet); + } else { + $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z')); + } + } + return $t; +} + +/** + * Convert a datetime string into a Unix timestamp + * + * While timezones ought to be supported, the XML-RPC spec says: + * + * "Don't assume a timezone. It should be specified by the server in its + * documentation what assumptions it makes about timezones." + * + * This routine always assumes localtime unless $utc is set to 1, in which + * case UTC is assumed and an adjustment for locale is made when encoding. + * + * @return int the unix timestamp of the date submitted + */ +function XML_RPC_iso8601_decode($idate, $utc = 0) +{ + $t = 0; + if (ereg('([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})', $idate, $regs)) { + if ($utc) { + $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); + } else { + $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); + } + } + return $t; +} + +/** + * Converts an XML_RPC_Value object into native PHP types + * + * @param object $XML_RPC_val the XML_RPC_Value object to decode + * + * @return mixed the PHP values + */ +function XML_RPC_decode($XML_RPC_val) +{ + $kind = $XML_RPC_val->kindOf(); + + if ($kind == 'scalar') { + return $XML_RPC_val->scalarval(); + + } elseif ($kind == 'array') { + $size = $XML_RPC_val->arraysize(); + $arr = array(); + for ($i = 0; $i < $size; $i++) { + $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i)); + } + return $arr; + + } elseif ($kind == 'struct') { + $XML_RPC_val->structreset(); + $arr = array(); + while (list($key, $value) = $XML_RPC_val->structeach()) { + $arr[$key] = XML_RPC_decode($value); + } + return $arr; + } +} + +/** + * Converts native PHP types into an XML_RPC_Value object + * + * @param mixed $php_val the PHP value or variable you want encoded + * + * @return object the XML_RPC_Value object + */ +function XML_RPC_encode($php_val) +{ + global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double, $XML_RPC_String, + $XML_RPC_Array, $XML_RPC_Struct; + + $type = gettype($php_val); + $XML_RPC_val = new XML_RPC_Value; + + switch ($type) { + case 'array': + if (empty($php_val)) { + $XML_RPC_val->addArray($php_val); + break; + } + $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1)); + if (empty($tmp)) { + $arr = array(); + foreach ($php_val as $k => $v) { + $arr[$k] = XML_RPC_encode($v); + } + $XML_RPC_val->addArray($arr); + break; + } + // fall though if it's not an enumerated array + + case 'object': + $arr = array(); + foreach ($php_val as $k => $v) { + $arr[$k] = XML_RPC_encode($v); + } + $XML_RPC_val->addStruct($arr); + break; + + case 'integer': + $XML_RPC_val->addScalar($php_val, $XML_RPC_Int); + break; + + case 'double': + $XML_RPC_val->addScalar($php_val, $XML_RPC_Double); + break; + + case 'string': + case 'NULL': + $XML_RPC_val->addScalar($php_val, $XML_RPC_String); + break; + + case 'boolean': + // Add support for encoding/decoding of booleans, since they + // are supported in PHP + // by + $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean); + break; + + case 'unknown type': + default: + $XML_RPC_val = false; + } + return $XML_RPC_val; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ + +?> +XML_RPC-1.3.1/Server.php100644 1750 144 47770 10260516576 10104 + * @author Stig Bakken + * @author Martin Jansen + * @author Daniel Convissor + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version CVS: $Id: Server.php,v 1.26 2005/05/09 21:39:47 danielc Exp $ + * @link http://pear.php.net/package/XML_RPC + */ + + +/** + * Pull in the XML_RPC class + */ +require_once 'XML/RPC.php'; + + +/** + * signature for system.listMethods: return = array, + * parameters = a string or nothing + * @global array $GLOBALS['XML_RPC_Server_listMethods_sig'] + */ +$GLOBALS['XML_RPC_Server_listMethods_sig'] = array( + array($GLOBALS['XML_RPC_Array'], + $GLOBALS['XML_RPC_String'] + ), + array($GLOBALS['XML_RPC_Array']) +); + +/** + * docstring for system.listMethods + * @global string $GLOBALS['XML_RPC_Server_listMethods_doc'] + */ +$GLOBALS['XML_RPC_Server_listMethods_doc'] = 'This method lists all the' + . ' methods that the XML-RPC server knows how to dispatch'; + +/** + * signature for system.methodSignature: return = array, + * parameters = string + * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig'] + */ +$GLOBALS['XML_RPC_Server_methodSignature_sig'] = array( + array($GLOBALS['XML_RPC_Array'], + $GLOBALS['XML_RPC_String'] + ) +); + +/** + * docstring for system.methodSignature + * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc'] + */ +$GLOBALS['XML_RPC_Server_methodSignature_doc'] = 'Returns an array of known' + . ' signatures (an array of arrays) for the method name passed. If' + . ' no signatures are known, returns a none-array (test for type !=' + . ' array to detect missing signature)'; + +/** + * signature for system.methodHelp: return = string, + * parameters = string + * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig'] + */ +$GLOBALS['XML_RPC_Server_methodHelp_sig'] = array( + array($GLOBALS['XML_RPC_String'], + $GLOBALS['XML_RPC_String'] + ) +); + +/** + * docstring for methodHelp + * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc'] + */ +$GLOBALS['XML_RPC_Server_methodHelp_doc'] = 'Returns help text if defined' + . ' for the method passed, otherwise returns an empty string'; + +/** + * dispatch map for the automatically declared XML-RPC methods. + * @global array $GLOBALS['XML_RPC_Server_dmap'] + */ +$GLOBALS['XML_RPC_Server_dmap'] = array( + 'system.listMethods' => array( + 'function' => 'XML_RPC_Server_listMethods', + 'signature' => $GLOBALS['XML_RPC_Server_listMethods_sig'], + 'docstring' => $GLOBALS['XML_RPC_Server_listMethods_doc'] + ), + 'system.methodHelp' => array( + 'function' => 'XML_RPC_Server_methodHelp', + 'signature' => $GLOBALS['XML_RPC_Server_methodHelp_sig'], + 'docstring' => $GLOBALS['XML_RPC_Server_methodHelp_doc'] + ), + 'system.methodSignature' => array( + 'function' => 'XML_RPC_Server_methodSignature', + 'signature' => $GLOBALS['XML_RPC_Server_methodSignature_sig'], + 'docstring' => $GLOBALS['XML_RPC_Server_methodSignature_doc'] + ) +); + +/** + * @global string $GLOBALS['XML_RPC_Server_debuginfo'] + */ +$GLOBALS['XML_RPC_Server_debuginfo'] = ''; + + +/** + * Lists all the methods that the XML-RPC server knows how to dispatch + * + * @return object a new XML_RPC_Response object + */ +function XML_RPC_Server_listMethods($server, $m) +{ + global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; + + $v = new XML_RPC_Value(); + $outAr = array(); + foreach ($server->dmap as $key => $val) { + $outAr[] = new XML_RPC_Value($key, 'string'); + } + foreach ($XML_RPC_Server_dmap as $key => $val) { + $outAr[] = new XML_RPC_Value($key, 'string'); + } + $v->addArray($outAr); + return new XML_RPC_Response($v); +} + +/** + * Returns an array of known signatures (an array of arrays) + * for the given method + * + * If no signatures are known, returns a none-array + * (test for type != array to detect missing signature) + * + * @return object a new XML_RPC_Response object + */ +function XML_RPC_Server_methodSignature($server, $m) +{ + global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; + + $methName = $m->getParam(0); + $methName = $methName->scalarval(); + if (strpos($methName, 'system.') === 0) { + $dmap = $XML_RPC_Server_dmap; + $sysCall = 1; + } else { + $dmap = $server->dmap; + $sysCall = 0; + } + // print "\n"; + if (isset($dmap[$methName])) { + if ($dmap[$methName]['signature']) { + $sigs = array(); + $thesigs = $dmap[$methName]['signature']; + for ($i = 0; $i < sizeof($thesigs); $i++) { + $cursig = array(); + $inSig = $thesigs[$i]; + for ($j = 0; $j < sizeof($inSig); $j++) { + $cursig[] = new XML_RPC_Value($inSig[$j], 'string'); + } + $sigs[] = new XML_RPC_Value($cursig, 'array'); + } + $r = new XML_RPC_Response(new XML_RPC_Value($sigs, 'array')); + } else { + $r = new XML_RPC_Response(new XML_RPC_Value('undef', 'string')); + } + } else { + $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'], + $XML_RPC_str['introspect_unknown']); + } + return $r; +} + +/** + * Returns help text if defined for the method passed, otherwise returns + * an empty string + * + * @return object a new XML_RPC_Response object + */ +function XML_RPC_Server_methodHelp($server, $m) +{ + global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; + + $methName = $m->getParam(0); + $methName = $methName->scalarval(); + if (strpos($methName, 'system.') === 0) { + $dmap = $XML_RPC_Server_dmap; + $sysCall = 1; + } else { + $dmap = $server->dmap; + $sysCall = 0; + } + // print "\n"; + if (isset($dmap[$methName])) { + if ($dmap[$methName]['docstring']) { + $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']), + 'string'); + } else { + $r = new XML_RPC_Response(new XML_RPC_Value('', 'string')); + } + } else { + $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'], + $XML_RPC_str['introspect_unknown']); + } + return $r; +} + +/** + * @return void + */ +function XML_RPC_Server_debugmsg($m) +{ + global $XML_RPC_Server_debuginfo; + $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n"; +} + + +/** + * A server for receiving and replying to XML RPC requests + * + * + * $server = new XML_RPC_Server( + * array( + * 'isan8' => + * array( + * 'function' => 'is_8', + * 'signature' => + * array( + * array('boolean', 'int'), + * array('boolean', 'int', 'boolean'), + * array('boolean', 'string'), + * array('boolean', 'string', 'boolean'), + * ), + * 'docstring' => 'Is the value an 8?' + * ), + * ), + * 1, + * 0 + * ); + * + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill + * @author Stig Bakken + * @author Martin Jansen + * @author Daniel Convissor + * @copyright 1999-2001 Edd Dumbill, 2001-2005 The PHP Group + * @version Release: 1.3.1 + * @link http://pear.php.net/package/XML_RPC + */ +class XML_RPC_Server +{ + /** + * The dispatch map, listing the methods this server provides. + * @var array + */ + var $dmap = array(); + + /** + * The present response's encoding + * @var string + * @see XML_RPC_Message::getEncoding() + */ + var $encoding = ''; + + /** + * Debug mode (0 = off, 1 = on) + * @var integer + */ + var $debug = 0; + + /** + * The response's HTTP headers + * @var string + */ + var $server_headers = ''; + + /** + * The response's XML payload + * @var string + */ + var $server_payload = ''; + + + /** + * Constructor for the XML_RPC_Server class + * + * @param array $dispMap the dispatch map. An associative array + * explaining each function. The keys of the main + * array are the procedure names used by the + * clients. The value is another associative array + * that contains up to three elements: + * + The 'function' element's value is the name + * of the function or method that gets called. + * To define a class' method: 'class::method'. + * + The 'signature' element (optional) is an + * array describing the return values and + * parameters + * + The 'docstring' element (optional) is a + * string describing what the method does + * @param int $serviceNow should the HTTP response be sent now? + * (1 = yes, 0 = no) + * @param int $debug should debug output be displayed? + * (1 = yes, 0 = no) + * + * @return void + */ + function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0) + { + global $HTTP_RAW_POST_DATA; + + if ($debug) { + $this->debug = 1; + } else { + $this->debug = 0; + } + + $this->dmap = $dispMap; + + if ($serviceNow) { + $this->service(); + } else { + $this->createServerPayload(); + $this->createServerHeaders(); + } + } + + /** + * @return string the debug information if debug debug mode is on + */ + function serializeDebug() + { + global $XML_RPC_Server_debuginfo, $HTTP_RAW_POST_DATA; + + if ($this->debug) { + XML_RPC_Server_debugmsg('vvv POST DATA RECEIVED BY SERVER vvv' . "\n" + . $HTTP_RAW_POST_DATA + . "\n" . '^^^ END POST DATA ^^^'); + } + + if ($XML_RPC_Server_debuginfo != '') { + return "\n"; + } else { + return ''; + } + } + + /** + * Sends the response + * + * The encoding and content-type are determined by + * XML_RPC_Message::getEncoding() + * + * @return void + * + * @uses XML_RPC_Server::createServerPayload(), + * XML_RPC_Server::createServerHeaders() + */ + function service() + { + $this->createServerPayload(); + $this->createServerHeaders(); + header($this->server_headers); + print $this->server_payload; + } + + /** + * Generates the payload and puts it in the $server_payload property + * + * @return void + * + * @uses XML_RPC_Server::parseRequest(), XML_RPC_Server::$encoding, + * XML_RPC_Response::serialize(), XML_RPC_Server::serializeDebug() + */ + function createServerPayload() + { + $r = $this->parseRequest(); + $this->server_payload = 'encoding . '"?>' . "\n" + . $this->serializeDebug() + . $r->serialize(); + } + + /** + * Determines the HTTP headers and puts them in the $server_headers + * property + * + * @return boolean TRUE if okay, FALSE if $server_payload isn't set. + * + * @uses XML_RPC_Server::createServerPayload(), + * XML_RPC_Server::$server_headers + */ + function createServerHeaders() + { + if (!$this->server_payload) { + return false; + } + $this->server_headers = 'Content-Length: ' + . strlen($this->server_payload) . "\r\n" + . 'Content-Type: text/xml;' + . ' charset=' . $this->encoding; + return true; + } + + /** + * @return array + */ + function verifySignature($in, $sig) + { + for ($i = 0; $i < sizeof($sig); $i++) { + // check each possible signature in turn + $cursig = $sig[$i]; + if (sizeof($cursig) == $in->getNumParams() + 1) { + $itsOK = 1; + for ($n = 0; $n < $in->getNumParams(); $n++) { + $p = $in->getParam($n); + // print "\n"; + if ($p->kindOf() == 'scalar') { + $pt = $p->scalartyp(); + } else { + $pt = $p->kindOf(); + } + // $n+1 as first type of sig is return type + if ($pt != $cursig[$n+1]) { + $itsOK = 0; + $pno = $n+1; + $wanted = $cursig[$n+1]; + $got = $pt; + break; + } + } + if ($itsOK) { + return array(1); + } + } + } + if (isset($wanted)) { + return array(0, "Wanted ${wanted}, got ${got} at param ${pno}"); + } else { + $allowed = array(); + foreach ($sig as $val) { + end($val); + $allowed[] = key($val); + } + $allowed = array_unique($allowed); + $last = count($allowed) - 1; + if ($last > 0) { + $allowed[$last] = 'or ' . $allowed[$last]; + } + return array(0, + 'Signature permits ' . implode(', ', $allowed) + . ' parameters but the request had ' + . $in->getNumParams()); + } + } + + /** + * @return object a new XML_RPC_Response object + * + * @uses XML_RPC_Message::getEncoding(), XML_RPC_Server::$encoding + */ + function parseRequest($data = '') + { + global $XML_RPC_xh, $HTTP_RAW_POST_DATA, + $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml, + $XML_RPC_defencoding, $XML_RPC_Server_dmap; + + if ($data == '') { + $data = $HTTP_RAW_POST_DATA; + } + + $this->encoding = XML_RPC_Message::getEncoding($data); + $parser_resource = xml_parser_create($this->encoding); + $parser = (int) $parser_resource; + + $XML_RPC_xh[$parser] = array(); + $XML_RPC_xh[$parser]['st'] = ''; + $XML_RPC_xh[$parser]['cm'] = 0; + $XML_RPC_xh[$parser]['isf'] = 0; + $XML_RPC_xh[$parser]['params'] = array(); + $XML_RPC_xh[$parser]['method'] = ''; + + $plist = ''; + + // decompose incoming XML into request structure + + xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true); + xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee'); + xml_set_character_data_handler($parser_resource, 'XML_RPC_cd'); + if (!xml_parse($parser_resource, $data, 1)) { + // return XML error as a faultCode + $r = new XML_RPC_Response(0, + $XML_RPC_errxml+xml_get_error_code($parser_resource), + sprintf('XML error: %s at line %d', + xml_error_string(xml_get_error_code($parser_resource)), + xml_get_current_line_number($parser_resource))); + xml_parser_free($parser_resource); + } else { + xml_parser_free($parser_resource); + $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']); + // now add parameters in + for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) { + // print '\n"; + $plist .= "$i - " . $XML_RPC_xh[$parser]['params'][$i] . " \n"; + eval('$m->addParam(' . $XML_RPC_xh[$parser]['params'][$i] . ');'); + } + XML_RPC_Server_debugmsg($plist); + + // now to deal with the method + $methName = $XML_RPC_xh[$parser]['method']; + if (strpos($methName, 'system.') === 0) { + $dmap = $XML_RPC_Server_dmap; + $sysCall = 1; + } else { + $dmap = $this->dmap; + $sysCall = 0; + } + + if (isset($dmap[$methName]['function']) + && is_string($dmap[$methName]['function']) + && strpos($dmap[$methName]['function'], '::') !== false) + { + $dmap[$methName]['function'] = + explode('::', $dmap[$methName]['function']); + } + + if (isset($dmap[$methName]['function']) + && is_callable($dmap[$methName]['function'])) + { + // dispatch if exists + if (isset($dmap[$methName]['signature'])) { + $sr = $this->verifySignature($m, + $dmap[$methName]['signature'] ); + } + if (!isset($dmap[$methName]['signature']) || $sr[0]) { + // if no signature or correct signature + if ($sysCall) { + $r = call_user_func($dmap[$methName]['function'], $this, $m); + } else { + $r = call_user_func($dmap[$methName]['function'], $m); + } + if (!is_a($r, 'XML_RPC_Response')) { + $r = new XML_RPC_Response(0, $XML_RPC_err['not_response_object'], + $XML_RPC_str['not_response_object']); + } + } else { + $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'], + $XML_RPC_str['incorrect_params'] + . ': ' . $sr[1]); + } + } else { + // else prepare error response + $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'], + $XML_RPC_str['unknown_method']); + } + } + return $r; + } + + /** + * Echos back the input packet as a string value + * + * @return void + * + * Useful for debugging. + */ + function echoInput() + { + global $HTTP_RAW_POST_DATA; + + $r = new XML_RPC_Response(0); + $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string'); + print $r->serialize(); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ + +?> +package.xml100644 1750 144 22507 10260516576 6427 + + + XML_RPC + PHP implementation of the XML-RPC protocol + A PEAR-ified version of Useful Inc's XML-RPC for PHP. + +It has support for HTTP/HTTPS transport, proxies and authentication. + + + + ssb + Stig Bakken + stig@php.net + lead + + + danielc + Daniel Convissor + danielc@php.net + lead + + + + 1.3.1 + 2005-06-29 + PHP License + stable + * Security fix. Update highly recommended! + + + + + + + + + + + + + + + + + + + + + + 1.3.0RC3 + 2005-05-10 + beta + * When verifying requests against function signatures, if the number of parameters don't match, provide an appropriate message. NOTE: this resolves a path disclosure vulnerability. (Refines the changes made in the last commit.) Bug 4231. +* XML_RPC_Message::getParam() now returns an XML_RPC_Response object upon error. Changed from Release 1.3.0RC2. +* Add the XML_RPC_Value::isValue() method. For testing if an item is an XML_RPC_Value object. +* If XML_RPC_Client::send() is given an incorrect $msg parameter, raise an error with the new XML_RPC_ERROR_PROGRAMMING code and return 0. +* Improve cross-platform operation by using PEAR::loadExtension() instead of dl(). +* Use <br /> instead of <br> in XML_RPC_Value::dump(). + + + + 1.3.0RC2 + 2005-05-05 + beta + * If XML_RPC_Message::getParam() is given an incorrect parameter, raise an error with the new XML_RPC_ERROR_INCORRECT_PARAMS code and return FALSE. +* Handle improper requests to XML_RPC_Server::verifySignature(). Bug 4231. +* Try to allow HTTP 100 responses if followed by a 200 response. Bug 4116. +* Help Delphi users by making RPCMETHODNAME an alias for METHODNAME. Request 4205. + + + + 1.3.0RC1 + 2005-04-07 + beta + * Improve timeout handling for situations where connection to server is made but no response is not received in time. Accomplished via stream_set_timeout(). Request 3963. +* Add Fault Code 6: "The requested method didn't return an XML_RPC_Response object." Request 4032. +* Add the createServerPayload() and createServerHeaders() methods and the $server_payload and $server_headers properties. Request 3121. +* As in earlier versions, if the $serviceNow parameter to XML_RPC_Server() is 0, no data will be returned, but now the new $server_payload and $server_headers properties will be set. +* Convert the parser handle to an integer before using it as an index for $XML_RPC_xh[$parser]. Reduces E_STRICT notices. Bug 3782. +* Add createHeaders() method and $headers property to XML_RPC_Client to make testing easier. + + + + 1.2.2 + 2005-03-07 + stable + * When using a proxy, add the protocol to the Request-URI, making it an "absoluteURI" as per the HTTP 1.0 spec. Bug 3679. + + + + 1.2.1 + 2005-03-01 + stable + * Add isset() check before examining the dispatch map. Bug 3658. + + + + 1.2.0 + 2005-02-27 + stable + * Provide the "stable" release. +* Add package2.xml for compatibility with PEAR 1.4.0. +* For changes since 1.1.0, see the changelogs for the various RC releases. + + + + 1.2.0RC7 + 2005-02-22 + beta + * Add the setSendEncoding() method and $send_encoding + property to XML_RPC_Message. Request 3537. +* Allow class methods to be mapped using either syntax: + 'function' => 'hello::sayHello', + or + 'function' => array('hello', 'sayhello'), + Bug 3363. +* Use 8192 instead of 32768 for bytes in fread() + in parseResponseFile(). Bug 3340. + + + + 1.2.0RC6 + 2005-01-25 + beta + * Don't put the protocol in the Host field of the POST data. (danielc) + + + + 1.2.0RC5 + 2005-01-24 + beta + * If $port is 443 but a protocol isn't specified in $server, assume ssl:// is the protocol. + + + + 1.2.0RC4 + 2005-01-24 + beta + * When a connection attempt fails, have the method return 0. (danielc) +* Move the protocol/port checking/switching and the property settings from sendPayloadHTTP10() to the XML_RPC_Client constructor. (danielc) +* Add tests for setting the client properties. (danielc) +* Remove $GLOBALS['XML_RPC_twoslash'] since it's not used. (danielc) +* Bundle the tests with the package. (danielc) + + + + 1.2.0RC3 + 2005-01-19 + beta + * ssl uses port 443, not 445. + + + + 1.2.0RC2 + 2005-01-11 + beta + * Handle ssl:// in the $server string. (danielc) +* Also default to port 445 for ssl:// requests as well. (danielc) +* Enhance debugging in the server. (danielc) + + + + 1.2.0RC1 + 2004-12-30 + beta + * Make things work with SSL. Bug 2489. (nkukard lbsd net) +* Allow array function callbacks (Matt Kane) +* Some minor speed-ups (Matt Kane) +* Add Dump.php to the package (Christian Weiske) +* Replace all line endings with \r\n. Had only done replacements on \n. Bug 2521. (danielc) +* Silence fsockopen() errors. Bug 1714. (danielc) +* Encode empty arrays as an array. Bug 1493. (danielc) +* Eliminate undefined index notice when submitting empty arrays to XML_RPC_Encode(). Bug 1819. (danielc) +* Speed up check for enumerated arrays in XML_RPC_Encode(). (danielc) +* Prepend "XML_RPC_" to ERROR_NON_NUMERIC_FOUND, eliminating problem when eval()'ing error messages. (danielc) +* Use XML_RPC_Base::raiseError() instead of PEAR::raiseError() in XML_RPC_ee() because PEAR.php is lazy loaded. (danielc) +* Allow raiseError() to be called statically. (danielc) +* Stop double escaping of character entities. Bug 987. (danielc) + NOTICE: the following have been removed: + * XML_RPC_dh() + * $GLOBALS['XML_RPC_entities'] + * XML_RPC_entity_decode() + * XML_RPC_lookup_entity() +* Determine the XML's encoding via the encoding attribute in the XML declaration. Bug 52. (danielc) + + + + 1.1.0 + 2004-03-15 + stable + * Added support for sequential arrays to XML_RPC_encode() (mroch) +* Cleaned up new XML_RPC_encode() changes a bit (mroch, pierre) +* Remove "require_once 'PEAR.php'", include only when needed to raise an error +* Replace echo and error_log() with raiseError() (mroch) +* Make all classes extend XML_RPC_Base, which will handle common functions (mroch) +* be tolerant of junk after methodResponse (Luca Mariano, mroch) +* Silent notice even in the error log (pierre) +* fix include of shared xml extension on win32 (pierre) + + + + 1.0.4 + 2002-10-02 + stable + * added HTTP proxy authorization support (thanks to Arnaud Limbourg) + + + + 1.0.3 + 2002-05-19 + stable + * fix bug when parsing responses with boolean types + + + + 1.0.2 + 2002-04-16 + stable + * E_ALL fixes +* fix HTTP response header parsing + + + + 1.0.1 + 2001-09-25 + stable + This is a PEAR-ified version of Useful Inc's 1.0.1 release. +Includes an urgent security fix identified by Dan Libby <dan@libby.com>. + + + + + \ Kein Zeilenumbruch am Dateiende. diff -Naur php-5.0.4/php.ini-dist hardening-patch-5.0.4-0.3.2/php.ini-dist --- php-5.0.4/php.ini-dist 2005-03-01 01:25:09.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/php.ini-dist 2005-07-02 13:37:27.000000000 +0200 @@ -1187,6 +1187,177 @@ ; instead of original one. soap.wsdl_cache_ttl=86400 +[hardening-patch] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; +; hphp.log.syslog - Configures level for alerts reported through syslog +; hphp.log.sapi - Configures level for alerts reported through SAPI errorlog +; hphp.log.script - Configures level for alerts reported through external script +; +; hphp.log.syslog, hphp.log.sapi, hphp.log.script are bit-fields. +; Or each number up to get desired Hardening-Patch's reporting level +; +; S_ALL - All alerts +; S_MEMORY - All canary violations and the safe unlink protection use this class +; S_VARS - All variable filters trigger this class +; S_FILES - All violation of uploaded files filter use this class +; S_INCLUDE - The protection against malicious include filenames use this class +; S_SQL - Failed SQL queries in MySQL are logged with this class +; S_EXECUTOR - The execution depth protection uses this logging class +; S_MISC - All other log messages (f.e. format string protection) use this class +; +; Example: +; +; - Report all alerts (except memory alerts) to the SAPI errorlog, +; memory alerts through syslog and SQL+Include alerts fo the script +; +;hphp.log.syslog = S_MEMORY +;hphp.log.sapi = S_ALL & ~S_MEMORY +;hphp.log.script = S_INCLUDE | S_SQL +; +; Syslog logging: +; +; - Facility configuration: one of the following facilities +; +; LOG_KERN, LOG_USER, LOG_MAIL, LOG_DAEMON +; LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS +; LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_LOCAL0 +; LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4 +; LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_PID +; LOG_CONS, LOG_ODELAY, LOG_NDELAY, LOG_NOWAIT +; LOG_PERROR +; +; - Priority configuration: one of the followinf priorities +; +; LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_WARNING +; LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_ERR +; +hphp.log.syslog.priority = LOG_ALERT +hphp.log.syslog.facility = LOG_USER +; +; Script logging: +; +;hphp.log.script.name = /home/hphp/log_script +; +; Alert configuration: +; +; - Logged IP addresses from X-Forwarded-For instead of REMOTE_ADDR +; +;hphp.log.use-x-forwarded-for = On +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's 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-5.0.4/php.ini-recommended hardening-patch-5.0.4-0.3.2/php.ini-recommended --- php-5.0.4/php.ini-recommended 2005-03-01 01:25:09.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/php.ini-recommended 2005-07-02 13:38:04.000000000 +0200 @@ -1245,6 +1245,176 @@ ; instead of original one. soap.wsdl_cache_ttl=86400 +[hardening-patch] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; +; hphp.log.syslog - Configures level for alerts reported through syslog +; hphp.log.sapi - Configures level for alerts reported through SAPI errorlog +; hphp.log.script - Configures level for alerts reported through external script +; +; hphp.log.syslog, hphp.log.sapi, hphp.log.script are bit-fields. +; Or each number up to get desired Hardening-Patch's reporting level +; +; S_ALL - All alerts +; S_MEMORY - All canary violations and the safe unlink protection use this class +; S_VARS - All variable filters trigger this class +; S_FILES - All violation of uploaded files filter use this class +; S_INCLUDE - The protection against malicious include filenames use this class +; S_SQL - Failed SQL queries in MySQL are logged with this class +; S_EXECUTOR - The execution depth protection uses this logging class +; S_MISC - All other log messages (f.e. format string protection) use this class +; +; Example: +; +; - Report all alerts (except memory alerts) to the SAPI errorlog, +; memory alerts through syslog and SQL+Include alerts fo the script +; +;hphp.log.syslog = S_MEMORY +;hphp.log.sapi = S_ALL & ~S_MEMORY +;hphp.log.script = S_INCLUDE | S_SQL +; +; Syslog logging: +; +; - Facility configuration: one of the following facilities +; +; LOG_KERN, LOG_USER, LOG_MAIL, LOG_DAEMON +; LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS +; LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_LOCAL0 +; LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4 +; LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_PID +; LOG_CONS, LOG_ODELAY, LOG_NDELAY, LOG_NOWAIT +; LOG_PERROR +; +; - Priority configuration: one of the followinf priorities +; +; LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_WARNING +; LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_ERR +; +hphp.log.syslog.priority = LOG_ALERT +hphp.log.syslog.facility = LOG_USER +; +; Script logging: +; +;hphp.log.script.name = /home/hphp/log_script +; +; Alert configuration: +; +; - Logged IP addresses from X-Forwarded-For instead of REMOTE_ADDR +; +;hphp.log.use-x-forwarded-for = On +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Hardening-Patch's 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-5.0.4/sapi/apache/mod_php5.c hardening-patch-5.0.4-0.3.2/sapi/apache/mod_php5.c --- php-5.0.4/sapi/apache/mod_php5.c 2004-07-14 11:43:26.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/sapi/apache/mod_php5.c 2005-06-26 21:58:33.000000000 +0200 @@ -447,7 +447,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, }; /* }}} */ @@ -899,7 +899,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-5.0.4/sapi/apache2filter/sapi_apache2.c hardening-patch-5.0.4-0.3.2/sapi/apache2filter/sapi_apache2.c --- php-5.0.4/sapi/apache2filter/sapi_apache2.c 2005-01-07 07:28:24.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/sapi/apache2filter/sapi_apache2.c 2005-06-26 21:56:59.000000000 +0200 @@ -572,7 +572,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-5.0.4/sapi/apache2handler/sapi_apache2.c hardening-patch-5.0.4-0.3.2/sapi/apache2handler/sapi_apache2.c --- php-5.0.4/sapi/apache2handler/sapi_apache2.c 2005-03-10 12:23:57.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/sapi/apache2handler/sapi_apache2.c 2005-06-26 21:56:13.000000000 +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-5.0.4/sapi/cgi/cgi_main.c hardening-patch-5.0.4-0.3.2/sapi/cgi/cgi_main.c --- php-5.0.4/sapi/cgi/cgi_main.c 2005-02-11 03:06:48.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/sapi/cgi/cgi_main.c 2005-06-26 22:01:33.000000000 +0200 @@ -1414,11 +1414,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-5.0.4/sapi/cli/php_cli.c hardening-patch-5.0.4-0.3.2/sapi/cli/php_cli.c --- php-5.0.4/sapi/cli/php_cli.c 2005-03-22 16:09:20.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/sapi/cli/php_cli.c 2005-06-28 01:08:04.000000000 +0200 @@ -694,11 +694,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-5.0.4/TSRM/TSRM.h hardening-patch-5.0.4-0.3.2/TSRM/TSRM.h --- php-5.0.4/TSRM/TSRM.h 2005-03-11 12:12:07.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/TSRM/TSRM.h 2005-06-26 22:07:33.000000000 +0200 @@ -33,6 +33,13 @@ # define TSRM_API #endif +#if HARDENING_PATCH +# if HAVE_REALPATH +# undef realpath +# define realpath php_realpath +# endif +#endif + /* Only compile multi-threading functions if we're in ZTS mode */ #ifdef ZTS @@ -88,6 +95,7 @@ #define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts + #ifdef __cplusplus extern "C" { #endif diff -Naur php-5.0.4/TSRM/tsrm_virtual_cwd.c hardening-patch-5.0.4-0.3.2/TSRM/tsrm_virtual_cwd.c --- php-5.0.4/TSRM/tsrm_virtual_cwd.c 2005-03-11 12:09:42.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/TSRM/tsrm_virtual_cwd.c 2005-06-26 22:07:48.000000000 +0200 @@ -197,6 +197,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]; @@ -321,8 +480,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 */ @@ -348,9 +506,8 @@ path = resolved_path; path_length = strlen(path); } else { - /* disable for now free(tmp); - return 1; */ + return 1; } } free(tmp); diff -Naur php-5.0.4/TSRM/tsrm_virtual_cwd.h hardening-patch-5.0.4-0.3.2/TSRM/tsrm_virtual_cwd.h --- php-5.0.4/TSRM/tsrm_virtual_cwd.h 2005-03-11 12:07:17.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/TSRM/tsrm_virtual_cwd.h 2005-06-26 22:07:57.000000000 +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-5.0.4/Zend/zend_alloc.c hardening-patch-5.0.4-0.3.2/Zend/zend_alloc.c --- php-5.0.4/Zend/zend_alloc.c 2004-08-27 18:49:54.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_alloc.c 2005-06-26 22:52:59.000000000 +0200 @@ -64,6 +64,11 @@ # define END_MAGIC_SIZE 0 #endif +#if HARDENING_PATCH_MM_PROTECT +# define CANARY_SIZE sizeof(unsigned int) +#else +# define CANARY_SIZE 0 +#endif # if MEMORY_LIMIT # if ZEND_DEBUG @@ -104,9 +109,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; \ } #else @@ -145,6 +158,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 @@ -163,6 +182,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); @@ -179,7 +202,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); #if !ZEND_DISABLE_MEMORY_CACHE } #endif @@ -211,7 +234,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); } @@ -239,6 +265,10 @@ } } + +#if HARDENING_PATCH + zend_security_log(S_MEMORY, "Possible integer overflow catched by safe_emalloc()"); +#endif zend_error(E_ERROR, "Possible integer overflow in memory allocation (%zd * %zd + %zd)", nmemb, size, offset); return 0; } @@ -247,9 +277,22 @@ 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()) { @@ -291,23 +334,35 @@ ZEND_API void *_ecalloc(size_t nmemb, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - void *p; - int final_size = size*nmemb; + char *p; + size_t _size = nmemb * size; + + if (nmemb && (_size/nmemb!=size)) { +#if HARDENING_PATCH + zend_security_log(S_MEMORY, "Possible integer overflow catched by ecalloc()"); +#endif + fprintf(stderr,"FATAL: ecalloc(): Unable to allocate %ld * %ld bytes\n", (long) nmemb, (long) size); +#if ZEND_DEBUG && HAVE_KILL && HAVE_GETPID + kill(getpid(), SIGSEGV); +#else + exit(1); +#endif + } - HANDLE_BLOCK_INTERRUPTIONS(); - p = _emalloc(final_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - if (!p) { - HANDLE_UNBLOCK_INTERRUPTIONS(); - return (void *) p; + p = (char *) _emalloc(_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + if (p) { + memset(p, 0, _size); } - memset(p, 0, final_size); - HANDLE_UNBLOCK_INTERRUPTIONS(); - return p; + + return ((void *)p); } ZEND_API void *_erealloc(void *ptr, size_t size, int allow_failure ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { +#if HARDENING_PATCH_MM_PROTECT + unsigned int canary_2; +#endif zend_mem_header *p; zend_mem_header *orig; DECLARE_CACHE_VARS(); @@ -319,6 +374,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; @@ -342,7 +405,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); @@ -364,6 +427,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(); @@ -439,6 +505,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-5.0.4/Zend/zend_alloc.h hardening-patch-5.0.4-0.3.2/Zend/zend_alloc.h --- php-5.0.4/Zend/zend_alloc.h 2004-08-11 08:13:12.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_alloc.h 2005-06-26 22:16:00.000000000 +0200 @@ -35,6 +35,9 @@ #define MEM_BLOCK_CACHED_MAGIC 0xFB8277DCL typedef struct _zend_mem_header { +#if HARDENING_PATCH_MM_PROTECT + unsigned int canary; +#endif #if ZEND_DEBUG long magic; char *filename; diff -Naur php-5.0.4/Zend/zend_API.h hardening-patch-5.0.4-0.3.2/Zend/zend_API.h --- php-5.0.4/Zend/zend_API.h 2005-01-22 13:29:13.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_API.h 2005-06-26 20:59:20.000000000 +0200 @@ -47,6 +47,7 @@ #define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_FN(classname##_##name)) #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, +#define ZEND_STATIC_FE(zend_name, name, arg_info) { zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), 0 }, #define ZEND_NAMED_FE(zend_name, name, arg_info) ZEND_FENTRY(zend_name, name, arg_info, 0) #define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0) diff -Naur php-5.0.4/Zend/zend_builtin_functions.c hardening-patch-5.0.4-0.3.2/Zend/zend_builtin_functions.c --- php-5.0.4/Zend/zend_builtin_functions.c 2005-03-14 10:13:14.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_builtin_functions.c 2005-06-26 22:15:50.000000000 +0200 @@ -52,6 +52,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); @@ -111,6 +114,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) @@ -999,6 +1005,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-5.0.4/Zend/zend.c hardening-patch-5.0.4-0.3.2/Zend/zend.c --- php-5.0.4/Zend/zend.c 2005-03-16 00:47:12.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend.c 2005-07-05 18:32:04.000000000 +0200 @@ -54,6 +54,12 @@ ZEND_API void (*zend_unblock_interruptions)(void); ZEND_API void (*zend_ticks_function)(int ticks); ZEND_API void (*zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); +#if HARDENING_PATCH +ZEND_API void (*zend_security_log)(int loglevel, char *fmt, ...); +#endif +#if HARDENING_PATCH_INC_PROTECT +ZEND_API int (*zend_is_valid_include)(zval *z); +#endif int (*zend_vspprintf)(char **pbuf, size_t max_len, const char *format, va_list ap); void (*zend_on_timeout)(int seconds TSRMLS_DC); @@ -72,9 +78,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 STD_ZEND_INI_BOOLEAN("zend.ze1_compatibility_mode", "0", ZEND_INI_ALL, OnUpdateBool, ze1_compatibility_mode, zend_executor_globals, executor_globals) ZEND_INI_END() @@ -476,6 +553,7 @@ EG(user_error_handler) = NULL; EG(user_exception_handler) = NULL; EG(in_execution) = 0; + EG(in_code_type) = 0; EG(in_autoload) = NULL; EG(current_execute_data) = NULL; EG(current_module) = NULL; @@ -545,6 +623,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 @@ -747,6 +833,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-5.0.4/Zend/zend_canary.c hardening-patch-5.0.4-0.3.2/Zend/zend_canary.c --- php-5.0.4/Zend/zend_canary.c 1970-01-01 01:00:00.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_canary.c 2005-06-26 22:13:40.000000000 +0200 @@ -0,0 +1,58 @@ +/* + +----------------------------------------------------------------------+ + | Hardening-Patch for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Stefan Esser | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Stefan Esser | + +----------------------------------------------------------------------+ + */ +/* $Id: zend_canary.c,v 1.1 2004/11/26 12:45:41 ionic Exp $ */ + +#include "zend.h" + +#include +#include + + +#if HARDENING_PATCH_MM_PROTECT || HARDENING_PATCH_LL_PROTECT || HARDENING_PATCH_HASH_PROTECT + +/* will be replaced later with more compatible method */ +ZEND_API unsigned int zend_canary() +{ + time_t t; + unsigned int canary; + int fd; + + fd = open("/dev/urandom", 0); + if (fd != -1) { + int r = read(fd, &canary, sizeof(canary)); + close(fd); + if (r == sizeof(canary)) { + return (canary); + } + } + /* not good but we never want to do this */ + time(&t); + canary = *(unsigned int *)&t + getpid() << 16; + return (canary); +} +#endif + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff -Naur php-5.0.4/Zend/zend_compile.c hardening-patch-5.0.4-0.3.2/Zend/zend_compile.c --- php-5.0.4/Zend/zend_compile.c 2005-03-10 14:24:32.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_compile.c 2005-06-27 00:23:42.000000000 +0200 @@ -1455,7 +1455,7 @@ zend_op *opline; if (switch_entry->cond.op_type != IS_VAR && switch_entry->cond.op_type != IS_TMP_VAR) { - return 0; + return (switch_entry->cond.op_type == IS_UNUSED); } opline = get_next_op(CG(active_op_array) TSRMLS_CC); diff -Naur php-5.0.4/Zend/zend_compile.h hardening-patch-5.0.4-0.3.2/Zend/zend_compile.h --- php-5.0.4/Zend/zend_compile.h 2004-09-16 02:44:12.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_compile.h 2005-06-27 23:31:04.000000000 +0200 @@ -275,6 +275,8 @@ zval *object; union _temp_variable *Ts; zend_bool original_in_execution; + zend_uint original_in_code_type; + zend_uint execute_depth; zend_class_entry *calling_scope; struct _zend_execute_data *prev_execute_data; }; @@ -767,6 +769,7 @@ #define ZEND_OVERLOADED_FUNCTION 3 #define ZEND_EVAL_CODE 4 #define ZEND_OVERLOADED_FUNCTION_TEMPORARY 5 +#define ZEND_SANDBOX_CODE 6 #define ZEND_INTERNAL_CLASS 1 #define ZEND_USER_CLASS 2 diff -Naur php-5.0.4/Zend/zend_constants.c hardening-patch-5.0.4-0.3.2/Zend/zend_constants.c --- php-5.0.4/Zend/zend_constants.c 2004-07-13 21:22:11.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_constants.c 2005-07-01 18:17:57.000000000 +0200 @@ -107,6 +107,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-5.0.4/Zend/zend_errors.h hardening-patch-5.0.4-0.3.2/Zend/zend_errors.h --- php-5.0.4/Zend/zend_errors.h 2004-01-08 18:31:47.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_errors.h 2005-06-28 00:03:24.000000000 +0200 @@ -38,6 +38,17 @@ #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-5.0.4/Zend/zend_execute_API.c hardening-patch-5.0.4-0.3.2/Zend/zend_execute_API.c --- php-5.0.4/Zend/zend_execute_API.c 2005-03-19 15:29:18.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_execute_API.c 2005-07-03 10:40:05.000000000 +0200 @@ -137,6 +137,7 @@ EG(class_table) = CG(class_table); EG(in_execution) = 0; + EG(in_code_type) = 0; EG(in_autoload) = NULL; zend_ptr_stack_init(&EG(argument_stack)); @@ -966,7 +967,7 @@ return retval; } -ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC) +ZEND_API int zend_eval_string_ex_ex(char *str, zval *retval_ptr, char *string_name, int type TSRMLS_DC) { zval pv; zend_op_array *new_op_array; @@ -999,6 +1000,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; @@ -1033,6 +1035,12 @@ } +ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC) +{ + return (zend_eval_string_ex_ex(str, retval_ptr, string_name, ZEND_EVAL_CODE TSRMLS_CC)); +} + + ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, int handle_exceptions TSRMLS_DC) { int result; diff -Naur php-5.0.4/Zend/zend_execute.c hardening-patch-5.0.4-0.3.2/Zend/zend_execute.c --- php-5.0.4/Zend/zend_execute.c 2005-03-21 17:22:10.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_execute.c 2005-06-28 00:08:12.000000000 +0200 @@ -1343,6 +1343,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 1; /* CHECK_ME */ @@ -1369,6 +1370,16 @@ EX(original_in_execution) = EG(in_execution); EX(prev_execute_data) = EG(current_execute_data); EG(current_execute_data) = &execute_data; +#if HARDENING_PATCH + EX(execute_depth) = 0; + + if (op_array->type == ZEND_EVAL_CODE && 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) { @@ -1395,6 +1406,19 @@ EX(function_state).function_symbol_table = NULL; #endif +#if HARDENING_PATCH + if (EX(prev_execute_data) == NULL) { + EX(execute_depth) = 0; + } else { + EX(execute_depth) = EX(prev_execute_data)->execute_depth + 1; + } + + if (EG(hphp_executor_max_depth) > 0 && EX(execute_depth) > EG(hphp_executor_max_depth)) { + zend_security_log(S_EXECUTOR, "Maximum execution depth of %u violated", EG(hphp_executor_max_depth)); + zend_bailout(); + } +#endif + while (1) { #ifdef ZEND_WIN32 if (EG(timed_out)) { @@ -3523,7 +3547,12 @@ int dummy = 1; zend_file_handle file_handle; +#if HARDENING_PATCH_INC_PROTECT + if (zend_is_valid_include(inc_filename) + && (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC))) { +#else if (SUCCESS == zend_stream_open(inc_filename->value.str.val, &file_handle TSRMLS_CC)) { +#endif if (!file_handle.opened_path) { file_handle.opened_path = estrndup(inc_filename->value.str.val, inc_filename->value.str.len); @@ -3548,6 +3577,11 @@ break; case ZEND_INCLUDE: case ZEND_REQUIRE: +#if HARDENING_PATCH_INC_PROTECT + if (!zend_is_valid_include(inc_filename)) { + break; + } +#endif new_op_array = compile_filename(opline->op2.u.constant.value.lval, inc_filename TSRMLS_CC); break; case ZEND_EVAL: { diff -Naur php-5.0.4/Zend/zend_extensions.h hardening-patch-5.0.4-0.3.2/Zend/zend_extensions.h --- php-5.0.4/Zend/zend_extensions.h 2004-11-25 21:26:48.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_extensions.h 2005-06-28 14:46:24.000000000 +0200 @@ -24,10 +24,11 @@ #include "zend_compile.h" -/* The first number is the engine version and the rest is the date. +/* The first number is a flag saying that Hardening-Patch is used + * the second number is the engine version and the rest is the date. * This way engine 2 API no. is always greater than engine 1 API no.. */ -#define ZEND_EXTENSION_API_NO 220040412 +#define ZEND_EXTENSION_API_NO 1220050627 typedef struct _zend_extension_version_info { int zend_extension_api_no; diff -Naur php-5.0.4/Zend/zend_globals.h hardening-patch-5.0.4-0.3.2/Zend/zend_globals.h --- php-5.0.4/Zend/zend_globals.h 2004-11-04 00:14:31.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_globals.h 2005-06-30 12:41:57.000000000 +0200 @@ -174,6 +174,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; @@ -191,6 +201,7 @@ int ticks_count; zend_bool in_execution; + zend_uint in_code_type; HashTable *in_autoload; zend_bool bailout_set; zend_bool full_tables_cleanup; diff -Naur php-5.0.4/Zend/zend.h hardening-patch-5.0.4-0.3.2/Zend/zend.h --- php-5.0.4/Zend/zend.h 2005-03-10 12:30:44.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend.h 2005-07-03 10:41:35.000000000 +0200 @@ -290,6 +290,7 @@ /* Variable information */ zvalue_value value; /* value */ zend_uint refcount; + zend_ushort flags; zend_uchar type; /* active type */ zend_uchar is_ref; }; @@ -359,6 +360,12 @@ void (*on_timeout)(int seconds TSRMLS_DC); int (*stream_open_function)(const char *filename, zend_file_handle *handle TSRMLS_DC); int (*vspprintf_function)(char **pbuf, size_t max_len, const char *format, va_list ap); +#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; @@ -496,6 +503,16 @@ extern void (*zend_on_timeout)(int seconds TSRMLS_DC); extern ZEND_API int (*zend_stream_open_function)(const char *filename, zend_file_handle *handle TSRMLS_DC); extern int (*zend_vspprintf)(char **pbuf, size_t max_len, const char *format, va_list ap); +#if HARDENING_PATCH +extern ZEND_API void (*zend_security_log)(int loglevel, char *fmt, ...); +#endif +#if HARDENING_PATCH_INC_PROTECT +extern ZEND_API int (*zend_is_valid_include)(zval *z); +#endif + +#if HARDENING_PATCH_MM_PROTECT || HARDENING_PATCH_LL_PROTECT || HARDENING_PATCH_HASH_PROTECT +ZEND_API unsigned int zend_canary(void); +#endif ZEND_API void zend_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); @@ -620,6 +637,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-5.0.4/Zend/zend_hash.c hardening-patch-5.0.4-0.3.2/Zend/zend_hash.c --- php-5.0.4/Zend/zend_hash.c 2004-07-10 09:45:49.000000000 +0200 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_hash.c 2005-06-26 22:51:36.000000000 +0200 @@ -21,6 +21,18 @@ #include "zend.h" +#if HARDENING_PATCH_HASH_PROTECT + unsigned int zend_hash_canary = 0x1234567; + zend_bool zend_hash_canary_inited = 0; +#endif + +#define CHECK_HASH_CANARY(hash) \ + if (zend_hash_canary != (hash)->canary) { \ + zend_security_log(S_MEMORY, "Zend HashTable canary was overwritten"); \ + exit(1); \ + } + + #define CONNECT_TO_BUCKET_DLLIST(element, list_head) \ (element)->pNext = (list_head); \ (element)->pLast = NULL; \ @@ -138,6 +150,9 @@ { uint i = 3; Bucket **tmp; +#if HARDENING_PATCH_HASH_PROTECT + TSRMLS_FETCH(); +#endif SET_INCONSISTENT(HT_OK); @@ -147,6 +162,13 @@ ht->nTableSize = 1 << i; ht->nTableMask = ht->nTableSize - 1; +#if HARDENING_PATCH_HASH_PROTECT + if (zend_hash_canary_inited==0) { + zend_hash_canary = zend_canary(); + zend_hash_canary_inited = 1; + } + ht->canary = zend_hash_canary; +#endif ht->pDestructor = pDestructor; ht->arBuckets = NULL; ht->pListHead = NULL; @@ -226,6 +248,9 @@ } #endif if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } UPDATE_DATA(ht, p, pData, nDataSize); @@ -291,6 +316,9 @@ } #endif if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } UPDATE_DATA(ht, p, pData, nDataSize); @@ -366,6 +394,9 @@ } #endif if (ht->pDestructor) { +#if HARDENING_PATCH_HASH_PROTECT + CHECK_HASH_CANARY(ht); +#endif ht->pDestructor(p->pData); } UPDATE_DATA(ht, p, pData, nDataSize); @@ -414,7 +445,7 @@ IS_CONSISTENT(ht); if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */ - t = (Bucket **) perealloc_recoverable(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent); + t = (Bucket **) perealloc(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent); if (t) { HANDLE_BLOCK_INTERRUPTIONS(); ht->arBuckets = t; @@ -424,6 +455,7 @@ HANDLE_UNBLOCK_INTERRUPTIONS(); return SUCCESS; } + zend_error(E_ERROR, "zend_hash_do_resize - out of memory"); return FAILURE; } return SUCCESS; @@ -487,6 +519,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) { @@ -516,6 +551,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) { @@ -542,6 +580,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) { @@ -571,6 +612,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-5.0.4/Zend/zend_hash.h hardening-patch-5.0.4-0.3.2/Zend/zend_hash.h --- php-5.0.4/Zend/zend_hash.h 2004-01-08 18:31:47.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_hash.h 2005-06-26 22:20:57.000000000 +0200 @@ -58,6 +58,9 @@ } Bucket; typedef struct _hashtable { +#if HARDENING_PATCH_HASH_PROTECT + unsigned int canary; +#endif uint nTableSize; uint nTableMask; uint nNumOfElements; diff -Naur php-5.0.4/Zend/zend_llist.c hardening-patch-5.0.4-0.3.2/Zend/zend_llist.c --- php-5.0.4/Zend/zend_llist.c 2004-01-08 18:31:47.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_llist.c 2005-06-26 22:51:54.000000000 +0200 @@ -22,9 +22,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; @@ -38,6 +63,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) { @@ -56,6 +86,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) { @@ -93,10 +128,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; @@ -108,7 +153,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); @@ -133,7 +185,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; } @@ -159,9 +218,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; } @@ -172,11 +238,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; } @@ -187,7 +263,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); } } @@ -199,6 +281,9 @@ zend_llist_element **elements; zend_llist_element *element, **ptr; +#if HARDENING_PATCH_LL_PROTECT + CHECK_LIST_CANARY(l) +#endif if (l->count <= 0) { return; } @@ -208,6 +293,9 @@ ptr = &elements[0]; for (element=l->head; element; element=element->next) { +#if HARDENING_PATCH_LL_PROTECT + CHECK_LISTELEMENT_CANARY(element) +#endif *ptr++ = element; } @@ -230,7 +318,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); } } @@ -241,8 +335,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); @@ -251,6 +351,10 @@ ZEND_API int zend_llist_count(zend_llist *l) { +#if HARDENING_PATCH_LL_PROTECT + TSRMLS_FETCH(); + CHECK_LIST_CANARY(l) +#endif return l->count; } @@ -259,8 +363,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; @@ -272,8 +383,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; @@ -285,9 +403,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; } } @@ -299,9 +427,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-5.0.4/Zend/zend_llist.h hardening-patch-5.0.4-0.3.2/Zend/zend_llist.h --- php-5.0.4/Zend/zend_llist.h 2004-01-08 18:31:47.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_llist.h 2005-06-26 22:17:28.000000000 +0200 @@ -23,6 +23,9 @@ #define ZEND_LLIST_H 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 */ @@ -35,6 +38,9 @@ typedef void (*llist_apply_func_t)(void * TSRMLS_DC); typedef struct _zend_llist { +#if HARDENING_PATCH_LL_PROTECT + unsigned int canary_h; /* head */ +#endif zend_llist_element *head; zend_llist_element *tail; size_t count; @@ -42,6 +48,9 @@ llist_dtor_func_t dtor; unsigned char persistent; zend_llist_element *traverse_ptr; +#if HARDENING_PATCH_LL_PROTECT + unsigned int canary_t; /* tail */ +#endif } zend_llist; typedef zend_llist_element* zend_llist_position; diff -Naur php-5.0.4/Zend/zend_modules.h hardening-patch-5.0.4-0.3.2/Zend/zend_modules.h --- php-5.0.4/Zend/zend_modules.h 2005-03-16 00:47:12.000000000 +0100 +++ hardening-patch-5.0.4-0.3.2/Zend/zend_modules.h 2005-06-28 14:48:10.000000000 +0200 @@ -38,7 +38,7 @@ extern struct _zend_arg_info fourth_arg_force_ref[5]; extern struct _zend_arg_info all_args_by_ref[1]; -#define ZEND_MODULE_API_NO 20041030 +#define ZEND_MODULE_API_NO 1020050627 #ifdef ZTS #define USING_ZTS 1 #else