Skip to content

Add support for libmemcached encryption #381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 20, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions config.m4
Original file line number Diff line number Diff line change
@@ -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 <libmemcached/memcached.h> ],
[ 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
4 changes: 4 additions & 0 deletions memcached-api.php
Original file line number Diff line number Diff line change
@@ -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 {
2 changes: 2 additions & 0 deletions package.xml
Original file line number Diff line number Diff line change
@@ -159,6 +159,8 @@ Fixes
<file role='test' name='testdata.res'/>
<file role='test' name='config.inc'/>
<file role='test' name='sasl_basic.phpt'/>
<file role='test' name='set_encoding_key.phpt'/>
<file role='test' name='set_encoding_key2.phpt'/>
<file role='test' name='getserverbykey.phpt'/>
<file role='test' name='gh_155.phpt'/>
<file role='test' name='get_flags.phpt'/>
56 changes: 56 additions & 0 deletions php_memcached.c
Original file line number Diff line number Diff line change
@@ -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
59 changes: 59 additions & 0 deletions tests/set_encoding_key.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
--TEST--
Test libmemcached encryption
--SKIPIF--
<?php
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--
<?php
include dirname (__FILE__) . '/config.inc';
$m = memc_get_instance ();
$key = uniqid ('encoding_test_');

var_dump ($m->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
37 changes: 37 additions & 0 deletions tests/set_encoding_key2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Test libmemcached encryption
--SKIPIF--
<?php
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 lower than 1.0.18");
?>
--FILE--
<?php
include dirname (__FILE__) . '/config.inc';
$m = memc_get_instance ();
$key = uniqid ('encoding_test_');

var_dump ($m->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