1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2014 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: mcrypt_filter.c,v 1.1.1.4 2014/06/15 20:03:51 misho Exp $
19: */
20:
21: #include "php.h"
22:
23: #include "php_mcrypt_filter.h"
24: #include "php_ini.h"
25: #include <mcrypt.h>
26:
27: typedef 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:
36: static 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:
127: static 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:
143: static 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: */
152: static 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:
272: php_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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>