xref: /PHP-7.1/ext/mcrypt/mcrypt_filter.c (revision ccd4716e)
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