From 3ed3c4f59ab010bc0c968ac98c6b4bf28dfe1267 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 19 Jan 2018 14:13:45 -0800 Subject: [PATCH 1/7] Add support for libmemcached encryption --- config.m4 | 19 ++++++++++++++++ memcached-api.php | 4 ++++ package.xml | 1 + php_memcached.c | 44 +++++++++++++++++++++++++++++++++++++ tests/set_encoding_key.phpt | 44 +++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 tests/set_encoding_key.phpt diff --git a/config.m4 b/config.m4 index 7a4a6c9f..d38d5dc4 100644 --- a/config.m4 +++ b/config.m4 @@ -317,10 +317,29 @@ if test "$PHP_MEMCACHED" != "no"; then CFLAGS="$ORIG_CFLAGS" LIBS="$ORIG_LIBS" + CFLAGS="$CFLAGS $PHP_LIBMEMCACHED_INCLUDES" + LIBS="$LIBS $PHP_LIBMEMCACHED_LIBS" + if test "$ac_cv_have_memcached_exist" = "yes"; then AC_DEFINE(HAVE_MEMCACHED_EXIST, [1], [Whether memcached_exist is defined]) fi + AC_CACHE_CHECK([whether memcached_set_encoding_key is defined], ac_cv_have_memcached_set_encoding_key, [ + AC_TRY_LINK( + [ #include ], + [ memcached_set_encoding_key (NULL, NULL, 0); ], + [ ac_cv_have_memcached_set_encoding_key="yes" ], + [ ac_cv_have_memcached_set_encoding_key="no" ] + ) + ]) + + CFLAGS="$ORIG_CFLAGS" + LIBS="$ORIG_LIBS" + + if test "$ac_cv_have_memcached_set_encoding_key" = "yes"; then + AC_DEFINE(HAVE_MEMCACHED_SET_ENCODING_KEY, [1], [Whether memcached_set_encoding_key is defined]) + fi + PHP_MEMCACHED_FILES="php_memcached.c php_libmemcached_compat.c g_fmt.c" if test "$PHP_SYSTEM_FASTLZ" != "no"; then diff --git a/memcached-api.php b/memcached-api.php index f5174e22..8ab03d4c 100644 --- a/memcached-api.php +++ b/memcached-api.php @@ -96,6 +96,8 @@ class Memcached { const HAVE_MSGPACK; + const HAVE_ENCODING; + /** * Feature support */ @@ -363,6 +365,8 @@ public function isPristine( ) {} public function setSaslAuthData( $username, $password ) {} + public function setEncodingKey( $key ) {} + } class MemcachedException extends Exception { diff --git a/package.xml b/package.xml index bbdaae81..7d2b04b4 100644 --- a/package.xml +++ b/package.xml @@ -159,6 +159,7 @@ Fixes + diff --git a/php_memcached.c b/php_memcached.c index cc6456be..134b4d7e 100644 --- a/php_memcached.c +++ b/php_memcached.c @@ -3265,6 +3265,32 @@ static PHP_METHOD(Memcached, setSaslAuthData) /* }}} */ #endif /* HAVE_MEMCACHED_SASL */ +#ifdef HAVE_MEMCACHED_SET_ENCODING_KEY +/* {{{ Memcached::setEncodingKey(string key) + Sets AES encryption key (libmemcached 1.0.6 and higher) */ +static PHP_METHOD(Memcached, setEncodingKey) +{ + MEMC_METHOD_INIT_VARS; + memcached_return status; + zend_string *key; + + /* "S" */ + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(key) + ZEND_PARSE_PARAMETERS_END(); + + MEMC_METHOD_FETCH_OBJECT; + + status = memcached_set_encoding_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key)); + + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ +#endif /* HAVE_MEMCACHED_SET_ENCODING_KEY */ + /* {{{ Memcached::getResultCode() Returns the result code from the last operation */ static PHP_METHOD(Memcached, getResultCode) @@ -4034,6 +4060,12 @@ ZEND_BEGIN_ARG_INFO(arginfo_setSaslAuthData, 0) ZEND_END_ARG_INFO() #endif +#ifdef HAVE_MEMCACHED_SET_ENCODING_KEY +ZEND_BEGIN_ARG_INFO(arginfo_setEncodingKey, 0) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_INFO(arginfo_setOption, 0) ZEND_ARG_INFO(0, option) ZEND_ARG_INFO(0, value) @@ -4133,6 +4165,9 @@ static zend_function_entry memcached_class_methods[] = { MEMC_ME(setBucket, arginfo_setBucket) #ifdef HAVE_MEMCACHED_SASL MEMC_ME(setSaslAuthData, arginfo_setSaslAuthData) +#endif +#ifdef HAVE_MEMCACHED_SET_ENCODING_KEY + MEMC_ME(setEncodingKey, arginfo_setEncodingKey) #endif MEMC_ME(isPersistent, arginfo_isPersistent) MEMC_ME(isPristine, arginfo_isPristine) @@ -4282,6 +4317,15 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_MSGPACK, 0); #endif + /* + * Indicate whether set_encoding_key is available + */ +#ifdef HAVE_MEMCACHED_SET_ENCODING_KEY + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_ENCODING, 1); +#else + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_ENCODING, 0); +#endif + #ifdef HAVE_MEMCACHED_SESSION REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SESSION, 1); #else diff --git a/tests/set_encoding_key.phpt b/tests/set_encoding_key.phpt new file mode 100644 index 00000000..a59d5045 --- /dev/null +++ b/tests/set_encoding_key.phpt @@ -0,0 +1,44 @@ +--TEST-- +Test libmemcached encryption +--SKIPIF-- + +--FILE-- +setEncodingKey("Hello")); + +$key = uniqid ('encoding_test_'); +var_dump ($m->set ($key, 'set using encoding')); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# Change the encryption key. The old value will be inaccessible. +var_dump ($m->setEncodingKey("World")); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +var_dump ($m->set ($key, 'set using encoding')); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +?> +--EXPECT-- +bool(true) +bool(true) +string(18) "set using encoding" +OK +bool(true) +bool(false) +OK +bool(true) +string(18) "set using encoding" +OK From 8cabcc14870f7b6a2d9c6643abd2b53afc4df2e1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 20 Jan 2018 07:11:07 -0800 Subject: [PATCH 2/7] Exploring behavior changes in libmemcached 1.0.18 set_encoding_key --- tests/set_encoding_key.phpt | 14 ++++++++-- tests/set_encoding_key2.phpt | 51 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 tests/set_encoding_key2.phpt diff --git a/tests/set_encoding_key.phpt b/tests/set_encoding_key.phpt index a59d5045..37b6039e 100644 --- a/tests/set_encoding_key.phpt +++ b/tests/set_encoding_key.phpt @@ -5,15 +5,15 @@ Test libmemcached encryption include dirname (__FILE__) . '/config.inc'; if (!extension_loaded("memcached")) die ("skip"); if (!Memcached::HAVE_ENCODING) die ("skip no set_encoding_key support enabled"); +// if (Memcached::LIBMEMCACHED_VERSION_HEX >= 0x01000018) die ("skip test for libmemcached 1.0.18 and higher"); ?> --FILE-- setEncodingKey("Hello")); - -$key = uniqid ('encoding_test_'); var_dump ($m->set ($key, 'set using encoding')); var_dump ($m->get ($key)); @@ -25,6 +25,16 @@ var_dump ($m->get ($key)); echo "OK" . PHP_EOL; +# Restore the original key to retrieve old values again. +var_dump ($m->setEncodingKey("Hello")); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# With a new encoding key we can still write new values, +# this works as expected with libmemcached 1.0.18 and higher. +var_dump ($m->setEncodingKey("World")); +var_dump ($m->get ($key)); var_dump ($m->set ($key, 'set using encoding')); var_dump ($m->get ($key)); diff --git a/tests/set_encoding_key2.phpt b/tests/set_encoding_key2.phpt new file mode 100644 index 00000000..8ad519ec --- /dev/null +++ b/tests/set_encoding_key2.phpt @@ -0,0 +1,51 @@ +--TEST-- +Test libmemcached encryption +--SKIPIF-- += 0x01000018) die ("skip test for libmemcached lower than 1.0.18"); +?> +--FILE-- +setEncodingKey("Hello")); +var_dump ($m->set ($key, 'set using encoding')); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# Change the encryption key. The old value will be inaccessible. +var_dump ($m->setEncodingKey("World")); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# Restore the original key to retrieve old values again. +var_dump ($m->setEncodingKey("Hello")); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# New values cannot be written after the key has changed in libmemcached < 1.0.18. +var_dump ($m->set ($key, 'set using encoding')); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +?> +--EXPECT-- +bool(true) +bool(true) +string(18) "set using encoding" +OK +bool(true) +bool(false) +OK +bool(false) +bool(false) +OK From cf81e92f6c80292d7f0c6135c208b804546121d0 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 20 Jan 2018 07:16:08 -0800 Subject: [PATCH 3/7] Exploring behavior changes in libmemcached 1.0.18 set_encoding_key --- tests/set_encoding_key.phpt | 7 ++++++- tests/set_encoding_key2.phpt | 14 +++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/set_encoding_key.phpt b/tests/set_encoding_key.phpt index 37b6039e..86539102 100644 --- a/tests/set_encoding_key.phpt +++ b/tests/set_encoding_key.phpt @@ -5,7 +5,7 @@ Test libmemcached encryption include dirname (__FILE__) . '/config.inc'; if (!extension_loaded("memcached")) die ("skip"); if (!Memcached::HAVE_ENCODING) die ("skip no set_encoding_key support enabled"); -// if (Memcached::LIBMEMCACHED_VERSION_HEX >= 0x01000018) die ("skip test for libmemcached 1.0.18 and higher"); +if (Memcached::LIBMEMCACHED_VERSION_HEX >= 0x01000018) die ("skip test for libmemcached 1.0.18 and higher"); ?> --FILE-- = 0x01000018) die ("skip test for libmemcached lower than 1.0.18"); +if (Memcached::LIBMEMCACHED_VERSION_HEX < 0x01000018) die ("skip test for libmemcached lower than 1.0.18"); ?> --FILE-- get ($key)); echo "OK" . PHP_EOL; -# New values cannot be written after the key has changed in libmemcached < 1.0.18. +# Once the encoding key has changes we are no longer able to +# write new values in libmemcached < 1.0.18. +var_dump ($m->setEncodingKey("World")); +var_dump ($m->get ($key)); var_dump ($m->set ($key, 'set using encoding')); var_dump ($m->get ($key)); @@ -46,6 +49,11 @@ OK bool(true) bool(false) OK +bool(true) +string(18) "set using encoding" +OK +bool(true) bool(false) -bool(false) +bool(true) +string(18) "set using encoding" OK From 2905ebf5a170705bc22c8c061e2f82f7b4caccc1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 20 Jan 2018 07:16:30 -0800 Subject: [PATCH 4/7] Add second test for set_encoding_key --- package.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/package.xml b/package.xml index 7d2b04b4..a8ece289 100644 --- a/package.xml +++ b/package.xml @@ -160,6 +160,7 @@ Fixes + From d729b7ac05c0341e88ace88f5e81e75233a8b516 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 20 Jan 2018 07:18:49 -0800 Subject: [PATCH 5/7] Had the conditions backwards --- tests/set_encoding_key.phpt | 2 +- tests/set_encoding_key2.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/set_encoding_key.phpt b/tests/set_encoding_key.phpt index 86539102..72d96daa 100644 --- a/tests/set_encoding_key.phpt +++ b/tests/set_encoding_key.phpt @@ -5,7 +5,7 @@ Test libmemcached encryption include dirname (__FILE__) . '/config.inc'; if (!extension_loaded("memcached")) die ("skip"); if (!Memcached::HAVE_ENCODING) die ("skip no set_encoding_key support enabled"); -if (Memcached::LIBMEMCACHED_VERSION_HEX >= 0x01000018) die ("skip test for libmemcached 1.0.18 and higher"); +if (Memcached::LIBMEMCACHED_VERSION_HEX < 0x01000018) die ("skip test for libmemcached 1.0.18 and higher"); ?> --FILE-- = 0x01000018) die ("skip test for libmemcached lower than 1.0.18"); ?> --FILE-- Date: Sat, 20 Jan 2018 07:45:20 -0800 Subject: [PATCH 6/7] Warnings when changing the encoding key for libmemcached < 1.0.18 --- php_memcached.c | 12 ++++++++++++ tests/set_encoding_key2.phpt | 28 ++-------------------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/php_memcached.c b/php_memcached.c index 134b4d7e..ec76f9bb 100644 --- a/php_memcached.c +++ b/php_memcached.c @@ -142,6 +142,7 @@ typedef struct { zend_bool is_persistent; zend_bool compression_enabled; + zend_bool encoding_enabled; zend_long serializer; zend_long compression_type; @@ -1227,6 +1228,7 @@ static PHP_METHOD(Memcached, __construct) memc_user_data->serializer = MEMC_G(serializer_type); memc_user_data->compression_type = MEMC_G(compression_type); memc_user_data->compression_enabled = 1; + memc_user_data->encoding_enabled = 0; memc_user_data->store_retry_count = MEMC_G(store_retry_count); memc_user_data->set_udf_flags = -1; memc_user_data->is_persistent = is_persistent; @@ -3281,11 +3283,21 @@ static PHP_METHOD(Memcached, setEncodingKey) MEMC_METHOD_FETCH_OBJECT; + // libmemcached < 1.0.18 cannot handle a change of encoding key. Warn about this and return false. +#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018 + if (memc_user_data->encoding_enabled) { + php_error_docref(NULL, E_WARNING, "libmemcached versions less than 1.0.18 cannot change encoding key"); + RETURN_FALSE; + } +#endif + status = memcached_set_encoding_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key)); if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } + + memc_user_data->encoding_enabled = 1; RETURN_TRUE; } /* }}} */ diff --git a/tests/set_encoding_key2.phpt b/tests/set_encoding_key2.phpt index 48dc7d87..40439283 100644 --- a/tests/set_encoding_key2.phpt +++ b/tests/set_encoding_key2.phpt @@ -19,24 +19,9 @@ var_dump ($m->get ($key)); echo "OK" . PHP_EOL; -# Change the encryption key. The old value will be inaccessible. +# libmemcached < 1.0.18 goes into a bad state when the encoding key is changed, +# so php-memcached warns and returns false when trying to change the key. var_dump ($m->setEncodingKey("World")); -var_dump ($m->get ($key)); - -echo "OK" . PHP_EOL; - -# Restore the original key to retrieve old values again. -var_dump ($m->setEncodingKey("Hello")); -var_dump ($m->get ($key)); - -echo "OK" . PHP_EOL; - -# Once the encoding key has changes we are no longer able to -# write new values in libmemcached < 1.0.18. -var_dump ($m->setEncodingKey("World")); -var_dump ($m->get ($key)); -var_dump ($m->set ($key, 'set using encoding')); -var_dump ($m->get ($key)); echo "OK" . PHP_EOL; @@ -46,14 +31,5 @@ bool(true) bool(true) string(18) "set using encoding" OK -bool(true) bool(false) OK -bool(true) -string(18) "set using encoding" -OK -bool(true) -bool(false) -bool(true) -string(18) "set using encoding" -OK From dc42f74c3524220616cd3b4f1c5840b3a866b455 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 20 Jan 2018 07:51:51 -0800 Subject: [PATCH 7/7] tests --- tests/set_encoding_key2.phpt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/set_encoding_key2.phpt b/tests/set_encoding_key2.phpt index 40439283..f8d21a16 100644 --- a/tests/set_encoding_key2.phpt +++ b/tests/set_encoding_key2.phpt @@ -26,10 +26,12 @@ var_dump ($m->setEncodingKey("World")); echo "OK" . PHP_EOL; ?> ---EXPECT-- +--EXPECTF-- bool(true) bool(true) string(18) "set using encoding" OK + +Warning: Memcached::setEncodingKey(): libmemcached versions less than 1.0.18 cannot change encoding key in %s on line %d bool(false) OK