--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2010 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.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: Sara Golemon <pollita@php.net> |
+ +----------------------------------------------------------------------+
+
+ $Id$
+*/
+
+#include "php.h"
+
+#include "php_mcrypt_filter.h"
+#include "php_ini.h"
+#include <mcrypt.h>
+
+typedef struct _php_mcrypt_filter_data {
+ MCRYPT module;
+ char encrypt;
+ int blocksize;
+ char *block_buffer;
+ int block_used;
+ char persistent;
+} php_mcrypt_filter_data;
+
+static php_stream_filter_status_t php_mcrypt_filter(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ php_stream_bucket_brigade *buckets_in,
+ php_stream_bucket_brigade *buckets_out,
+ size_t *bytes_consumed,
+ int flags TSRMLS_DC)
+{
+ php_mcrypt_filter_data *data;
+ php_stream_bucket *bucket;
+ size_t consumed = 0;
+ php_stream_filter_status_t exit_status = PSFS_FEED_ME;
+
+ if (!thisfilter || !thisfilter->abstract) {
+ /* Should never happen */
+ return PSFS_ERR_FATAL;
+ }
+
+ data = (php_mcrypt_filter_data *)(thisfilter->abstract);
+ while(buckets_in->head) {
+ bucket = buckets_in->head;
+
+ if (bucket->buf_type == IS_UNICODE) {
+ /* inflation not allowed for unicode data */
+ return PSFS_ERR_FATAL;
+ }
+
+ consumed += bucket->buflen;
+
+ if (data->blocksize) {
+ /* Blockmode cipher */
+ char *outchunk;
+ int chunklen = bucket->buflen + data->block_used, n;
+ php_stream_bucket *newbucket;
+
+ outchunk = pemalloc(chunklen, data->persistent);
+ if (data->block_used) {
+ memcpy(outchunk, data->block_buffer, data->block_used);
+ }
+ memcpy(outchunk + data->block_used, bucket->buf.s, bucket->buflen);
+
+ for(n=0; (n + data->blocksize) <= chunklen; n += data->blocksize) {
+
+ if (data->encrypt) {
+ mcrypt_generic(data->module, outchunk + n, data->blocksize);
+ } else {
+ mdecrypt_generic(data->module, outchunk + n, data->blocksize);
+ }
+ }
+ data->block_used = chunklen - n;
+ memcpy(data->block_buffer, outchunk + n, data->block_used);
+
+ newbucket = php_stream_bucket_new(stream, outchunk, n, 1, data->persistent TSRMLS_CC);
+ php_stream_bucket_append(buckets_out, newbucket TSRMLS_CC);
+
+ exit_status = PSFS_PASS_ON;
+
+ php_stream_bucket_unlink(bucket TSRMLS_CC);
+ php_stream_bucket_delref(bucket TSRMLS_CC);
+ } else {
+ /* Stream cipher */
+ php_stream_bucket_make_writeable(bucket TSRMLS_CC);
+ if (data->encrypt) {
+ mcrypt_generic(data->module, bucket->buf.s, bucket->buflen);
+ } else {
+ mdecrypt_generic(data->module, bucket->buf.s, bucket->buflen);
+ }
+ php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
+
+ exit_status = PSFS_PASS_ON;
+ }
+ }
+
+ if ((flags & PSFS_FLAG_FLUSH_CLOSE) && data->blocksize && data->block_used) {
+ php_stream_bucket *newbucket;
+
+ memset(data->block_buffer + data->block_used, 0, data->blocksize - data->block_used);
+ if (data->encrypt) {
+ mcrypt_generic(data->module, data->block_buffer, data->blocksize);
+ } else {
+ mdecrypt_generic(data->module, data->block_buffer, data->blocksize);
+ }
+
+ newbucket = php_stream_bucket_new(stream, data->block_buffer, data->blocksize, 0, data->persistent TSRMLS_CC);
+ php_stream_bucket_append(buckets_out, newbucket TSRMLS_CC);
+
+ exit_status = PSFS_PASS_ON;
+ }
+
+ if (bytes_consumed) {
+ *bytes_consumed = consumed;
+ }
+
+ return exit_status;
+}
+
+static void php_mcrypt_filter_dtor(php_stream_filter *thisfilter TSRMLS_DC)
+{
+ if (thisfilter && thisfilter->abstract) {
+ php_mcrypt_filter_data *data = (php_mcrypt_filter_data*)thisfilter->abstract;
+
+ if (data->block_buffer) {
+ pefree(data->block_buffer, data->persistent);
+ }
+
+ mcrypt_generic_deinit(data->module);
+ mcrypt_module_close(data->module);
+
+ pefree(data, data->persistent);
+ }
+}
+
+static php_stream_filter_ops php_mcrypt_filter_ops = {
+ php_mcrypt_filter,
+ php_mcrypt_filter_dtor,
+ "mcrypt.*",
+ PSFO_FLAG_ACCEPTS_STRING | PSFO_FLAG_OUTPUTS_STRING
+};
+
+/* {{{ php_mcrypt_filter_create
+ * Instantiate mcrypt filter
+ */
+static php_stream_filter *php_mcrypt_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
+{
+ int encrypt = 1, iv_len, key_len, keyl, result;
+ const char *cipher = filtername + sizeof("mcrypt.") - 1;
+ zval **tmpzval;
+ MCRYPT mcrypt_module;
+ char *iv = NULL, *key = NULL;
+ char *algo_dir = INI_STR("mcrypt.algorithms_dir");
+ char *mode_dir = INI_STR("mcrypt.modes_dir");
+ char *mode = "cbc";
+ php_mcrypt_filter_data *data;
+
+ if (strncasecmp(filtername, "mdecrypt.", sizeof("mdecrypt.") - 1) == 0) {
+ encrypt = 0;
+ cipher += sizeof("de") - 1;
+ } else if (strncasecmp(filtername, "mcrypt.", sizeof("mcrypt.") - 1) != 0) {
+ /* Should never happen */
+ return NULL;
+ }
+
+ if (!filterparams || Z_TYPE_P(filterparams) != IS_ARRAY) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter parameters for %s must be an array", filtername);
+ return NULL;
+ }
+
+ if (zend_ascii_hash_find(HASH_OF(filterparams), "mode", sizeof("mode"), (void**)&tmpzval) == SUCCESS) {
+ if (Z_TYPE_PP(tmpzval) == IS_STRING) {
+ mode = Z_STRVAL_PP(tmpzval);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "mode is not a string, ignoring");
+ }
+ }
+
+ if (zend_ascii_hash_find(HASH_OF(filterparams), "algorithms_dir", sizeof("algorithms_dir"), (void**)&tmpzval) == SUCCESS) {
+ if (Z_TYPE_PP(tmpzval) == IS_STRING) {
+ algo_dir = Z_STRVAL_PP(tmpzval);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "algorithms_dir is not a string, ignoring");
+ }
+ }
+
+ if (zend_ascii_hash_find(HASH_OF(filterparams), "modes_dir", sizeof("modes_dir"), (void**)&tmpzval) == SUCCESS) {
+ if (Z_TYPE_PP(tmpzval) == IS_STRING) {
+ mode_dir = Z_STRVAL_PP(tmpzval);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "modes_dir is not a string, ignoring");
+ }
+ }
+
+ if (zend_ascii_hash_find(HASH_OF(filterparams), "key", sizeof("key"), (void**)&tmpzval) == SUCCESS &&
+ Z_TYPE_PP(tmpzval) == IS_STRING) {
+ key = Z_STRVAL_PP(tmpzval);
+ key_len = Z_STRLEN_PP(tmpzval);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "key not specified or is not a string");
+ return NULL;
+ }
+
+ mcrypt_module = mcrypt_module_open(cipher, algo_dir, mode, mode_dir);
+ if (mcrypt_module == MCRYPT_FAILED) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not open encryption module");
+ return NULL;
+ }
+ iv_len = mcrypt_enc_get_iv_size(mcrypt_module);
+ keyl = mcrypt_enc_get_key_size(mcrypt_module);
+ if (keyl < key_len) {
+ key_len = keyl;
+ }
+
+ if (zend_ascii_hash_find(HASH_OF(filterparams), "iv", sizeof("iv"), (void**) &tmpzval) == FAILURE ||
+ Z_TYPE_PP(tmpzval) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter parameter[iv] not provided or not of type: string");
+ mcrypt_module_close(mcrypt_module);
+ return NULL;
+ }
+
+ iv = emalloc(iv_len + 1);
+ if (iv_len <= Z_STRLEN_PP(tmpzval)) {
+ memcpy(iv, Z_STRVAL_PP(tmpzval), iv_len);
+ } else {
+ memcpy(iv, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval));
+ memset(iv + Z_STRLEN_PP(tmpzval), 0, iv_len - Z_STRLEN_PP(tmpzval));
+ }
+
+ result = mcrypt_generic_init(mcrypt_module, key, key_len, iv);
+ efree(iv);
+ if (result < 0) {
+ switch (result) {
+ case -3:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key length incorrect");
+ break;
+ case -4:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Memory allocation error");
+ break;
+ case -1:
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown error");
+ break;
+ }
+ mcrypt_module_close(mcrypt_module);
+ return NULL;
+ }
+
+ data = pemalloc(sizeof(php_mcrypt_filter_data), persistent);
+ data->module = mcrypt_module;
+ data->encrypt = encrypt;
+ if (mcrypt_enc_is_block_mode(mcrypt_module)) {
+ data->blocksize = mcrypt_enc_get_block_size(mcrypt_module);
+ data->block_buffer = pemalloc(data->blocksize, persistent);
+ } else {
+ data->blocksize = 0;
+ data->block_buffer = NULL;
+ }
+ data->block_used = 0;
+ data->persistent = persistent;
+
+ return php_stream_filter_alloc(&php_mcrypt_filter_ops, data, persistent);
+}
+/* }}} */
+
+php_stream_filter_factory php_mcrypt_filter_factory = {
+ php_mcrypt_filter_create
+};
+
+/*
+ * 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
+ */