root/ext/mcrypt/mcrypt_filter.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_mcrypt_filter
  2. php_mcrypt_filter_dtor
  3. php_mcrypt_filter_create

/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2013 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;

                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, 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, bucket->buflen);
                        } else {
                                mdecrypt_generic(data->module, bucket->buf, 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.*"
};

/* {{{ 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_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_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_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_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_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
 */

/* [<][>][^][v][top][bottom][index][help] */