1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 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
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
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)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)
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 || !Z_PTR(thisfilter->abstract)) {
50 /* Should never happen */
51 return PSFS_ERR_FATAL;
52 }
53
54 data = (php_mcrypt_filter_data *)(Z_PTR(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 = (int)(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);
84 php_stream_bucket_append(buckets_out, newbucket);
85
86 exit_status = PSFS_PASS_ON;
87
88 php_stream_bucket_unlink(bucket);
89 php_stream_bucket_delref(bucket);
90 } else {
91 /* Stream cipher */
92 bucket = php_stream_bucket_make_writeable(bucket);
93 if (data->encrypt) {
94 mcrypt_generic(data->module, bucket->buf, (int)bucket->buflen);
95 } else {
96 mdecrypt_generic(data->module, bucket->buf, (int)bucket->buflen);
97 }
98 php_stream_bucket_append(buckets_out, bucket);
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);
115 php_stream_bucket_append(buckets_out, newbucket);
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
php_mcrypt_filter_dtor(php_stream_filter * thisfilter)127 static void php_mcrypt_filter_dtor(php_stream_filter *thisfilter)
128 {
129 if (thisfilter && Z_PTR(thisfilter->abstract)) {
130 php_mcrypt_filter_data *data = (php_mcrypt_filter_data*) Z_PTR(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 */
php_mcrypt_filter_create(const char * filtername,zval * filterparams,int persistent)152 static php_stream_filter *php_mcrypt_filter_create(const char *filtername, zval *filterparams, int persistent)
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, E_WARNING, "Filter parameters for %s must be an array", filtername);
174 return NULL;
175 }
176
177 if ((tmpzval = zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("mode")))) {
178 if (Z_TYPE_P(tmpzval) == IS_STRING) {
179 mode = Z_STRVAL_P(tmpzval);
180 } else {
181 php_error_docref(NULL, E_WARNING, "mode is not a string, ignoring");
182 }
183 }
184
185 if ((tmpzval=zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("algorithms_dir")))) {
186 if (Z_TYPE_P(tmpzval) == IS_STRING) {
187 algo_dir = Z_STRVAL_P(tmpzval);
188 } else {
189 php_error_docref(NULL, E_WARNING, "algorithms_dir is not a string, ignoring");
190 }
191 }
192
193 if ((tmpzval=zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("modes_dir")))) {
194 if (Z_TYPE_P(tmpzval) == IS_STRING) {
195 mode_dir = Z_STRVAL_P(tmpzval);
196 } else {
197 php_error_docref(NULL, E_WARNING, "modes_dir is not a string, ignoring");
198 }
199 }
200
201 if ((tmpzval = zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("key"))) &&
202 Z_TYPE_P(tmpzval) == IS_STRING) {
203 key = Z_STRVAL_P(tmpzval);
204 key_len = (int)Z_STRLEN_P(tmpzval);
205 } else {
206 php_error_docref(NULL, E_WARNING, "key not specified or is not a string");
207 return NULL;
208 }
209
210 mcrypt_module = mcrypt_module_open((char *)cipher, algo_dir, mode, mode_dir);
211 if (mcrypt_module == MCRYPT_FAILED) {
212 php_error_docref(NULL, 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 (!(tmpzval = zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("iv"))) ||
222 Z_TYPE_P(tmpzval) != IS_STRING) {
223 php_error_docref(NULL, 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_P(tmpzval)) {
230 memcpy(iv, Z_STRVAL_P(tmpzval), iv_len);
231 } else {
232 memcpy(iv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
233 memset(iv + Z_STRLEN_P(tmpzval), 0, iv_len - Z_STRLEN_P(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, E_WARNING, "Key length incorrect");
242 break;
243 case -4:
244 php_error_docref(NULL, E_WARNING, "Memory allocation error");
245 break;
246 case -1:
247 default:
248 php_error_docref(NULL, 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 */
284