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