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..a8ece289 100644 --- a/package.xml +++ b/package.xml @@ -159,6 +159,8 @@ Fixes + + diff --git a/php_memcached.c b/php_memcached.c index cc6456be..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; @@ -3265,6 +3267,42 @@ 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; + + // 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; +} +/* }}} */ +#endif /* HAVE_MEMCACHED_SET_ENCODING_KEY */ + /* {{{ Memcached::getResultCode() Returns the result code from the last operation */ static PHP_METHOD(Memcached, getResultCode) @@ -4034,6 +4072,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 +4177,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 +4329,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..72d96daa --- /dev/null +++ b/tests/set_encoding_key.phpt @@ -0,0 +1,59 @@ +--TEST-- +Test libmemcached encryption +--SKIPIF-- + +--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; + +# 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)); + +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 +bool(true) +bool(false) +bool(true) +string(18) "set using encoding" +OK diff --git a/tests/set_encoding_key2.phpt b/tests/set_encoding_key2.phpt new file mode 100644 index 00000000..f8d21a16 --- /dev/null +++ b/tests/set_encoding_key2.phpt @@ -0,0 +1,37 @@ +--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; + +# 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")); + +echo "OK" . PHP_EOL; + +?> +--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