1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Author: Sara Golemon <pollita@php.net> | 16 +----------------------------------------------------------------------+ 17 18 $Id$ 19*/ 20 21#include "php.h" 22 23#include "php_mcrypt_filter.h" 24#include "php_ini.h" 25#include <mcrypt.h> 26 27typedef struct _php_mcrypt_filter_data { 28 MCRYPT module; 29 char encrypt; 30 int blocksize; 31 char *block_buffer; 32 int block_used; 33 char persistent; 34} php_mcrypt_filter_data; 35 36static php_stream_filter_status_t php_mcrypt_filter( 37 php_stream *stream, 38 php_stream_filter *thisfilter, 39 php_stream_bucket_brigade *buckets_in, 40 php_stream_bucket_brigade *buckets_out, 41 size_t *bytes_consumed, 42 int flags TSRMLS_DC) 43{ 44 php_mcrypt_filter_data *data; 45 php_stream_bucket *bucket; 46 size_t consumed = 0; 47 php_stream_filter_status_t exit_status = PSFS_FEED_ME; 48 49 if (!thisfilter || !thisfilter->abstract) { 50 /* Should never happen */ 51 return PSFS_ERR_FATAL; 52 } 53 54 data = (php_mcrypt_filter_data *)(thisfilter->abstract); 55 while(buckets_in->head) { 56 bucket = buckets_in->head; 57 58 consumed += bucket->buflen; 59 60 if (data->blocksize) { 61 /* Blockmode cipher */ 62 char *outchunk; 63 int chunklen = bucket->buflen + data->block_used, n; 64 php_stream_bucket *newbucket; 65 66 outchunk = pemalloc(chunklen, data->persistent); 67 if (data->block_used) { 68 memcpy(outchunk, data->block_buffer, data->block_used); 69 } 70 memcpy(outchunk + data->block_used, bucket->buf, bucket->buflen); 71 72 for(n=0; (n + data->blocksize) <= chunklen; n += data->blocksize) { 73 74 if (data->encrypt) { 75 mcrypt_generic(data->module, outchunk + n, data->blocksize); 76 } else { 77 mdecrypt_generic(data->module, outchunk + n, data->blocksize); 78 } 79 } 80 data->block_used = chunklen - n; 81 memcpy(data->block_buffer, outchunk + n, data->block_used); 82 83 newbucket = php_stream_bucket_new(stream, outchunk, n, 1, data->persistent TSRMLS_CC); 84 php_stream_bucket_append(buckets_out, newbucket TSRMLS_CC); 85 86 exit_status = PSFS_PASS_ON; 87 88 php_stream_bucket_unlink(bucket TSRMLS_CC); 89 php_stream_bucket_delref(bucket TSRMLS_CC); 90 } else { 91 /* Stream cipher */ 92 php_stream_bucket_make_writeable(bucket TSRMLS_CC); 93 if (data->encrypt) { 94 mcrypt_generic(data->module, bucket->buf, bucket->buflen); 95 } else { 96 mdecrypt_generic(data->module, bucket->buf, bucket->buflen); 97 } 98 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); 99 100 exit_status = PSFS_PASS_ON; 101 } 102 } 103 104 if ((flags & PSFS_FLAG_FLUSH_CLOSE) && data->blocksize && data->block_used) { 105 php_stream_bucket *newbucket; 106 107 memset(data->block_buffer + data->block_used, 0, data->blocksize - data->block_used); 108 if (data->encrypt) { 109 mcrypt_generic(data->module, data->block_buffer, data->blocksize); 110 } else { 111 mdecrypt_generic(data->module, data->block_buffer, data->blocksize); 112 } 113 114 newbucket = php_stream_bucket_new(stream, data->block_buffer, data->blocksize, 0, data->persistent TSRMLS_CC); 115 php_stream_bucket_append(buckets_out, newbucket TSRMLS_CC); 116 117 exit_status = PSFS_PASS_ON; 118 } 119 120 if (bytes_consumed) { 121 *bytes_consumed = consumed; 122 } 123 124 return exit_status; 125} 126 127static void php_mcrypt_filter_dtor(php_stream_filter *thisfilter TSRMLS_DC) 128{ 129 if (thisfilter && thisfilter->abstract) { 130 php_mcrypt_filter_data *data = (php_mcrypt_filter_data*)thisfilter->abstract; 131 132 if (data->block_buffer) { 133 pefree(data->block_buffer, data->persistent); 134 } 135 136 mcrypt_generic_deinit(data->module); 137 mcrypt_module_close(data->module); 138 139 pefree(data, data->persistent); 140 } 141} 142 143static php_stream_filter_ops php_mcrypt_filter_ops = { 144 php_mcrypt_filter, 145 php_mcrypt_filter_dtor, 146 "mcrypt.*" 147}; 148 149/* {{{ php_mcrypt_filter_create 150 * Instantiate mcrypt filter 151 */ 152static php_stream_filter *php_mcrypt_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) 153{ 154 int encrypt = 1, iv_len, key_len, keyl, result; 155 const char *cipher = filtername + sizeof("mcrypt.") - 1; 156 zval **tmpzval; 157 MCRYPT mcrypt_module; 158 char *iv = NULL, *key = NULL; 159 char *algo_dir = INI_STR("mcrypt.algorithms_dir"); 160 char *mode_dir = INI_STR("mcrypt.modes_dir"); 161 char *mode = "cbc"; 162 php_mcrypt_filter_data *data; 163 164 if (strncasecmp(filtername, "mdecrypt.", sizeof("mdecrypt.") - 1) == 0) { 165 encrypt = 0; 166 cipher += sizeof("de") - 1; 167 } else if (strncasecmp(filtername, "mcrypt.", sizeof("mcrypt.") - 1) != 0) { 168 /* Should never happen */ 169 return NULL; 170 } 171 172 if (!filterparams || Z_TYPE_P(filterparams) != IS_ARRAY) { 173 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter parameters for %s must be an array", filtername); 174 return NULL; 175 } 176 177 if (zend_hash_find(HASH_OF(filterparams), "mode", sizeof("mode"), (void**)&tmpzval) == SUCCESS) { 178 if (Z_TYPE_PP(tmpzval) == IS_STRING) { 179 mode = Z_STRVAL_PP(tmpzval); 180 } else { 181 php_error_docref(NULL TSRMLS_CC, E_WARNING, "mode is not a string, ignoring"); 182 } 183 } 184 185 if (zend_hash_find(HASH_OF(filterparams), "algorithms_dir", sizeof("algorithms_dir"), (void**)&tmpzval) == SUCCESS) { 186 if (Z_TYPE_PP(tmpzval) == IS_STRING) { 187 algo_dir = Z_STRVAL_PP(tmpzval); 188 } else { 189 php_error_docref(NULL TSRMLS_CC, E_WARNING, "algorithms_dir is not a string, ignoring"); 190 } 191 } 192 193 if (zend_hash_find(HASH_OF(filterparams), "modes_dir", sizeof("modes_dir"), (void**)&tmpzval) == SUCCESS) { 194 if (Z_TYPE_PP(tmpzval) == IS_STRING) { 195 mode_dir = Z_STRVAL_PP(tmpzval); 196 } else { 197 php_error_docref(NULL TSRMLS_CC, E_WARNING, "modes_dir is not a string, ignoring"); 198 } 199 } 200 201 if (zend_hash_find(HASH_OF(filterparams), "key", sizeof("key"), (void**)&tmpzval) == SUCCESS && 202 Z_TYPE_PP(tmpzval) == IS_STRING) { 203 key = Z_STRVAL_PP(tmpzval); 204 key_len = Z_STRLEN_PP(tmpzval); 205 } else { 206 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key not specified or is not a string"); 207 return NULL; 208 } 209 210 mcrypt_module = mcrypt_module_open(cipher, algo_dir, mode, mode_dir); 211 if (mcrypt_module == MCRYPT_FAILED) { 212 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not open encryption module"); 213 return NULL; 214 } 215 iv_len = mcrypt_enc_get_iv_size(mcrypt_module); 216 keyl = mcrypt_enc_get_key_size(mcrypt_module); 217 if (keyl < key_len) { 218 key_len = keyl; 219 } 220 221 if (zend_hash_find(HASH_OF(filterparams), "iv", sizeof("iv"), (void**) &tmpzval) == FAILURE || 222 Z_TYPE_PP(tmpzval) != IS_STRING) { 223 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter parameter[iv] not provided or not of type: string"); 224 mcrypt_module_close(mcrypt_module); 225 return NULL; 226 } 227 228 iv = emalloc(iv_len + 1); 229 if (iv_len <= Z_STRLEN_PP(tmpzval)) { 230 memcpy(iv, Z_STRVAL_PP(tmpzval), iv_len); 231 } else { 232 memcpy(iv, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval)); 233 memset(iv + Z_STRLEN_PP(tmpzval), 0, iv_len - Z_STRLEN_PP(tmpzval)); 234 } 235 236 result = mcrypt_generic_init(mcrypt_module, key, key_len, iv); 237 efree(iv); 238 if (result < 0) { 239 switch (result) { 240 case -3: 241 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key length incorrect"); 242 break; 243 case -4: 244 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Memory allocation error"); 245 break; 246 case -1: 247 default: 248 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown error"); 249 break; 250 } 251 mcrypt_module_close(mcrypt_module); 252 return NULL; 253 } 254 255 data = pemalloc(sizeof(php_mcrypt_filter_data), persistent); 256 data->module = mcrypt_module; 257 data->encrypt = encrypt; 258 if (mcrypt_enc_is_block_mode(mcrypt_module)) { 259 data->blocksize = mcrypt_enc_get_block_size(mcrypt_module); 260 data->block_buffer = pemalloc(data->blocksize, persistent); 261 } else { 262 data->blocksize = 0; 263 data->block_buffer = NULL; 264 } 265 data->block_used = 0; 266 data->persistent = persistent; 267 268 return php_stream_filter_alloc(&php_mcrypt_filter_ops, data, persistent); 269} 270/* }}} */ 271 272php_stream_filter_factory php_mcrypt_filter_factory = { 273 php_mcrypt_filter_create 274}; 275 276/* 277 * Local variables: 278 * tab-width: 4 279 * c-basic-offset: 4 280 * End: 281 * vim600: noet sw=4 ts=4 fdm=marker 282 * vim<600: noet sw=4 ts=4 283 */ 284