1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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 php_error_docref(NULL, E_DEPRECATED, "mcrypt and mdecrypt stream filters have been deprecated");
165
166 if (strncasecmp(filtername, "mdecrypt.", sizeof("mdecrypt.") - 1) == 0) {
167 encrypt = 0;
168 cipher += sizeof("de") - 1;
169 } else if (strncasecmp(filtername, "mcrypt.", sizeof("mcrypt.") - 1) != 0) {
170 /* Should never happen */
171 return NULL;
172 }
173
174 if (!filterparams || Z_TYPE_P(filterparams) != IS_ARRAY) {
175 php_error_docref(NULL, E_WARNING, "Filter parameters for %s must be an array", filtername);
176 return NULL;
177 }
178
179 if ((tmpzval = zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("mode")))) {
180 if (Z_TYPE_P(tmpzval) == IS_STRING) {
181 mode = Z_STRVAL_P(tmpzval);
182 } else {
183 php_error_docref(NULL, E_WARNING, "mode is not a string, ignoring");
184 }
185 }
186
187 if ((tmpzval=zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("algorithms_dir")))) {
188 if (Z_TYPE_P(tmpzval) == IS_STRING) {
189 algo_dir = Z_STRVAL_P(tmpzval);
190 } else {
191 php_error_docref(NULL, E_WARNING, "algorithms_dir is not a string, ignoring");
192 }
193 }
194
195 if ((tmpzval=zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("modes_dir")))) {
196 if (Z_TYPE_P(tmpzval) == IS_STRING) {
197 mode_dir = Z_STRVAL_P(tmpzval);
198 } else {
199 php_error_docref(NULL, E_WARNING, "modes_dir is not a string, ignoring");
200 }
201 }
202
203 if ((tmpzval = zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("key"))) &&
204 Z_TYPE_P(tmpzval) == IS_STRING) {
205 key = Z_STRVAL_P(tmpzval);
206 key_len = (int)Z_STRLEN_P(tmpzval);
207 } else {
208 php_error_docref(NULL, E_WARNING, "key not specified or is not a string");
209 return NULL;
210 }
211
212 mcrypt_module = mcrypt_module_open((char *)cipher, algo_dir, mode, mode_dir);
213 if (mcrypt_module == MCRYPT_FAILED) {
214 php_error_docref(NULL, E_WARNING, "Could not open encryption module");
215 return NULL;
216 }
217 iv_len = mcrypt_enc_get_iv_size(mcrypt_module);
218 keyl = mcrypt_enc_get_key_size(mcrypt_module);
219 if (keyl < key_len) {
220 key_len = keyl;
221 }
222
223 if (!(tmpzval = zend_hash_str_find(Z_ARRVAL_P(filterparams), ZEND_STRL("iv"))) ||
224 Z_TYPE_P(tmpzval) != IS_STRING) {
225 php_error_docref(NULL, E_WARNING, "Filter parameter[iv] not provided or not of type: string");
226 mcrypt_module_close(mcrypt_module);
227 return NULL;
228 }
229
230 iv = emalloc(iv_len + 1);
231 if ((size_t)iv_len <= Z_STRLEN_P(tmpzval)) {
232 memcpy(iv, Z_STRVAL_P(tmpzval), iv_len);
233 } else {
234 memcpy(iv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
235 memset(iv + Z_STRLEN_P(tmpzval), 0, iv_len - Z_STRLEN_P(tmpzval));
236 }
237
238 result = mcrypt_generic_init(mcrypt_module, key, key_len, iv);
239 efree(iv);
240 if (result < 0) {
241 switch (result) {
242 case -3:
243 php_error_docref(NULL, E_WARNING, "Key length incorrect");
244 break;
245 case -4:
246 php_error_docref(NULL, E_WARNING, "Memory allocation error");
247 break;
248 case -1:
249 default:
250 php_error_docref(NULL, E_WARNING, "Unknown error");
251 break;
252 }
253 mcrypt_module_close(mcrypt_module);
254 return NULL;
255 }
256
257 data = pemalloc(sizeof(php_mcrypt_filter_data), persistent);
258 data->module = mcrypt_module;
259 data->encrypt = encrypt;
260 if (mcrypt_enc_is_block_mode(mcrypt_module)) {
261 data->blocksize = mcrypt_enc_get_block_size(mcrypt_module);
262 data->block_buffer = pemalloc(data->blocksize, persistent);
263 } else {
264 data->blocksize = 0;
265 data->block_buffer = NULL;
266 }
267 data->block_used = 0;
268 data->persistent = persistent;
269
270 return php_stream_filter_alloc(&php_mcrypt_filter_ops, data, persistent);
271 }
272 /* }}} */
273
274 php_stream_filter_factory php_mcrypt_filter_factory = {
275 php_mcrypt_filter_create
276 };
277
278 /*
279 * Local variables:
280 * tab-width: 4
281 * c-basic-offset: 4
282 * End:
283 * vim600: noet sw=4 ts=4 fdm=marker
284 * vim<600: noet sw=4 ts=4
285 */
286