xref: /PHP-8.0/ext/openssl/openssl.c (revision 255e08ac)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Stig Venaas <venaas@php.net>                                |
14    |          Wez Furlong <wez@thebrainroom.com>                          |
15    |          Sascha Kettler <kettler@gmx.net>                            |
16    |          Pierre-Alain Joye <pierre@php.net>                          |
17    |          Marc Delling <delling@silpion.de> (PKCS12 functions)        |
18    |          Jakub Zelenka <bukka@php.net>                               |
19    |          Eliot Lear <lear@ofcourseimright.com>                       |
20    +----------------------------------------------------------------------+
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "php.h"
28 #include "php_ini.h"
29 #include "php_openssl.h"
30 #include "zend_exceptions.h"
31 #include "Zend/zend_interfaces.h"
32 
33 /* PHP Includes */
34 #include "ext/standard/file.h"
35 #include "ext/standard/info.h"
36 #include "ext/standard/php_fopen_wrappers.h"
37 #include "ext/standard/md5.h"
38 #include "ext/standard/base64.h"
39 #ifdef PHP_WIN32
40 # include "win32/winutil.h"
41 #endif
42 
43 /* OpenSSL includes */
44 #include <openssl/evp.h>
45 #include <openssl/bn.h>
46 #include <openssl/rsa.h>
47 #include <openssl/dsa.h>
48 #include <openssl/dh.h>
49 #include <openssl/x509.h>
50 #include <openssl/x509v3.h>
51 #include <openssl/crypto.h>
52 #include <openssl/pem.h>
53 #include <openssl/err.h>
54 #include <openssl/conf.h>
55 #include <openssl/rand.h>
56 #include <openssl/ssl.h>
57 #include <openssl/pkcs12.h>
58 #include <openssl/cms.h>
59 
60 /* Common */
61 #include <time.h>
62 
63 #if (defined(PHP_WIN32) && defined(_MSC_VER) && _MSC_VER >= 1900)
64 #define timezone _timezone	/* timezone is called _timezone in LibC */
65 #endif
66 
67 #define MIN_KEY_LENGTH		384
68 
69 #define OPENSSL_ALGO_SHA1 	1
70 #define OPENSSL_ALGO_MD5	2
71 #ifndef OPENSSL_NO_MD4
72 #define OPENSSL_ALGO_MD4	3
73 #endif
74 #ifndef OPENSSL_NO_MD2
75 #define OPENSSL_ALGO_MD2	4
76 #endif
77 #if PHP_OPENSSL_API_VERSION < 0x10100
78 #define OPENSSL_ALGO_DSS1	5
79 #endif
80 #define OPENSSL_ALGO_SHA224 6
81 #define OPENSSL_ALGO_SHA256 7
82 #define OPENSSL_ALGO_SHA384 8
83 #define OPENSSL_ALGO_SHA512 9
84 #ifndef OPENSSL_NO_RMD160
85 #define OPENSSL_ALGO_RMD160 10
86 #endif
87 #define DEBUG_SMIME	0
88 
89 #if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC)
90 #define HAVE_EVP_PKEY_EC 1
91 #endif
92 
93 #include "openssl_arginfo.h"
94 
95 ZEND_DECLARE_MODULE_GLOBALS(openssl)
96 
97 /* FIXME: Use the openssl constants instead of
98  * enum. It is now impossible to match real values
99  * against php constants. Also sorry to break the
100  * enum principles here, BC...
101  */
102 enum php_openssl_key_type {
103 	OPENSSL_KEYTYPE_RSA,
104 	OPENSSL_KEYTYPE_DSA,
105 	OPENSSL_KEYTYPE_DH,
106 	OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA,
107 #ifdef HAVE_EVP_PKEY_EC
108 	OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1
109 #endif
110 };
111 
112 enum php_openssl_cipher_type {
113 	PHP_OPENSSL_CIPHER_RC2_40,
114 	PHP_OPENSSL_CIPHER_RC2_128,
115 	PHP_OPENSSL_CIPHER_RC2_64,
116 	PHP_OPENSSL_CIPHER_DES,
117 	PHP_OPENSSL_CIPHER_3DES,
118 	PHP_OPENSSL_CIPHER_AES_128_CBC,
119 	PHP_OPENSSL_CIPHER_AES_192_CBC,
120 	PHP_OPENSSL_CIPHER_AES_256_CBC,
121 
122 	PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_RC2_40
123 };
124 
125 /* Add some encoding rules.  This is normally handled through filters
126  * in the OpenSSL code, but we will do that part as if we were one
127  * of the OpenSSL binaries along the lines of -outform {DER|CMS|PEM}
128  */
129 enum php_openssl_encoding {
130 	ENCODING_DER,
131 	ENCODING_SMIME,
132 	ENCODING_PEM,
133 };
134 
135 /* OpenSSLCertificate class */
136 
137 zend_class_entry *php_openssl_certificate_ce;
138 
139 static zend_object_handlers php_openssl_certificate_object_handlers;
140 
php_openssl_certificate_create_object(zend_class_entry * class_type)141 static zend_object *php_openssl_certificate_create_object(zend_class_entry *class_type) {
142 	php_openssl_certificate_object *intern = zend_object_alloc(sizeof(php_openssl_certificate_object), class_type);
143 
144 	zend_object_std_init(&intern->std, class_type);
145 	object_properties_init(&intern->std, class_type);
146 	intern->std.handlers = &php_openssl_certificate_object_handlers;
147 
148 	return &intern->std;
149 }
150 
php_openssl_certificate_get_constructor(zend_object * object)151 static zend_function *php_openssl_certificate_get_constructor(zend_object *object) {
152 	zend_throw_error(NULL, "Cannot directly construct OpenSSLCertificate, use openssl_x509_read() instead");
153 	return NULL;
154 }
155 
php_openssl_certificate_free_obj(zend_object * object)156 static void php_openssl_certificate_free_obj(zend_object *object)
157 {
158 	php_openssl_certificate_object *x509_object = php_openssl_certificate_from_obj(object);
159 
160 	X509_free(x509_object->x509);
161 	zend_object_std_dtor(&x509_object->std);
162 }
163 
164 /* OpenSSLCertificateSigningRequest class */
165 
166 typedef struct _php_openssl_x509_request_object {
167 	X509_REQ *csr;
168 	zend_object std;
169 } php_openssl_request_object;
170 
171 zend_class_entry *php_openssl_request_ce;
172 
php_openssl_request_from_obj(zend_object * obj)173 static inline php_openssl_request_object *php_openssl_request_from_obj(zend_object *obj) {
174 	return (php_openssl_request_object *)((char *)(obj) - XtOffsetOf(php_openssl_request_object, std));
175 }
176 
177 #define Z_OPENSSL_REQUEST_P(zv) php_openssl_request_from_obj(Z_OBJ_P(zv))
178 
179 static zend_object_handlers php_openssl_request_object_handlers;
180 
php_openssl_request_create_object(zend_class_entry * class_type)181 static zend_object *php_openssl_request_create_object(zend_class_entry *class_type) {
182 	php_openssl_request_object *intern = zend_object_alloc(sizeof(php_openssl_request_object), class_type);
183 
184 	zend_object_std_init(&intern->std, class_type);
185 	object_properties_init(&intern->std, class_type);
186 	intern->std.handlers = &php_openssl_request_object_handlers;
187 
188 	return &intern->std;
189 }
190 
php_openssl_request_get_constructor(zend_object * object)191 static zend_function *php_openssl_request_get_constructor(zend_object *object) {
192 	zend_throw_error(NULL, "Cannot directly construct OpenSSLCertificateSigningRequest, use openssl_csr_new() instead");
193 	return NULL;
194 }
195 
php_openssl_request_free_obj(zend_object * object)196 static void php_openssl_request_free_obj(zend_object *object)
197 {
198 	php_openssl_request_object *x509_request = php_openssl_request_from_obj(object);
199 
200 	X509_REQ_free(x509_request->csr);
201 	zend_object_std_dtor(&x509_request->std);
202 }
203 
204 /* OpenSSLAsymmetricKey class */
205 
206 typedef struct _php_openssl_pkey_object {
207 	EVP_PKEY *pkey;
208 	zend_object std;
209 } php_openssl_pkey_object;
210 
211 zend_class_entry *php_openssl_pkey_ce;
212 
php_openssl_pkey_from_obj(zend_object * obj)213 static inline php_openssl_pkey_object *php_openssl_pkey_from_obj(zend_object *obj) {
214 	return (php_openssl_pkey_object *)((char *)(obj) - XtOffsetOf(php_openssl_pkey_object, std));
215 }
216 
217 #define Z_OPENSSL_PKEY_P(zv) php_openssl_pkey_from_obj(Z_OBJ_P(zv))
218 
219 static zend_object_handlers php_openssl_pkey_object_handlers;
220 
php_openssl_pkey_create_object(zend_class_entry * class_type)221 static zend_object *php_openssl_pkey_create_object(zend_class_entry *class_type) {
222 	php_openssl_pkey_object *intern = zend_object_alloc(sizeof(php_openssl_pkey_object), class_type);
223 
224 	zend_object_std_init(&intern->std, class_type);
225 	object_properties_init(&intern->std, class_type);
226 	intern->std.handlers = &php_openssl_pkey_object_handlers;
227 
228 	return &intern->std;
229 }
230 
php_openssl_pkey_get_constructor(zend_object * object)231 static zend_function *php_openssl_pkey_get_constructor(zend_object *object) {
232 	zend_throw_error(NULL, "Cannot directly construct OpenSSLAsymmetricKey, use openssl_pkey_new() instead");
233 	return NULL;
234 }
235 
php_openssl_pkey_free_obj(zend_object * object)236 static void php_openssl_pkey_free_obj(zend_object *object)
237 {
238 	php_openssl_pkey_object *key_object = php_openssl_pkey_from_obj(object);
239 
240 	EVP_PKEY_free(key_object->pkey);
241 	zend_object_std_dtor(&key_object->std);
242 }
243 
244 /* {{{ openssl_module_entry */
245 zend_module_entry openssl_module_entry = {
246 	STANDARD_MODULE_HEADER,
247 	"openssl",
248 	ext_functions,
249 	PHP_MINIT(openssl),
250 	PHP_MSHUTDOWN(openssl),
251 	NULL,
252 	NULL,
253 	PHP_MINFO(openssl),
254 	PHP_OPENSSL_VERSION,
255 	PHP_MODULE_GLOBALS(openssl),
256 	PHP_GINIT(openssl),
257 	PHP_GSHUTDOWN(openssl),
258 	NULL,
259 	STANDARD_MODULE_PROPERTIES_EX
260 };
261 /* }}} */
262 
263 #ifdef COMPILE_DL_OPENSSL
ZEND_GET_MODULE(openssl)264 ZEND_GET_MODULE(openssl)
265 #endif
266 
267 /* {{{ OpenSSL compatibility functions and macros */
268 #if PHP_OPENSSL_API_VERSION < 0x10100
269 
270 #define EVP_PKEY_get0_RSA(_pkey) _pkey->pkey.rsa
271 #define EVP_PKEY_get0_DH(_pkey) _pkey->pkey.dh
272 #define EVP_PKEY_get0_DSA(_pkey) _pkey->pkey.dsa
273 #define EVP_PKEY_get0_EC_KEY(_pkey) _pkey->pkey.ec
274 
275 static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
276 {
277 	r->n = n;
278 	r->e = e;
279 	r->d = d;
280 
281 	return 1;
282 }
283 
RSA_set0_factors(RSA * r,BIGNUM * p,BIGNUM * q)284 static int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
285 {
286 	r->p = p;
287 	r->q = q;
288 
289 	return 1;
290 }
291 
RSA_set0_crt_params(RSA * r,BIGNUM * dmp1,BIGNUM * dmq1,BIGNUM * iqmp)292 static int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
293 {
294 	r->dmp1 = dmp1;
295 	r->dmq1 = dmq1;
296 	r->iqmp = iqmp;
297 
298 	return 1;
299 }
300 
RSA_get0_key(const RSA * r,const BIGNUM ** n,const BIGNUM ** e,const BIGNUM ** d)301 static void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
302 {
303 	*n = r->n;
304 	*e = r->e;
305 	*d = r->d;
306 }
307 
RSA_get0_factors(const RSA * r,const BIGNUM ** p,const BIGNUM ** q)308 static void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
309 {
310 	*p = r->p;
311 	*q = r->q;
312 }
313 
RSA_get0_crt_params(const RSA * r,const BIGNUM ** dmp1,const BIGNUM ** dmq1,const BIGNUM ** iqmp)314 static void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp)
315 {
316 	*dmp1 = r->dmp1;
317 	*dmq1 = r->dmq1;
318 	*iqmp = r->iqmp;
319 }
320 
DH_get0_pqg(const DH * dh,const BIGNUM ** p,const BIGNUM ** q,const BIGNUM ** g)321 static void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
322 {
323 	*p = dh->p;
324 	*q = dh->q;
325 	*g = dh->g;
326 }
327 
DH_set0_pqg(DH * dh,BIGNUM * p,BIGNUM * q,BIGNUM * g)328 static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
329 {
330 	dh->p = p;
331 	dh->q = q;
332 	dh->g = g;
333 
334 	return 1;
335 }
336 
DH_get0_key(const DH * dh,const BIGNUM ** pub_key,const BIGNUM ** priv_key)337 static void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
338 {
339 	*pub_key = dh->pub_key;
340 	*priv_key = dh->priv_key;
341 }
342 
DH_set0_key(DH * dh,BIGNUM * pub_key,BIGNUM * priv_key)343 static int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
344 {
345 	dh->pub_key = pub_key;
346 	dh->priv_key = priv_key;
347 
348 	return 1;
349 }
350 
DSA_get0_pqg(const DSA * d,const BIGNUM ** p,const BIGNUM ** q,const BIGNUM ** g)351 static void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
352 {
353 	*p = d->p;
354 	*q = d->q;
355 	*g = d->g;
356 }
357 
DSA_set0_pqg(DSA * d,BIGNUM * p,BIGNUM * q,BIGNUM * g)358 int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
359 {
360 	d->p = p;
361 	d->q = q;
362 	d->g = g;
363 
364 	return 1;
365 }
366 
DSA_get0_key(const DSA * d,const BIGNUM ** pub_key,const BIGNUM ** priv_key)367 static void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
368 {
369 	*pub_key = d->pub_key;
370 	*priv_key = d->priv_key;
371 }
372 
DSA_set0_key(DSA * d,BIGNUM * pub_key,BIGNUM * priv_key)373 int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
374 {
375 	d->pub_key = pub_key;
376 	d->priv_key = priv_key;
377 
378 	return 1;
379 }
380 
ASN1_STRING_get0_data(const ASN1_STRING * asn1)381 static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1)
382 {
383 	return M_ASN1_STRING_data(asn1);
384 }
385 
EVP_PKEY_up_ref(EVP_PKEY * pkey)386 static int EVP_PKEY_up_ref(EVP_PKEY *pkey)
387 {
388 	return CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
389 }
390 
391 #if PHP_OPENSSL_API_VERSION < 0x10002
392 
X509_get_signature_nid(const X509 * x)393 static int X509_get_signature_nid(const X509 *x)
394 {
395 	return OBJ_obj2nid(x->sig_alg->algorithm);
396 }
397 
398 #endif
399 
400 #define OpenSSL_version		SSLeay_version
401 #define OPENSSL_VERSION		SSLEAY_VERSION
402 #define X509_getm_notBefore	X509_get_notBefore
403 #define X509_getm_notAfter	X509_get_notAfter
404 #define EVP_CIPHER_CTX_reset	EVP_CIPHER_CTX_cleanup
405 
406 #endif
407 /* }}} */
408 
409 /* number conversion flags checks */
410 #define PHP_OPENSSL_CHECK_NUMBER_CONVERSION(_cond, _name, _arg_num) \
411 	do { \
412 		if (_cond) { \
413 			zend_argument_value_error((_arg_num), #_name" is too long"); \
414 			RETURN_THROWS(); \
415 		} \
416 	} while(0)
417 #define PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(_cond, _name) \
418 	do { \
419 		if (_cond) { \
420 			zend_value_error(#_name" is too long"); \
421 			return NULL; \
422 		} \
423 	} while(0)
424 /* check if size_t can be safely casted to int */
425 #define PHP_OPENSSL_CHECK_SIZE_T_TO_INT(_var, _name, _arg_num) \
426 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_SIZE_T_INT_OVFL(_var), _name, _arg_num)
427 #define PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(_var, _name) \
428 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(ZEND_SIZE_T_INT_OVFL(_var), _name)
429 /* check if size_t can be safely casted to unsigned int */
430 #define PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(_var, _name, _arg_num) \
431 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_SIZE_T_UINT_OVFL(_var), _name, _arg_num)
432 /* check if long can be safely casted to int */
433 #define PHP_OPENSSL_CHECK_LONG_TO_INT(_var, _name, _arg_num) \
434 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_LONG_EXCEEDS_INT(_var), _name, _arg_num)
435 #define PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(_var, _name) \
436 	PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(ZEND_LONG_EXCEEDS_INT(_var), _name)
437 
438 /* {{{ php_openssl_store_errors */
php_openssl_store_errors()439 void php_openssl_store_errors()
440 {
441 	struct php_openssl_errors *errors;
442 	int error_code = ERR_get_error();
443 
444 	if (!error_code) {
445 		return;
446 	}
447 
448 	if (!OPENSSL_G(errors)) {
449 		OPENSSL_G(errors) = pecalloc(1, sizeof(struct php_openssl_errors), 1);
450 	}
451 
452 	errors = OPENSSL_G(errors);
453 
454 	do {
455 		errors->top = (errors->top + 1) % ERR_NUM_ERRORS;
456 		if (errors->top == errors->bottom) {
457 			errors->bottom = (errors->bottom + 1) % ERR_NUM_ERRORS;
458 		}
459 		errors->buffer[errors->top] = error_code;
460 	} while ((error_code = ERR_get_error()));
461 
462 }
463 /* }}} */
464 
465 /* openssl file path check error function */
php_openssl_check_path_error(uint32_t arg_num,int type,const char * format,...)466 static void php_openssl_check_path_error(uint32_t arg_num, int type, const char *format, ...)
467 {
468 	va_list va;
469 	const char *arg_name;
470 
471 	va_start(va, format);
472 
473 	if (type == E_ERROR) {
474 		zend_argument_error_variadic(zend_ce_value_error, arg_num, format, va);
475 	} else {
476 		arg_name = get_active_function_arg_name(arg_num);
477 		php_verror(NULL, arg_name, type, format, va);
478 	}
479 	va_end(va);
480 }
481 
482 /* openssl file path check extended */
php_openssl_check_path_ex(const char * file_path,size_t file_path_len,char * real_path,uint32_t arg_num,bool contains_file_protocol,bool is_from_array,const char * option_name)483 static bool php_openssl_check_path_ex(
484 		const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num,
485 		bool contains_file_protocol, bool is_from_array, const char *option_name)
486 {
487 	const char *fs_file_path;
488 	size_t fs_file_path_len;
489 	const char *error_msg = NULL;
490 	int error_type = E_WARNING;
491 
492 	if (file_path_len == 0) {
493 		real_path[0] = '\0';
494 		return true;
495 	}
496 
497 	if (contains_file_protocol) {
498 		size_t path_prefix_len = sizeof("file://") - 1;
499 		if (file_path_len <= path_prefix_len) {
500 			return false;
501 		}
502 		fs_file_path = file_path + path_prefix_len;
503 		fs_file_path_len = file_path_len - path_prefix_len;
504 	} else {
505 		fs_file_path = file_path;
506 		fs_file_path_len = file_path_len;
507 	}
508 
509 	if (CHECK_NULL_PATH(fs_file_path, fs_file_path_len)) {
510 		error_msg = "must not contain any null bytes";
511 		error_type = E_ERROR;
512 	} else if (expand_filepath(fs_file_path, real_path) == NULL) {
513 		error_msg = "must be a valid file path";
514 	}
515 
516 	if (error_msg != NULL) {
517 		if (arg_num == 0) {
518 			const char *option_title = option_name ? option_name : "unknown";
519 			const char *option_label = is_from_array ? "array item" : "option";
520 			php_error_docref(NULL, E_WARNING, "Path for %s %s %s",
521 					option_title, option_label, error_msg);
522 		} else if (is_from_array && option_name != NULL) {
523 			php_openssl_check_path_error(
524 					arg_num, error_type, "option %s array item %s", option_name, error_msg);
525 		} else if (is_from_array) {
526 			php_openssl_check_path_error(arg_num, error_type, "array item %s", error_msg);
527 		} else if (option_name != NULL) {
528 			php_openssl_check_path_error(
529 					arg_num, error_type, "option %s %s", option_name, error_msg);
530 		} else {
531 			php_openssl_check_path_error(arg_num, error_type, "%s", error_msg);
532 		}
533 	} else if (!php_check_open_basedir(real_path)) {
534 		return true;
535 	}
536 
537 	return false;
538 }
539 
540 /* openssl file path check */
php_openssl_check_path(const char * file_path,size_t file_path_len,char * real_path,uint32_t arg_num)541 static inline bool php_openssl_check_path(
542 		const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num)
543 {
544 	return php_openssl_check_path_ex(
545 			file_path, file_path_len, real_path, arg_num, false, false, NULL);
546 }
547 
548 /* openssl file path extra check with zend string */
php_openssl_check_path_str_ex(zend_string * file_path,char * real_path,uint32_t arg_num,bool contains_file_protocol,bool is_from_array,const char * option_name)549 static inline bool php_openssl_check_path_str_ex(
550 		zend_string *file_path, char *real_path, uint32_t arg_num,
551 		bool contains_file_protocol, bool is_from_array, const char *option_name)
552 {
553 	return php_openssl_check_path_ex(
554 			ZSTR_VAL(file_path), ZSTR_LEN(file_path), real_path, arg_num, contains_file_protocol,
555 			is_from_array, option_name);
556 }
557 
558 /* openssl file path check with zend string */
php_openssl_check_path_str(zend_string * file_path,char * real_path,uint32_t arg_num)559 static inline bool php_openssl_check_path_str(
560 		zend_string *file_path, char *real_path, uint32_t arg_num)
561 {
562 	return php_openssl_check_path_str_ex(file_path, real_path, arg_num, true, false, NULL);
563 }
564 
565 static int ssl_stream_data_index;
566 
php_openssl_get_stream_from_ssl_handle(const SSL * ssl)567 php_stream* php_openssl_get_stream_from_ssl_handle(const SSL *ssl)
568 {
569 	return (php_stream*)SSL_get_ex_data(ssl, ssl_stream_data_index);
570 }
571 
php_openssl_get_ssl_stream_data_index()572 int php_openssl_get_ssl_stream_data_index()
573 {
574 	return ssl_stream_data_index;
575 }
576 
577 /* openssl -> PHP "bridging" */
578 /* true global; readonly after module startup */
579 static char default_ssl_conf_filename[MAXPATHLEN];
580 
581 struct php_x509_request { /* {{{ */
582 	LHASH_OF(CONF_VALUE) * global_config;	/* Global SSL config */
583 	LHASH_OF(CONF_VALUE) * req_config;		/* SSL config for this request */
584 	const EVP_MD * md_alg;
585 	const EVP_MD * digest;
586 	char	* section_name,
587 			* config_filename,
588 			* digest_name,
589 			* extensions_section,
590 			* request_extensions_section;
591 	int priv_key_bits;
592 	int priv_key_type;
593 
594 	int priv_key_encrypt;
595 
596 #ifdef HAVE_EVP_PKEY_EC
597 	int curve_name;
598 #endif
599 
600 	EVP_PKEY * priv_key;
601 
602 	const EVP_CIPHER * priv_key_encrypt_cipher;
603 };
604 /* }}} */
605 
606 static X509 *php_openssl_x509_from_param(
607 		zend_object *cert_obj, zend_string *cert_str, uint32_t arg_num);
608 static X509 *php_openssl_x509_from_zval(
609 		zval *val, bool *free_cert, uint32_t arg_num, bool is_from_array, const char *option_name);
610 static X509_REQ *php_openssl_csr_from_param(
611 		zend_object *csr_obj, zend_string *csr_str, uint32_t arg_num);
612 static EVP_PKEY *php_openssl_pkey_from_zval(
613 		zval *val, int public_key, char *passphrase, size_t passphrase_len, uint32_t arg_num);
614 
615 static int php_openssl_is_private_key(EVP_PKEY* pkey);
616 static X509_STORE * php_openssl_setup_verify(zval * calist, uint32_t arg_num);
617 static STACK_OF(X509) * php_openssl_load_all_certs_from_file(
618 		char *cert_file, size_t cert_file_len, uint32_t arg_num);
619 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req);
620 
php_openssl_add_assoc_name_entry(zval * val,char * key,X509_NAME * name,int shortname)621 static void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname) /* {{{ */
622 {
623 	zval *data;
624 	zval subitem, tmp;
625 	int i;
626 	char *sname;
627 	int nid;
628 	X509_NAME_ENTRY * ne;
629 	ASN1_STRING * str = NULL;
630 	ASN1_OBJECT * obj;
631 
632 	if (key != NULL) {
633 		array_init(&subitem);
634 	} else {
635 		ZVAL_COPY_VALUE(&subitem, val);
636 	}
637 
638 	for (i = 0; i < X509_NAME_entry_count(name); i++) {
639 		const unsigned char *to_add = NULL;
640 		int to_add_len = 0;
641 		unsigned char *to_add_buf = NULL;
642 
643 		ne = X509_NAME_get_entry(name, i);
644 		obj = X509_NAME_ENTRY_get_object(ne);
645 		nid = OBJ_obj2nid(obj);
646 
647 		if (shortname) {
648 			sname = (char *) OBJ_nid2sn(nid);
649 		} else {
650 			sname = (char *) OBJ_nid2ln(nid);
651 		}
652 
653 		str = X509_NAME_ENTRY_get_data(ne);
654 		if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) {
655 			/* ASN1_STRING_to_UTF8(3): The converted data is copied into a newly allocated buffer */
656 			to_add_len = ASN1_STRING_to_UTF8(&to_add_buf, str);
657 			to_add = to_add_buf;
658 		} else {
659 			/* ASN1_STRING_get0_data(3): Since this is an internal pointer it should not be freed or modified in any way */
660 			to_add = ASN1_STRING_get0_data(str);
661 			to_add_len = ASN1_STRING_length(str);
662 		}
663 
664 		if (to_add_len != -1) {
665 			if ((data = zend_hash_str_find(Z_ARRVAL(subitem), sname, strlen(sname))) != NULL) {
666 				if (Z_TYPE_P(data) == IS_ARRAY) {
667 					add_next_index_stringl(data, (const char *)to_add, to_add_len);
668 				} else if (Z_TYPE_P(data) == IS_STRING) {
669 					array_init(&tmp);
670 					add_next_index_str(&tmp, zend_string_copy(Z_STR_P(data)));
671 					add_next_index_stringl(&tmp, (const char *)to_add, to_add_len);
672 					zend_hash_str_update(Z_ARRVAL(subitem), sname, strlen(sname), &tmp);
673 				}
674 			} else {
675 				/* it might be better to expand it and pass zval from ZVAL_STRING
676 				 * to zend_symtable_str_update so we do not silently drop const
677 				 * but we need a test to cover this part first */
678 				add_assoc_stringl(&subitem, sname, (char *)to_add, to_add_len);
679 			}
680 		} else {
681 			php_openssl_store_errors();
682 		}
683 
684 		if (to_add_buf != NULL) {
685 			OPENSSL_free(to_add_buf);
686 		}
687 	}
688 
689 	if (key != NULL) {
690 		zend_hash_str_update(Z_ARRVAL_P(val), key, strlen(key), &subitem);
691 	}
692 }
693 /* }}} */
694 
php_openssl_add_assoc_asn1_string(zval * val,char * key,ASN1_STRING * str)695 static void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) /* {{{ */
696 {
697 	add_assoc_stringl(val, key, (char *)str->data, str->length);
698 }
699 /* }}} */
700 
php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr)701 static time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr) /* {{{ */
702 {
703 /*
704 	This is how the time string is formatted:
705 
706    snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
707 	  ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
708 */
709 
710 	time_t ret;
711 	struct tm thetime;
712 	char * strbuf;
713 	char * thestr;
714 	long gmadjust = 0;
715 	size_t timestr_len;
716 
717 	if (ASN1_STRING_type(timestr) != V_ASN1_UTCTIME && ASN1_STRING_type(timestr) != V_ASN1_GENERALIZEDTIME) {
718 		php_error_docref(NULL, E_WARNING, "Illegal ASN1 data type for timestamp");
719 		return (time_t)-1;
720 	}
721 
722 	timestr_len = (size_t)ASN1_STRING_length(timestr);
723 
724 	if (timestr_len != strlen((const char *)ASN1_STRING_get0_data(timestr))) {
725 		php_error_docref(NULL, E_WARNING, "Illegal length in timestamp");
726 		return (time_t)-1;
727 	}
728 
729 	if (timestr_len < 13 && timestr_len != 11) {
730 		php_error_docref(NULL, E_WARNING, "Unable to parse time string %s correctly", timestr->data);
731 		return (time_t)-1;
732 	}
733 
734 	if (ASN1_STRING_type(timestr) == V_ASN1_GENERALIZEDTIME && timestr_len < 15) {
735 		php_error_docref(NULL, E_WARNING, "Unable to parse time string %s correctly", timestr->data);
736 		return (time_t)-1;
737 	}
738 
739 	strbuf = estrdup((const char *)ASN1_STRING_get0_data(timestr));
740 
741 	memset(&thetime, 0, sizeof(thetime));
742 
743 	/* we work backwards so that we can use atoi more easily */
744 
745 	thestr = strbuf + timestr_len - 3;
746 
747 	if (timestr_len == 11) {
748 		thetime.tm_sec = 0;
749 	} else {
750 		thetime.tm_sec = atoi(thestr);
751 		*thestr = '\0';
752 		thestr -= 2;
753 	}
754 	thetime.tm_min = atoi(thestr);
755 	*thestr = '\0';
756 	thestr -= 2;
757 	thetime.tm_hour = atoi(thestr);
758 	*thestr = '\0';
759 	thestr -= 2;
760 	thetime.tm_mday = atoi(thestr);
761 	*thestr = '\0';
762 	thestr -= 2;
763 	thetime.tm_mon = atoi(thestr)-1;
764 
765 	*thestr = '\0';
766 	if( ASN1_STRING_type(timestr) == V_ASN1_UTCTIME ) {
767 		thestr -= 2;
768 		thetime.tm_year = atoi(thestr);
769 
770 		if (thetime.tm_year < 68) {
771 			thetime.tm_year += 100;
772 		}
773 	} else if( ASN1_STRING_type(timestr) == V_ASN1_GENERALIZEDTIME ) {
774 		thestr -= 4;
775 		thetime.tm_year = atoi(thestr) - 1900;
776 	}
777 
778 
779 	thetime.tm_isdst = -1;
780 	ret = mktime(&thetime);
781 
782 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
783 	gmadjust = thetime.tm_gmtoff;
784 #else
785 	/*
786 	** If correcting for daylight savings time, we set the adjustment to
787 	** the value of timezone - 3600 seconds. Otherwise, we need to overcorrect and
788 	** set the adjustment to the main timezone + 3600 seconds.
789 	*/
790 	gmadjust = -(thetime.tm_isdst ? (long)timezone - 3600 : (long)timezone);
791 #endif
792 	ret += gmadjust;
793 
794 	efree(strbuf);
795 
796 	return ret;
797 }
798 /* }}} */
799 
php_openssl_config_check_syntax(const char * section_label,const char * config_filename,const char * section,LHASH_OF (CONF_VALUE)* config)800 static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH_OF(CONF_VALUE) * config) /* {{{ */
801 {
802 	X509V3_CTX ctx;
803 
804 	X509V3_set_ctx_test(&ctx);
805 	X509V3_set_conf_lhash(&ctx, config);
806 	if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL)) {
807 		php_openssl_store_errors();
808 		php_error_docref(NULL, E_WARNING, "Error loading %s section %s of %s",
809 				section_label,
810 				section,
811 				config_filename);
812 		return FAILURE;
813 	}
814 	return SUCCESS;
815 }
816 /* }}} */
817 
php_openssl_conf_get_string(LHASH_OF (CONF_VALUE)* conf,const char * group,const char * name)818 static char *php_openssl_conf_get_string(
819 		LHASH_OF(CONF_VALUE) *conf, const char *group, const char *name) {
820 	char *str = CONF_get_string(conf, group, name);
821 	if (str == NULL) {
822 		/* OpenSSL reports an error if a configuration value is not found.
823 		 * However, we don't want to generate errors for optional configuration. */
824 		ERR_clear_error();
825 	}
826 	return str;
827 }
828 
php_openssl_add_oid_section(struct php_x509_request * req)829 static int php_openssl_add_oid_section(struct php_x509_request * req) /* {{{ */
830 {
831 	char * str;
832 	STACK_OF(CONF_VALUE) * sktmp;
833 	CONF_VALUE * cnf;
834 	int i;
835 
836 	str = php_openssl_conf_get_string(req->req_config, NULL, "oid_section");
837 	if (str == NULL) {
838 		return SUCCESS;
839 	}
840 	sktmp = CONF_get_section(req->req_config, str);
841 	if (sktmp == NULL) {
842 		php_openssl_store_errors();
843 		php_error_docref(NULL, E_WARNING, "Problem loading oid section %s", str);
844 		return FAILURE;
845 	}
846 	for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
847 		cnf = sk_CONF_VALUE_value(sktmp, i);
848 		if (OBJ_sn2nid(cnf->name) == NID_undef && OBJ_ln2nid(cnf->name) == NID_undef &&
849 				OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) {
850 			php_openssl_store_errors();
851 			php_error_docref(NULL, E_WARNING, "Problem creating object %s=%s", cnf->name, cnf->value);
852 			return FAILURE;
853 		}
854 	}
855 	return SUCCESS;
856 }
857 /* }}} */
858 
859 #define PHP_SSL_REQ_INIT(req)		memset(req, 0, sizeof(*req))
860 #define PHP_SSL_REQ_DISPOSE(req)	php_openssl_dispose_config(req)
861 #define PHP_SSL_REQ_PARSE(req, zval)	php_openssl_parse_config(req, zval)
862 
863 #define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \
864 			req->config_filename, req->var, req->req_config) == FAILURE) return FAILURE
865 
866 #define SET_OPTIONAL_STRING_ARG(key, varname, defval)	\
867 	do { \
868 		if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_STRING) { \
869 			varname = Z_STRVAL_P(item); \
870 		} else { \
871 			varname = defval; \
872 			if (varname == NULL) { \
873 				php_openssl_store_errors(); \
874 			} \
875 		} \
876 	} while(0)
877 
878 #define SET_OPTIONAL_LONG_ARG(key, varname, defval)	\
879 	if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_LONG) \
880 		varname = (int)Z_LVAL_P(item); \
881 	else \
882 		varname = defval
883 
884 static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo);
885 
886 /* {{{ strip line endings from spkac */
php_openssl_spki_cleanup(const char * src,char * dest)887 static int php_openssl_spki_cleanup(const char *src, char *dest)
888 {
889 	int removed = 0;
890 
891 	while (*src) {
892 		if (*src != '\n' && *src != '\r') {
893 			*dest++ = *src;
894 		} else {
895 			++removed;
896 		}
897 		++src;
898 	}
899 	*dest = 0;
900 	return removed;
901 }
902 /* }}} */
903 
php_openssl_parse_config(struct php_x509_request * req,zval * optional_args)904 static int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args) /* {{{ */
905 {
906 	char * str, path[MAXPATHLEN];
907 	zval * item;
908 
909 	SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename);
910 	SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req");
911 	req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL);
912 	if (req->global_config == NULL) {
913 		php_openssl_store_errors();
914 	}
915 	req->req_config = CONF_load(NULL, req->config_filename, NULL);
916 	if (req->req_config == NULL) {
917 		php_openssl_store_errors();
918 		return FAILURE;
919 	}
920 
921 	/* read in the oids */
922 	str = php_openssl_conf_get_string(req->req_config, NULL, "oid_file");
923 	if (str != NULL && php_openssl_check_path_ex(str, strlen(str), path, 0, false, false, "oid_file")) {
924 		BIO *oid_bio = BIO_new_file(path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
925 		if (oid_bio) {
926 			OBJ_create_objects(oid_bio);
927 			BIO_free(oid_bio);
928 			php_openssl_store_errors();
929 		}
930 	}
931 	if (php_openssl_add_oid_section(req) == FAILURE) {
932 		return FAILURE;
933 	}
934 	SET_OPTIONAL_STRING_ARG("digest_alg", req->digest_name,
935 		php_openssl_conf_get_string(req->req_config, req->section_name, "default_md"));
936 	SET_OPTIONAL_STRING_ARG("x509_extensions", req->extensions_section,
937 		php_openssl_conf_get_string(req->req_config, req->section_name, "x509_extensions"));
938 	SET_OPTIONAL_STRING_ARG("req_extensions", req->request_extensions_section,
939 		php_openssl_conf_get_string(req->req_config, req->section_name, "req_extensions"));
940 	SET_OPTIONAL_LONG_ARG("private_key_bits", req->priv_key_bits,
941 		CONF_get_number(req->req_config, req->section_name, "default_bits"));
942 
943 	SET_OPTIONAL_LONG_ARG("private_key_type", req->priv_key_type, OPENSSL_KEYTYPE_DEFAULT);
944 
945 	if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), "encrypt_key", sizeof("encrypt_key")-1)) != NULL) {
946 		req->priv_key_encrypt = Z_TYPE_P(item) == IS_TRUE ? 1 : 0;
947 	} else {
948 		str = php_openssl_conf_get_string(req->req_config, req->section_name, "encrypt_rsa_key");
949 		if (str == NULL) {
950 			str = php_openssl_conf_get_string(req->req_config, req->section_name, "encrypt_key");
951 		}
952 		if (str != NULL && strcmp(str, "no") == 0) {
953 			req->priv_key_encrypt = 0;
954 		} else {
955 			req->priv_key_encrypt = 1;
956 		}
957 	}
958 
959 	if (req->priv_key_encrypt &&
960 		optional_args &&
961 		(item = zend_hash_str_find(Z_ARRVAL_P(optional_args), "encrypt_key_cipher", sizeof("encrypt_key_cipher")-1)) != NULL &&
962 		Z_TYPE_P(item) == IS_LONG
963 	) {
964 		zend_long cipher_algo = Z_LVAL_P(item);
965 		const EVP_CIPHER* cipher = php_openssl_get_evp_cipher_from_algo(cipher_algo);
966 		if (cipher == NULL) {
967 			php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm for private key");
968 			return FAILURE;
969 		} else {
970 			req->priv_key_encrypt_cipher = cipher;
971 		}
972 	} else {
973 		req->priv_key_encrypt_cipher = NULL;
974 	}
975 
976 	/* digest alg */
977 	if (req->digest_name == NULL) {
978 		req->digest_name = php_openssl_conf_get_string(req->req_config, req->section_name, "default_md");
979 	}
980 	if (req->digest_name != NULL) {
981 		req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name);
982 	}
983 	if (req->md_alg == NULL) {
984 		req->md_alg = req->digest = EVP_sha1();
985 		php_openssl_store_errors();
986 	}
987 
988 	PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section);
989 #ifdef HAVE_EVP_PKEY_EC
990 	/* set the ec group curve name */
991 	req->curve_name = NID_undef;
992 	if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), "curve_name", sizeof("curve_name")-1)) != NULL
993 		&& Z_TYPE_P(item) == IS_STRING) {
994 		req->curve_name = OBJ_sn2nid(Z_STRVAL_P(item));
995 		if (req->curve_name == NID_undef) {
996 			php_error_docref(NULL, E_WARNING, "Unknown elliptic curve (short) name %s", Z_STRVAL_P(item));
997 			return FAILURE;
998 		}
999 	}
1000 #endif
1001 
1002 	/* set the string mask */
1003 	str = php_openssl_conf_get_string(req->req_config, req->section_name, "string_mask");
1004 	if (str != NULL && !ASN1_STRING_set_default_mask_asc(str)) {
1005 		php_error_docref(NULL, E_WARNING, "Invalid global string mask setting %s", str);
1006 		return FAILURE;
1007 	}
1008 
1009 	PHP_SSL_CONFIG_SYNTAX_CHECK(request_extensions_section);
1010 
1011 	return SUCCESS;
1012 }
1013 /* }}} */
1014 
php_openssl_dispose_config(struct php_x509_request * req)1015 static void php_openssl_dispose_config(struct php_x509_request * req) /* {{{ */
1016 {
1017 	if (req->priv_key) {
1018 		EVP_PKEY_free(req->priv_key);
1019 		req->priv_key = NULL;
1020 	}
1021 	if (req->global_config) {
1022 		CONF_free(req->global_config);
1023 		req->global_config = NULL;
1024 	}
1025 	if (req->req_config) {
1026 		CONF_free(req->req_config);
1027 		req->req_config = NULL;
1028 	}
1029 }
1030 /* }}} */
1031 
1032 #if defined(PHP_WIN32) || PHP_OPENSSL_API_VERSION >= 0x10100
1033 #define PHP_OPENSSL_RAND_ADD_TIME() ((void) 0)
1034 #else
1035 #define PHP_OPENSSL_RAND_ADD_TIME() php_openssl_rand_add_timeval()
1036 
php_openssl_rand_add_timeval()1037 static inline void php_openssl_rand_add_timeval()  /* {{{ */
1038 {
1039 	struct timeval tv;
1040 
1041 	gettimeofday(&tv, NULL);
1042 	RAND_add(&tv, sizeof(tv), 0.0);
1043 }
1044 /* }}} */
1045 
1046 #endif
1047 
php_openssl_load_rand_file(const char * file,int * egdsocket,int * seeded)1048 static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded) /* {{{ */
1049 {
1050 	char buffer[MAXPATHLEN];
1051 
1052 	*egdsocket = 0;
1053 	*seeded = 0;
1054 
1055 	if (file == NULL) {
1056 		file = RAND_file_name(buffer, sizeof(buffer));
1057 #ifdef HAVE_RAND_EGD
1058 	} else if (RAND_egd(file) > 0) {
1059 		/* if the given filename is an EGD socket, don't
1060 		 * write anything back to it */
1061 		*egdsocket = 1;
1062 		return SUCCESS;
1063 #endif
1064 	}
1065 	if (file == NULL || !RAND_load_file(file, -1)) {
1066 		if (RAND_status() == 0) {
1067 			php_openssl_store_errors();
1068 			php_error_docref(NULL, E_WARNING, "Unable to load random state; not enough random data!");
1069 			return FAILURE;
1070 		}
1071 		return FAILURE;
1072 	}
1073 	*seeded = 1;
1074 	return SUCCESS;
1075 }
1076 /* }}} */
1077 
php_openssl_write_rand_file(const char * file,int egdsocket,int seeded)1078 static int php_openssl_write_rand_file(const char * file, int egdsocket, int seeded) /* {{{ */
1079 {
1080 	char buffer[MAXPATHLEN];
1081 
1082 
1083 	if (egdsocket || !seeded) {
1084 		/* if we did not manage to read the seed file, we should not write
1085 		 * a low-entropy seed file back */
1086 		return FAILURE;
1087 	}
1088 	if (file == NULL) {
1089 		file = RAND_file_name(buffer, sizeof(buffer));
1090 	}
1091 	PHP_OPENSSL_RAND_ADD_TIME();
1092 	if (file == NULL || !RAND_write_file(file)) {
1093 		php_openssl_store_errors();
1094 		php_error_docref(NULL, E_WARNING, "Unable to write random state");
1095 		return FAILURE;
1096 	}
1097 	return SUCCESS;
1098 }
1099 /* }}} */
1100 
php_openssl_get_evp_md_from_algo(zend_long algo)1101 static EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { /* {{{ */
1102 	EVP_MD *mdtype;
1103 
1104 	switch (algo) {
1105 		case OPENSSL_ALGO_SHA1:
1106 			mdtype = (EVP_MD *) EVP_sha1();
1107 			break;
1108 		case OPENSSL_ALGO_MD5:
1109 			mdtype = (EVP_MD *) EVP_md5();
1110 			break;
1111 #ifndef OPENSSL_NO_MD4
1112 		case OPENSSL_ALGO_MD4:
1113 			mdtype = (EVP_MD *) EVP_md4();
1114 			break;
1115 #endif
1116 #ifndef OPENSSL_NO_MD2
1117 		case OPENSSL_ALGO_MD2:
1118 			mdtype = (EVP_MD *) EVP_md2();
1119 			break;
1120 #endif
1121 #if PHP_OPENSSL_API_VERSION < 0x10100
1122 		case OPENSSL_ALGO_DSS1:
1123 			mdtype = (EVP_MD *) EVP_dss1();
1124 			break;
1125 #endif
1126 		case OPENSSL_ALGO_SHA224:
1127 			mdtype = (EVP_MD *) EVP_sha224();
1128 			break;
1129 		case OPENSSL_ALGO_SHA256:
1130 			mdtype = (EVP_MD *) EVP_sha256();
1131 			break;
1132 		case OPENSSL_ALGO_SHA384:
1133 			mdtype = (EVP_MD *) EVP_sha384();
1134 			break;
1135 		case OPENSSL_ALGO_SHA512:
1136 			mdtype = (EVP_MD *) EVP_sha512();
1137 			break;
1138 #ifndef OPENSSL_NO_RMD160
1139 		case OPENSSL_ALGO_RMD160:
1140 			mdtype = (EVP_MD *) EVP_ripemd160();
1141 			break;
1142 #endif
1143 		default:
1144 			return NULL;
1145 			break;
1146 	}
1147 	return mdtype;
1148 }
1149 /* }}} */
1150 
php_openssl_get_evp_cipher_from_algo(zend_long algo)1151 static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo) { /* {{{ */
1152 	switch (algo) {
1153 #ifndef OPENSSL_NO_RC2
1154 		case PHP_OPENSSL_CIPHER_RC2_40:
1155 			return EVP_rc2_40_cbc();
1156 			break;
1157 		case PHP_OPENSSL_CIPHER_RC2_64:
1158 			return EVP_rc2_64_cbc();
1159 			break;
1160 		case PHP_OPENSSL_CIPHER_RC2_128:
1161 			return EVP_rc2_cbc();
1162 			break;
1163 #endif
1164 
1165 #ifndef OPENSSL_NO_DES
1166 		case PHP_OPENSSL_CIPHER_DES:
1167 			return EVP_des_cbc();
1168 			break;
1169 		case PHP_OPENSSL_CIPHER_3DES:
1170 			return EVP_des_ede3_cbc();
1171 			break;
1172 #endif
1173 
1174 #ifndef OPENSSL_NO_AES
1175 		case PHP_OPENSSL_CIPHER_AES_128_CBC:
1176 			return EVP_aes_128_cbc();
1177 			break;
1178 		case PHP_OPENSSL_CIPHER_AES_192_CBC:
1179 			return EVP_aes_192_cbc();
1180 			break;
1181 		case PHP_OPENSSL_CIPHER_AES_256_CBC:
1182 			return EVP_aes_256_cbc();
1183 			break;
1184 #endif
1185 
1186 
1187 		default:
1188 			return NULL;
1189 			break;
1190 	}
1191 }
1192 /* }}} */
1193 
1194 /* {{{ INI Settings */
1195 PHP_INI_BEGIN()
1196 	PHP_INI_ENTRY("openssl.cafile", NULL, PHP_INI_PERDIR, NULL)
1197 	PHP_INI_ENTRY("openssl.capath", NULL, PHP_INI_PERDIR, NULL)
PHP_INI_END()1198 PHP_INI_END()
1199 /* }}} */
1200 
1201 /* {{{ PHP_MINIT_FUNCTION */
1202 PHP_MINIT_FUNCTION(openssl)
1203 {
1204 	char * config_filename;
1205 
1206 	zend_class_entry ce;
1207 	INIT_CLASS_ENTRY(ce, "OpenSSLCertificate", class_OpenSSLCertificate_methods);
1208 	php_openssl_certificate_ce = zend_register_internal_class(&ce);
1209 	php_openssl_certificate_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
1210 	php_openssl_certificate_ce->create_object = php_openssl_certificate_create_object;
1211 	php_openssl_certificate_ce->serialize = zend_class_serialize_deny;
1212 	php_openssl_certificate_ce->unserialize = zend_class_unserialize_deny;
1213 
1214 	memcpy(&php_openssl_certificate_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1215 	php_openssl_certificate_object_handlers.offset = XtOffsetOf(php_openssl_certificate_object, std);
1216 	php_openssl_certificate_object_handlers.free_obj = php_openssl_certificate_free_obj;
1217 	php_openssl_certificate_object_handlers.get_constructor = php_openssl_certificate_get_constructor;
1218 	php_openssl_certificate_object_handlers.clone_obj = NULL;
1219 	php_openssl_certificate_object_handlers.compare = zend_objects_not_comparable;
1220 
1221 	zend_class_entry csr_ce;
1222 	INIT_CLASS_ENTRY(csr_ce, "OpenSSLCertificateSigningRequest", class_OpenSSLCertificateSigningRequest_methods);
1223 	php_openssl_request_ce = zend_register_internal_class(&csr_ce);
1224 	php_openssl_request_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
1225 	php_openssl_request_ce->create_object = php_openssl_request_create_object;
1226 	php_openssl_request_ce->serialize = zend_class_serialize_deny;
1227 	php_openssl_request_ce->unserialize = zend_class_unserialize_deny;
1228 
1229 	memcpy(&php_openssl_request_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1230 	php_openssl_request_object_handlers.offset = XtOffsetOf(php_openssl_request_object, std);
1231 	php_openssl_request_object_handlers.free_obj = php_openssl_request_free_obj;
1232 	php_openssl_request_object_handlers.get_constructor = php_openssl_request_get_constructor;
1233 	php_openssl_request_object_handlers.clone_obj = NULL;
1234 	php_openssl_request_object_handlers.compare = zend_objects_not_comparable;
1235 
1236 	zend_class_entry key_ce;
1237 	INIT_CLASS_ENTRY(key_ce, "OpenSSLAsymmetricKey", class_OpenSSLAsymmetricKey_methods);
1238 	php_openssl_pkey_ce = zend_register_internal_class(&key_ce);
1239 	php_openssl_pkey_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
1240 	php_openssl_pkey_ce->create_object = php_openssl_pkey_create_object;
1241 	php_openssl_pkey_ce->serialize = zend_class_serialize_deny;
1242 	php_openssl_pkey_ce->unserialize = zend_class_unserialize_deny;
1243 
1244 	memcpy(&php_openssl_pkey_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1245 	php_openssl_pkey_object_handlers.offset = XtOffsetOf(php_openssl_pkey_object, std);
1246 	php_openssl_pkey_object_handlers.free_obj = php_openssl_pkey_free_obj;
1247 	php_openssl_pkey_object_handlers.get_constructor = php_openssl_pkey_get_constructor;
1248 	php_openssl_pkey_object_handlers.clone_obj = NULL;
1249 	php_openssl_pkey_object_handlers.compare = zend_objects_not_comparable;
1250 
1251 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
1252 	OPENSSL_config(NULL);
1253 	SSL_library_init();
1254 	OpenSSL_add_all_ciphers();
1255 	OpenSSL_add_all_digests();
1256 	OpenSSL_add_all_algorithms();
1257 
1258 #if !defined(OPENSSL_NO_AES) && defined(EVP_CIPH_CCM_MODE) && OPENSSL_VERSION_NUMBER < 0x100020000
1259 	EVP_add_cipher(EVP_aes_128_ccm());
1260 	EVP_add_cipher(EVP_aes_192_ccm());
1261 	EVP_add_cipher(EVP_aes_256_ccm());
1262 #endif
1263 
1264 	SSL_load_error_strings();
1265 #else
1266 	OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
1267 #endif
1268 
1269 	/* register a resource id number with OpenSSL so that we can map SSL -> stream structures in
1270 	 * OpenSSL callbacks */
1271 	ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL);
1272 
1273 	REGISTER_STRING_CONSTANT("OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT, CONST_CS|CONST_PERSISTENT);
1274 	REGISTER_LONG_CONSTANT("OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER, CONST_CS|CONST_PERSISTENT);
1275 
1276 	/* purposes for cert purpose checking */
1277 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_CLIENT", X509_PURPOSE_SSL_CLIENT, CONST_CS|CONST_PERSISTENT);
1278 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_SERVER", X509_PURPOSE_SSL_SERVER, CONST_CS|CONST_PERSISTENT);
1279 	REGISTER_LONG_CONSTANT("X509_PURPOSE_NS_SSL_SERVER", X509_PURPOSE_NS_SSL_SERVER, CONST_CS|CONST_PERSISTENT);
1280 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_SIGN", X509_PURPOSE_SMIME_SIGN, CONST_CS|CONST_PERSISTENT);
1281 	REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_ENCRYPT", X509_PURPOSE_SMIME_ENCRYPT, CONST_CS|CONST_PERSISTENT);
1282 	REGISTER_LONG_CONSTANT("X509_PURPOSE_CRL_SIGN", X509_PURPOSE_CRL_SIGN, CONST_CS|CONST_PERSISTENT);
1283 #ifdef X509_PURPOSE_ANY
1284 	REGISTER_LONG_CONSTANT("X509_PURPOSE_ANY", X509_PURPOSE_ANY, CONST_CS|CONST_PERSISTENT);
1285 #endif
1286 
1287 	/* digest algorithm constants */
1288 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA1", OPENSSL_ALGO_SHA1, CONST_CS|CONST_PERSISTENT);
1289 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD5", OPENSSL_ALGO_MD5, CONST_CS|CONST_PERSISTENT);
1290 #ifndef OPENSSL_NO_MD4
1291 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD4", OPENSSL_ALGO_MD4, CONST_CS|CONST_PERSISTENT);
1292 #endif
1293 #ifndef OPENSSL_NO_MD2
1294 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD2", OPENSSL_ALGO_MD2, CONST_CS|CONST_PERSISTENT);
1295 #endif
1296 #if PHP_OPENSSL_API_VERSION < 0x10100
1297 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT);
1298 #endif
1299 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA224", OPENSSL_ALGO_SHA224, CONST_CS|CONST_PERSISTENT);
1300 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA256", OPENSSL_ALGO_SHA256, CONST_CS|CONST_PERSISTENT);
1301 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA384", OPENSSL_ALGO_SHA384, CONST_CS|CONST_PERSISTENT);
1302 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA512", OPENSSL_ALGO_SHA512, CONST_CS|CONST_PERSISTENT);
1303 #ifndef OPENSSL_NO_RMD160
1304 	REGISTER_LONG_CONSTANT("OPENSSL_ALGO_RMD160", OPENSSL_ALGO_RMD160, CONST_CS|CONST_PERSISTENT);
1305 #endif
1306 
1307 	/* flags for S/MIME */
1308 	REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT);
1309 	REGISTER_LONG_CONSTANT("PKCS7_TEXT", PKCS7_TEXT, CONST_CS|CONST_PERSISTENT);
1310 	REGISTER_LONG_CONSTANT("PKCS7_NOINTERN", PKCS7_NOINTERN, CONST_CS|CONST_PERSISTENT);
1311 	REGISTER_LONG_CONSTANT("PKCS7_NOVERIFY", PKCS7_NOVERIFY, CONST_CS|CONST_PERSISTENT);
1312 	REGISTER_LONG_CONSTANT("PKCS7_NOCHAIN", PKCS7_NOCHAIN, CONST_CS|CONST_PERSISTENT);
1313 	REGISTER_LONG_CONSTANT("PKCS7_NOCERTS", PKCS7_NOCERTS, CONST_CS|CONST_PERSISTENT);
1314 	REGISTER_LONG_CONSTANT("PKCS7_NOATTR", PKCS7_NOATTR, CONST_CS|CONST_PERSISTENT);
1315 	REGISTER_LONG_CONSTANT("PKCS7_BINARY", PKCS7_BINARY, CONST_CS|CONST_PERSISTENT);
1316 	REGISTER_LONG_CONSTANT("PKCS7_NOSIGS", PKCS7_NOSIGS, CONST_CS|CONST_PERSISTENT);
1317 
1318 	REGISTER_LONG_CONSTANT("OPENSSL_CMS_DETACHED", CMS_DETACHED, CONST_CS|CONST_PERSISTENT);
1319 	REGISTER_LONG_CONSTANT("OPENSSL_CMS_TEXT", CMS_TEXT, CONST_CS|CONST_PERSISTENT);
1320 	REGISTER_LONG_CONSTANT("OPENSSL_CMS_NOINTERN", CMS_NOINTERN, CONST_CS|CONST_PERSISTENT);
1321 	REGISTER_LONG_CONSTANT("OPENSSL_CMS_NOVERIFY", CMS_NOVERIFY, CONST_CS|CONST_PERSISTENT);
1322 	REGISTER_LONG_CONSTANT("OPENSSL_CMS_NOCERTS", CMS_NOCERTS, CONST_CS|CONST_PERSISTENT);
1323 	REGISTER_LONG_CONSTANT("OPENSSL_CMS_NOATTR", CMS_NOATTR, CONST_CS|CONST_PERSISTENT);
1324 	REGISTER_LONG_CONSTANT("OPENSSL_CMS_BINARY", CMS_BINARY, CONST_CS|CONST_PERSISTENT);
1325 	REGISTER_LONG_CONSTANT("OPENSSL_CMS_NOSIGS", CMS_NOSIGS, CONST_CS|CONST_PERSISTENT);
1326 
1327 	REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_PADDING", RSA_PKCS1_PADDING, CONST_CS|CONST_PERSISTENT);
1328 	REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING", RSA_SSLV23_PADDING, CONST_CS|CONST_PERSISTENT);
1329 	REGISTER_LONG_CONSTANT("OPENSSL_NO_PADDING", RSA_NO_PADDING, CONST_CS|CONST_PERSISTENT);
1330 	REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING, CONST_CS|CONST_PERSISTENT);
1331 
1332 	/* Informational stream wrapper constants */
1333 	REGISTER_STRING_CONSTANT("OPENSSL_DEFAULT_STREAM_CIPHERS", OPENSSL_DEFAULT_STREAM_CIPHERS, CONST_CS|CONST_PERSISTENT);
1334 
1335 	/* Ciphers */
1336 #ifndef OPENSSL_NO_RC2
1337 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_40", PHP_OPENSSL_CIPHER_RC2_40, CONST_CS|CONST_PERSISTENT);
1338 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_128", PHP_OPENSSL_CIPHER_RC2_128, CONST_CS|CONST_PERSISTENT);
1339 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_64", PHP_OPENSSL_CIPHER_RC2_64, CONST_CS|CONST_PERSISTENT);
1340 #endif
1341 #ifndef OPENSSL_NO_DES
1342 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_DES", PHP_OPENSSL_CIPHER_DES, CONST_CS|CONST_PERSISTENT);
1343 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_3DES", PHP_OPENSSL_CIPHER_3DES, CONST_CS|CONST_PERSISTENT);
1344 #endif
1345 #ifndef OPENSSL_NO_AES
1346 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_128_CBC", PHP_OPENSSL_CIPHER_AES_128_CBC, CONST_CS|CONST_PERSISTENT);
1347 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_192_CBC", PHP_OPENSSL_CIPHER_AES_192_CBC, CONST_CS|CONST_PERSISTENT);
1348 	REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_256_CBC", PHP_OPENSSL_CIPHER_AES_256_CBC, CONST_CS|CONST_PERSISTENT);
1349 #endif
1350 
1351 	/* Values for key types */
1352 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_RSA", OPENSSL_KEYTYPE_RSA, CONST_CS|CONST_PERSISTENT);
1353 #ifndef NO_DSA
1354 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DSA", OPENSSL_KEYTYPE_DSA, CONST_CS|CONST_PERSISTENT);
1355 #endif
1356 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH", OPENSSL_KEYTYPE_DH, CONST_CS|CONST_PERSISTENT);
1357 #ifdef HAVE_EVP_PKEY_EC
1358 	REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_EC", OPENSSL_KEYTYPE_EC, CONST_CS|CONST_PERSISTENT);
1359 #endif
1360 
1361 	REGISTER_LONG_CONSTANT("OPENSSL_RAW_DATA", OPENSSL_RAW_DATA, CONST_CS|CONST_PERSISTENT);
1362 	REGISTER_LONG_CONSTANT("OPENSSL_ZERO_PADDING", OPENSSL_ZERO_PADDING, CONST_CS|CONST_PERSISTENT);
1363 	REGISTER_LONG_CONSTANT("OPENSSL_DONT_ZERO_PAD_KEY", OPENSSL_DONT_ZERO_PAD_KEY, CONST_CS|CONST_PERSISTENT);
1364 
1365 #ifndef OPENSSL_NO_TLSEXT
1366 	/* SNI support included */
1367 	REGISTER_LONG_CONSTANT("OPENSSL_TLSEXT_SERVER_NAME", 1, CONST_CS|CONST_PERSISTENT);
1368 #endif
1369 
1370 	/* Register encodings */
1371 	REGISTER_LONG_CONSTANT("OPENSSL_ENCODING_DER",ENCODING_DER,CONST_CS|CONST_PERSISTENT);
1372 	REGISTER_LONG_CONSTANT("OPENSSL_ENCODING_SMIME",ENCODING_SMIME,CONST_CS|CONST_PERSISTENT);
1373 	REGISTER_LONG_CONSTANT("OPENSSL_ENCODING_PEM",ENCODING_PEM,CONST_CS|CONST_PERSISTENT);
1374 
1375 	/* Determine default SSL configuration file */
1376 	config_filename = getenv("OPENSSL_CONF");
1377 	if (config_filename == NULL) {
1378 		config_filename = getenv("SSLEAY_CONF");
1379 	}
1380 
1381 	/* default to 'openssl.cnf' if no environment variable is set */
1382 	if (config_filename == NULL) {
1383 		snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename), "%s/%s",
1384 				X509_get_default_cert_area(),
1385 				"openssl.cnf");
1386 	} else {
1387 		strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename));
1388 	}
1389 
1390 	php_stream_xport_register("ssl", php_openssl_ssl_socket_factory);
1391 #ifndef OPENSSL_NO_SSL3
1392 	php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory);
1393 #endif
1394 	php_stream_xport_register("tls", php_openssl_ssl_socket_factory);
1395 	php_stream_xport_register("tlsv1.0", php_openssl_ssl_socket_factory);
1396 	php_stream_xport_register("tlsv1.1", php_openssl_ssl_socket_factory);
1397 	php_stream_xport_register("tlsv1.2", php_openssl_ssl_socket_factory);
1398 #if OPENSSL_VERSION_NUMBER >= 0x10101000
1399 	php_stream_xport_register("tlsv1.3", php_openssl_ssl_socket_factory);
1400 #endif
1401 
1402 	/* override the default tcp socket provider */
1403 	php_stream_xport_register("tcp", php_openssl_ssl_socket_factory);
1404 
1405 	php_register_url_stream_wrapper("https", &php_stream_http_wrapper);
1406 	php_register_url_stream_wrapper("ftps", &php_stream_ftp_wrapper);
1407 
1408 	REGISTER_INI_ENTRIES();
1409 
1410 	return SUCCESS;
1411 }
1412 /* }}} */
1413 
1414 /* {{{ PHP_GINIT_FUNCTION */
PHP_GINIT_FUNCTION(openssl)1415 PHP_GINIT_FUNCTION(openssl)
1416 {
1417 #if defined(COMPILE_DL_OPENSSL) && defined(ZTS)
1418 	ZEND_TSRMLS_CACHE_UPDATE();
1419 #endif
1420 	openssl_globals->errors = NULL;
1421 }
1422 /* }}} */
1423 
1424 /* {{{ PHP_GSHUTDOWN_FUNCTION */
PHP_GSHUTDOWN_FUNCTION(openssl)1425 PHP_GSHUTDOWN_FUNCTION(openssl)
1426 {
1427 	if (openssl_globals->errors) {
1428 		pefree(openssl_globals->errors, 1);
1429 	}
1430 }
1431 /* }}} */
1432 
1433 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(openssl)1434 PHP_MINFO_FUNCTION(openssl)
1435 {
1436 	php_info_print_table_start();
1437 	php_info_print_table_row(2, "OpenSSL support", "enabled");
1438 	php_info_print_table_row(2, "OpenSSL Library Version", OpenSSL_version(OPENSSL_VERSION));
1439 	php_info_print_table_row(2, "OpenSSL Header Version", OPENSSL_VERSION_TEXT);
1440 	php_info_print_table_row(2, "Openssl default config", default_ssl_conf_filename);
1441 	php_info_print_table_end();
1442 	DISPLAY_INI_ENTRIES();
1443 }
1444 /* }}} */
1445 
1446 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(openssl)1447 PHP_MSHUTDOWN_FUNCTION(openssl)
1448 {
1449 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
1450 	EVP_cleanup();
1451 
1452 	/* prevent accessing locking callback from unloaded extension */
1453 	CRYPTO_set_locking_callback(NULL);
1454 	/* free allocated error strings */
1455 	ERR_free_strings();
1456 	CONF_modules_free();
1457 #endif
1458 
1459 	php_unregister_url_stream_wrapper("https");
1460 	php_unregister_url_stream_wrapper("ftps");
1461 
1462 	php_stream_xport_unregister("ssl");
1463 #ifndef OPENSSL_NO_SSL3
1464 	php_stream_xport_unregister("sslv3");
1465 #endif
1466 	php_stream_xport_unregister("tls");
1467 	php_stream_xport_unregister("tlsv1.0");
1468 	php_stream_xport_unregister("tlsv1.1");
1469 	php_stream_xport_unregister("tlsv1.2");
1470 #if OPENSSL_VERSION_NUMBER >= 0x10101000
1471 	php_stream_xport_unregister("tlsv1.3");
1472 #endif
1473 
1474 	/* reinstate the default tcp handler */
1475 	php_stream_xport_register("tcp", php_stream_generic_socket_factory);
1476 
1477 	UNREGISTER_INI_ENTRIES();
1478 
1479 	return SUCCESS;
1480 }
1481 /* }}} */
1482 
1483 /* {{{ x509 cert functions */
1484 
1485 /* {{{ Retrieve an array mapping available certificate locations */
PHP_FUNCTION(openssl_get_cert_locations)1486 PHP_FUNCTION(openssl_get_cert_locations)
1487 {
1488 	if (zend_parse_parameters_none() == FAILURE) {
1489 		RETURN_THROWS();
1490 	}
1491 
1492 	array_init(return_value);
1493 
1494 	add_assoc_string(return_value, "default_cert_file", (char *) X509_get_default_cert_file());
1495 	add_assoc_string(return_value, "default_cert_file_env", (char *) X509_get_default_cert_file_env());
1496 	add_assoc_string(return_value, "default_cert_dir", (char *) X509_get_default_cert_dir());
1497 	add_assoc_string(return_value, "default_cert_dir_env", (char *) X509_get_default_cert_dir_env());
1498 	add_assoc_string(return_value, "default_private_dir", (char *) X509_get_default_private_dir());
1499 	add_assoc_string(return_value, "default_default_cert_area", (char *) X509_get_default_cert_area());
1500 	add_assoc_string(return_value, "ini_cafile",
1501 		zend_ini_string("openssl.cafile", sizeof("openssl.cafile")-1, 0));
1502 	add_assoc_string(return_value, "ini_capath",
1503 		zend_ini_string("openssl.capath", sizeof("openssl.capath")-1, 0));
1504 }
1505 /* }}} */
1506 
php_openssl_x509_from_str(zend_string * cert_str,uint32_t arg_num,bool is_from_array,const char * option_name)1507 static X509 *php_openssl_x509_from_str(
1508 		zend_string *cert_str, uint32_t arg_num, bool is_from_array, const char *option_name) {
1509 	X509 *cert = NULL;
1510 	char cert_path[MAXPATHLEN];
1511 	BIO *in;
1512 
1513 	if (ZSTR_LEN(cert_str) > 7 && memcmp(ZSTR_VAL(cert_str), "file://", sizeof("file://") - 1) == 0) {
1514 		if (!php_openssl_check_path_str_ex(cert_str, cert_path, arg_num, true, is_from_array, option_name)) {
1515 			return NULL;
1516 		}
1517 
1518 		in = BIO_new_file(cert_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
1519 		if (in == NULL) {
1520 			php_openssl_store_errors();
1521 			return NULL;
1522 		}
1523 		cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1524 	} else {
1525 		in = BIO_new_mem_buf(ZSTR_VAL(cert_str), (int) ZSTR_LEN(cert_str));
1526 		if (in == NULL) {
1527 			php_openssl_store_errors();
1528 			return NULL;
1529 		}
1530 #ifdef TYPEDEF_D2I_OF
1531 		cert = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
1532 #else
1533 		cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
1534 #endif
1535 	}
1536 
1537 	if (!BIO_free(in)) {
1538 		php_openssl_store_errors();
1539 	}
1540 
1541 	if (cert == NULL) {
1542 		php_openssl_store_errors();
1543 		return NULL;
1544 	}
1545 
1546 	return cert;
1547 }
1548 
1549 /* {{{ php_openssl_x509_from_param
1550 	Given a parameter, extract it into an X509 object.
1551 	The parameter can be:
1552 		. X509 object created using openssl_read_x509()
1553 		. a path to that cert if it starts with file://
1554 		. the cert data otherwise
1555 */
php_openssl_x509_from_param(zend_object * cert_obj,zend_string * cert_str,uint32_t arg_num)1556 static X509 *php_openssl_x509_from_param(
1557 		zend_object *cert_obj, zend_string *cert_str, uint32_t arg_num) {
1558 	if (cert_obj) {
1559 		return php_openssl_certificate_from_obj(cert_obj)->x509;
1560 	}
1561 
1562 	ZEND_ASSERT(cert_str);
1563 
1564 	return php_openssl_x509_from_str(cert_str, arg_num, false, NULL);
1565 }
1566 /* }}} */
1567 
php_openssl_x509_from_zval(zval * val,bool * free_cert,uint32_t arg_num,bool is_from_array,const char * option_name)1568 static X509 *php_openssl_x509_from_zval(
1569 		zval *val, bool *free_cert, uint32_t arg_num, bool is_from_array, const char *option_name)
1570 {
1571 	if (Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_certificate_ce) {
1572 		*free_cert = 0;
1573 
1574 		return php_openssl_certificate_from_obj(Z_OBJ_P(val))->x509;
1575 	}
1576 
1577 	*free_cert = 1;
1578 
1579 	if (!try_convert_to_string(val)) {
1580 		return NULL;
1581 	}
1582 
1583 	return php_openssl_x509_from_str(Z_STR_P(val), arg_num, is_from_array, option_name);
1584 }
1585 /* }}} */
1586 
1587 /* {{{ Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export_to_file)1588 PHP_FUNCTION(openssl_x509_export_to_file)
1589 {
1590 	X509 *cert;
1591 	zend_object *cert_obj;
1592 	zend_string *cert_str;
1593 
1594 	zend_bool notext = 1;
1595 	BIO * bio_out;
1596 	char * filename, file_path[MAXPATHLEN];
1597 	size_t filename_len;
1598 
1599 	ZEND_PARSE_PARAMETERS_START(2, 3)
1600 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
1601 		Z_PARAM_PATH(filename, filename_len)
1602 		Z_PARAM_OPTIONAL
1603 		Z_PARAM_BOOL(notext)
1604 	ZEND_PARSE_PARAMETERS_END();
1605 
1606 	RETVAL_FALSE;
1607 
1608 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1609 	if (cert == NULL) {
1610 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
1611 		return;
1612 	}
1613 
1614 	if (!php_openssl_check_path(filename, filename_len, file_path, 2)) {
1615 		return;
1616 	}
1617 
1618 	bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
1619 	if (bio_out) {
1620 		if (!notext && !X509_print(bio_out, cert)) {
1621 			php_openssl_store_errors();
1622 		}
1623 		if (!PEM_write_bio_X509(bio_out, cert)) {
1624 			php_openssl_store_errors();
1625 		}
1626 
1627 		RETVAL_TRUE;
1628 	} else {
1629 		php_openssl_store_errors();
1630 		php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
1631 	}
1632 
1633 	if (cert_str) {
1634 		X509_free(cert);
1635 	}
1636 
1637 	if (!BIO_free(bio_out)) {
1638 		php_openssl_store_errors();
1639 	}
1640 }
1641 /* }}} */
1642 
1643 /* {{{ Creates new private key (or uses existing) and creates a new spki cert
1644    outputting results to var */
PHP_FUNCTION(openssl_spki_new)1645 PHP_FUNCTION(openssl_spki_new)
1646 {
1647 	size_t challenge_len;
1648 	char * challenge = NULL, *spkstr = NULL;
1649 	zend_string * s = NULL;
1650 	const char *spkac = "SPKAC=";
1651 	zend_long algo = OPENSSL_ALGO_MD5;
1652 
1653 	zval *zpkey = NULL;
1654 	EVP_PKEY *pkey = NULL;
1655 	NETSCAPE_SPKI *spki=NULL;
1656 	const EVP_MD *mdtype;
1657 
1658 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|l", &zpkey, php_openssl_pkey_ce, &challenge, &challenge_len, &algo) == FAILURE) {
1659 		RETURN_THROWS();
1660 	}
1661 	RETVAL_FALSE;
1662 
1663 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(challenge_len, challenge, 2);
1664 
1665 	pkey = php_openssl_pkey_from_zval(zpkey, 0, challenge, challenge_len, 1);
1666 	if (pkey == NULL) {
1667 		if (!EG(exception)) {
1668 			php_error_docref(NULL, E_WARNING, "Unable to use supplied private key");
1669 		}
1670 		goto cleanup;
1671 	}
1672 
1673 	mdtype = php_openssl_get_evp_md_from_algo(algo);
1674 
1675 	if (!mdtype) {
1676 		php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
1677 		goto cleanup;
1678 	}
1679 
1680 	if ((spki = NETSCAPE_SPKI_new()) == NULL) {
1681 		php_openssl_store_errors();
1682 		php_error_docref(NULL, E_WARNING, "Unable to create new SPKAC");
1683 		goto cleanup;
1684 	}
1685 
1686 	if (challenge) {
1687 		if (!ASN1_STRING_set(spki->spkac->challenge, challenge, (int)challenge_len)) {
1688 			php_openssl_store_errors();
1689 			php_error_docref(NULL, E_WARNING, "Unable to set challenge data");
1690 			goto cleanup;
1691 		}
1692 	}
1693 
1694 	if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) {
1695 		php_openssl_store_errors();
1696 		php_error_docref(NULL, E_WARNING, "Unable to embed public key");
1697 		goto cleanup;
1698 	}
1699 
1700 	if (!NETSCAPE_SPKI_sign(spki, pkey, mdtype)) {
1701 		php_openssl_store_errors();
1702 		php_error_docref(NULL, E_WARNING, "Unable to sign with specified digest algorithm");
1703 		goto cleanup;
1704 	}
1705 
1706 	spkstr = NETSCAPE_SPKI_b64_encode(spki);
1707 	if (!spkstr){
1708 		php_openssl_store_errors();
1709 		php_error_docref(NULL, E_WARNING, "Unable to encode SPKAC");
1710 		goto cleanup;
1711 	}
1712 
1713 	s = zend_string_alloc(strlen(spkac) + strlen(spkstr), 0);
1714 	sprintf(ZSTR_VAL(s), "%s%s", spkac, spkstr);
1715 	ZSTR_LEN(s) = strlen(ZSTR_VAL(s));
1716 	OPENSSL_free(spkstr);
1717 
1718 	RETVAL_STR(s);
1719 	goto cleanup;
1720 
1721 cleanup:
1722 	EVP_PKEY_free(pkey);
1723 	if (spki != NULL) {
1724 		NETSCAPE_SPKI_free(spki);
1725 	}
1726 
1727 	if (s && ZSTR_LEN(s) <= 0) {
1728 		RETVAL_FALSE;
1729 	}
1730 }
1731 /* }}} */
1732 
1733 /* {{{ Verifies spki returns boolean */
PHP_FUNCTION(openssl_spki_verify)1734 PHP_FUNCTION(openssl_spki_verify)
1735 {
1736 	size_t spkstr_len;
1737 	int i = 0, spkstr_cleaned_len = 0;
1738 	char *spkstr, * spkstr_cleaned = NULL;
1739 
1740 	EVP_PKEY *pkey = NULL;
1741 	NETSCAPE_SPKI *spki = NULL;
1742 
1743 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
1744 		RETURN_THROWS();
1745 	}
1746 	RETVAL_FALSE;
1747 
1748 	spkstr_cleaned = emalloc(spkstr_len + 1);
1749 	spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
1750 
1751 	if (spkstr_cleaned_len == 0) {
1752 		php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
1753 		goto cleanup;
1754 	}
1755 
1756 	spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
1757 	if (spki == NULL) {
1758 		php_openssl_store_errors();
1759 		php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
1760 		goto cleanup;
1761 	}
1762 
1763 	pkey = X509_PUBKEY_get(spki->spkac->pubkey);
1764 	if (pkey == NULL) {
1765 		php_openssl_store_errors();
1766 		php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
1767 		goto cleanup;
1768 	}
1769 
1770 	i = NETSCAPE_SPKI_verify(spki, pkey);
1771 	goto cleanup;
1772 
1773 cleanup:
1774 	if (spki != NULL) {
1775 		NETSCAPE_SPKI_free(spki);
1776 	}
1777 	EVP_PKEY_free(pkey);
1778 	if (spkstr_cleaned != NULL) {
1779 		efree(spkstr_cleaned);
1780 	}
1781 
1782 	if (i > 0) {
1783 		RETVAL_TRUE;
1784 	} else {
1785 		php_openssl_store_errors();
1786 	}
1787 }
1788 /* }}} */
1789 
1790 /* {{{ Exports public key from existing spki to var */
PHP_FUNCTION(openssl_spki_export)1791 PHP_FUNCTION(openssl_spki_export)
1792 {
1793 	size_t spkstr_len;
1794 	char *spkstr, * spkstr_cleaned = NULL, * s = NULL;
1795 	int spkstr_cleaned_len;
1796 
1797 	EVP_PKEY *pkey = NULL;
1798 	NETSCAPE_SPKI *spki = NULL;
1799 	BIO *out = NULL;
1800 
1801 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
1802 		RETURN_THROWS();
1803 	}
1804 	RETVAL_FALSE;
1805 
1806 	spkstr_cleaned = emalloc(spkstr_len + 1);
1807 	spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
1808 
1809 	if (spkstr_cleaned_len == 0) {
1810 		php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
1811 		goto cleanup;
1812 	}
1813 
1814 	spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
1815 	if (spki == NULL) {
1816 		php_openssl_store_errors();
1817 		php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
1818 		goto cleanup;
1819 	}
1820 
1821 	pkey = X509_PUBKEY_get(spki->spkac->pubkey);
1822 	if (pkey == NULL) {
1823 		php_openssl_store_errors();
1824 		php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
1825 		goto cleanup;
1826 	}
1827 
1828 	out = BIO_new(BIO_s_mem());
1829 	if (out && PEM_write_bio_PUBKEY(out, pkey)) {
1830 		BUF_MEM *bio_buf;
1831 
1832 		BIO_get_mem_ptr(out, &bio_buf);
1833 		RETVAL_STRINGL((char *)bio_buf->data, bio_buf->length);
1834 	} else {
1835 		php_openssl_store_errors();
1836 	}
1837 	goto cleanup;
1838 
1839 cleanup:
1840 
1841 	if (spki != NULL) {
1842 		NETSCAPE_SPKI_free(spki);
1843 	}
1844 	BIO_free_all(out);
1845 	EVP_PKEY_free(pkey);
1846 	if (spkstr_cleaned != NULL) {
1847 		efree(spkstr_cleaned);
1848 	}
1849 	if (s != NULL) {
1850 		efree(s);
1851 	}
1852 }
1853 /* }}} */
1854 
1855 /* {{{ Exports spkac challenge from existing spki to var */
PHP_FUNCTION(openssl_spki_export_challenge)1856 PHP_FUNCTION(openssl_spki_export_challenge)
1857 {
1858 	size_t spkstr_len;
1859 	char *spkstr, * spkstr_cleaned = NULL;
1860 	int spkstr_cleaned_len;
1861 
1862 	NETSCAPE_SPKI *spki = NULL;
1863 
1864 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
1865 		RETURN_THROWS();
1866 	}
1867 	RETVAL_FALSE;
1868 
1869 	spkstr_cleaned = emalloc(spkstr_len + 1);
1870 	spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
1871 
1872 	if (spkstr_cleaned_len == 0) {
1873 		php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
1874 		goto cleanup;
1875 	}
1876 
1877 	spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
1878 	if (spki == NULL) {
1879 		php_openssl_store_errors();
1880 		php_error_docref(NULL, E_WARNING, "Unable to decode SPKAC");
1881 		goto cleanup;
1882 	}
1883 
1884 	RETVAL_STRING((const char *)ASN1_STRING_get0_data(spki->spkac->challenge));
1885 	goto cleanup;
1886 
1887 cleanup:
1888 	if (spkstr_cleaned != NULL) {
1889 		efree(spkstr_cleaned);
1890 	}
1891 	if (spki) {
1892 		NETSCAPE_SPKI_free(spki);
1893 	}
1894 }
1895 /* }}} */
1896 
1897 /* {{{ Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export)1898 PHP_FUNCTION(openssl_x509_export)
1899 {
1900 	X509 *cert;
1901 	zend_object *cert_obj;
1902 	zend_string *cert_str;
1903 	zval *zout;
1904 	zend_bool notext = 1;
1905 	BIO * bio_out;
1906 
1907 	ZEND_PARSE_PARAMETERS_START(2, 3)
1908 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
1909 		Z_PARAM_ZVAL(zout)
1910 		Z_PARAM_OPTIONAL
1911 		Z_PARAM_BOOL(notext)
1912 	ZEND_PARSE_PARAMETERS_END();
1913 
1914 	RETVAL_FALSE;
1915 
1916 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1917 	if (cert == NULL) {
1918 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
1919 		return;
1920 	}
1921 
1922 	bio_out = BIO_new(BIO_s_mem());
1923 	if (!bio_out) {
1924 		php_openssl_store_errors();
1925 		goto cleanup;
1926 	}
1927 	if (!notext && !X509_print(bio_out, cert)) {
1928 		php_openssl_store_errors();
1929 	}
1930 	if (PEM_write_bio_X509(bio_out, cert)) {
1931 		BUF_MEM *bio_buf;
1932 
1933 		BIO_get_mem_ptr(bio_out, &bio_buf);
1934 		ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
1935 
1936 		RETVAL_TRUE;
1937 	} else {
1938 		php_openssl_store_errors();
1939 	}
1940 
1941 	BIO_free(bio_out);
1942 
1943 cleanup:
1944 	if (cert_str) {
1945 		X509_free(cert);
1946 	}
1947 }
1948 /* }}} */
1949 
php_openssl_x509_fingerprint(X509 * peer,const char * method,zend_bool raw)1950 zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, zend_bool raw)
1951 {
1952 	unsigned char md[EVP_MAX_MD_SIZE];
1953 	const EVP_MD *mdtype;
1954 	unsigned int n;
1955 	zend_string *ret;
1956 
1957 	if (!(mdtype = EVP_get_digestbyname(method))) {
1958 		php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
1959 		return NULL;
1960 	} else if (!X509_digest(peer, mdtype, md, &n)) {
1961 		php_openssl_store_errors();
1962 		php_error_docref(NULL, E_ERROR, "Could not generate signature");
1963 		return NULL;
1964 	}
1965 
1966 	if (raw) {
1967 		ret = zend_string_init((char*)md, n, 0);
1968 	} else {
1969 		ret = zend_string_alloc(n * 2, 0);
1970 		make_digest_ex(ZSTR_VAL(ret), md, n);
1971 		ZSTR_VAL(ret)[n * 2] = '\0';
1972 	}
1973 
1974 	return ret;
1975 }
1976 
PHP_FUNCTION(openssl_x509_fingerprint)1977 PHP_FUNCTION(openssl_x509_fingerprint)
1978 {
1979 	X509 *cert;
1980 	zend_object *cert_obj;
1981 	zend_string *cert_str;
1982 	zend_bool raw_output = 0;
1983 	char *method = "sha1";
1984 	size_t method_len;
1985 	zend_string *fingerprint;
1986 
1987 	ZEND_PARSE_PARAMETERS_START(1, 3)
1988 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
1989 		Z_PARAM_OPTIONAL
1990 		Z_PARAM_STRING(method, method_len)
1991 		Z_PARAM_BOOL(raw_output)
1992 	ZEND_PARSE_PARAMETERS_END();
1993 
1994 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1995 	if (cert == NULL) {
1996 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
1997 		RETURN_FALSE;
1998 	}
1999 
2000 	fingerprint = php_openssl_x509_fingerprint(cert, method, raw_output);
2001 	if (fingerprint) {
2002 		RETVAL_STR(fingerprint);
2003 	} else {
2004 		RETVAL_FALSE;
2005 	}
2006 
2007 	if (cert_str) {
2008 		X509_free(cert);
2009 	}
2010 }
2011 
2012 /* {{{ Checks if a private key corresponds to a CERT */
PHP_FUNCTION(openssl_x509_check_private_key)2013 PHP_FUNCTION(openssl_x509_check_private_key)
2014 {
2015 	X509 *cert;
2016 	zend_object *cert_obj;
2017 	zend_string *cert_str;
2018 	zval *zkey;
2019 	EVP_PKEY * key = NULL;
2020 
2021 	ZEND_PARSE_PARAMETERS_START(2, 2)
2022 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2023 		Z_PARAM_ZVAL(zkey)
2024 	ZEND_PARSE_PARAMETERS_END();
2025 
2026 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2027 	if (cert == NULL) {
2028 		RETURN_FALSE;
2029 	}
2030 
2031 	RETVAL_FALSE;
2032 
2033 	key = php_openssl_pkey_from_zval(zkey, 0, "", 0, 2);
2034 	if (key) {
2035 		RETVAL_BOOL(X509_check_private_key(cert, key));
2036 		EVP_PKEY_free(key);
2037 	}
2038 
2039 	if (cert_str) {
2040 		X509_free(cert);
2041 	}
2042 }
2043 /* }}} */
2044 
2045 /* {{{ Verifies the signature of certificate cert using public key key */
PHP_FUNCTION(openssl_x509_verify)2046 PHP_FUNCTION(openssl_x509_verify)
2047 {
2048 	X509 *cert;
2049 	zend_object *cert_obj;
2050 	zend_string *cert_str;
2051 	zval *zkey;
2052 	EVP_PKEY * key = NULL;
2053 	int err = -1;
2054 
2055 	ZEND_PARSE_PARAMETERS_START(2, 2)
2056 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2057 		Z_PARAM_ZVAL(zkey)
2058 	ZEND_PARSE_PARAMETERS_END();
2059 
2060 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2061 	if (cert == NULL) {
2062 		RETURN_LONG(err);
2063 	}
2064 
2065 	key = php_openssl_pkey_from_zval(zkey, 1, NULL, 0, 2);
2066 	if (key != NULL) {
2067 		err = X509_verify(cert, key);
2068 		if (err < 0) {
2069 			php_openssl_store_errors();
2070 		}
2071 
2072 		EVP_PKEY_free(key);
2073 	}
2074 
2075 	if (cert_str) {
2076 		X509_free(cert);
2077 	}
2078 
2079 	RETURN_LONG(err);
2080 }
2081 /* }}} */
2082 
2083 /* Special handling of subjectAltName, see CVE-2013-4073
2084  * Christian Heimes
2085  */
2086 
openssl_x509v3_subjectAltName(BIO * bio,X509_EXTENSION * extension)2087 static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
2088 {
2089 	GENERAL_NAMES *names;
2090 	const X509V3_EXT_METHOD *method = NULL;
2091 	ASN1_OCTET_STRING *extension_data;
2092 	long i, length, num;
2093 	const unsigned char *p;
2094 
2095 	method = X509V3_EXT_get(extension);
2096 	if (method == NULL) {
2097 		return -1;
2098 	}
2099 
2100 	extension_data = X509_EXTENSION_get_data(extension);
2101 	p = extension_data->data;
2102 	length = extension_data->length;
2103 	if (method->it) {
2104 		names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, &p, length,
2105 			ASN1_ITEM_ptr(method->it)));
2106 	} else {
2107 		names = (GENERAL_NAMES*) (method->d2i(NULL, &p, length));
2108 	}
2109 	if (names == NULL) {
2110 		php_openssl_store_errors();
2111 		return -1;
2112 	}
2113 
2114 	num = sk_GENERAL_NAME_num(names);
2115 	for (i = 0; i < num; i++) {
2116 		GENERAL_NAME *name;
2117 		ASN1_STRING *as;
2118 		name = sk_GENERAL_NAME_value(names, i);
2119 		switch (name->type) {
2120 			case GEN_EMAIL:
2121 				BIO_puts(bio, "email:");
2122 				as = name->d.rfc822Name;
2123 				BIO_write(bio, ASN1_STRING_get0_data(as),
2124 					ASN1_STRING_length(as));
2125 				break;
2126 			case GEN_DNS:
2127 				BIO_puts(bio, "DNS:");
2128 				as = name->d.dNSName;
2129 				BIO_write(bio, ASN1_STRING_get0_data(as),
2130 					ASN1_STRING_length(as));
2131 				break;
2132 			case GEN_URI:
2133 				BIO_puts(bio, "URI:");
2134 				as = name->d.uniformResourceIdentifier;
2135 				BIO_write(bio, ASN1_STRING_get0_data(as),
2136 					ASN1_STRING_length(as));
2137 				break;
2138 			default:
2139 				/* use builtin print for GEN_OTHERNAME, GEN_X400,
2140 				 * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID
2141 				 */
2142 				GENERAL_NAME_print(bio, name);
2143 			}
2144 			/* trailing ', ' except for last element */
2145 			if (i < (num - 1)) {
2146 				BIO_puts(bio, ", ");
2147 			}
2148 	}
2149 	sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
2150 
2151 	return 0;
2152 }
2153 
2154 /* {{{ Returns an array of the fields/values of the CERT */
PHP_FUNCTION(openssl_x509_parse)2155 PHP_FUNCTION(openssl_x509_parse)
2156 {
2157 	X509 *cert;
2158 	zend_object *cert_obj;
2159 	zend_string *cert_str;
2160 	int i, sig_nid;
2161 	zend_bool useshortnames = 1;
2162 	char * tmpstr;
2163 	zval subitem;
2164 	X509_EXTENSION *extension;
2165 	X509_NAME *subject_name;
2166 	char *cert_name;
2167 	char *extname;
2168 	BIO *bio_out;
2169 	BUF_MEM *bio_buf;
2170 	ASN1_INTEGER *asn1_serial;
2171 	BIGNUM *bn_serial;
2172 	char *str_serial;
2173 	char *hex_serial;
2174 	char buf[256];
2175 
2176 	ZEND_PARSE_PARAMETERS_START(1, 2)
2177 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2178 		Z_PARAM_OPTIONAL
2179 		Z_PARAM_BOOL(useshortnames)
2180 	ZEND_PARSE_PARAMETERS_END();
2181 
2182 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2183 	if (cert == NULL) {
2184 		// TODO Add Warning?
2185 		RETURN_FALSE;
2186 	}
2187 	array_init(return_value);
2188 
2189 	subject_name = X509_get_subject_name(cert);
2190 	cert_name = X509_NAME_oneline(subject_name, NULL, 0);
2191 	add_assoc_string(return_value, "name", cert_name);
2192 	OPENSSL_free(cert_name);
2193 
2194 	php_openssl_add_assoc_name_entry(return_value, "subject", subject_name, useshortnames);
2195 	/* hash as used in CA directories to lookup cert by subject name */
2196 	{
2197 		char buf[32];
2198 		snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert));
2199 		add_assoc_string(return_value, "hash", buf);
2200 	}
2201 
2202 	php_openssl_add_assoc_name_entry(return_value, "issuer", X509_get_issuer_name(cert), useshortnames);
2203 	add_assoc_long(return_value, "version", X509_get_version(cert));
2204 
2205 	asn1_serial = X509_get_serialNumber(cert);
2206 
2207 	bn_serial = ASN1_INTEGER_to_BN(asn1_serial, NULL);
2208 	/* Can return NULL on error or memory allocation failure */
2209 	if (!bn_serial) {
2210 		php_openssl_store_errors();
2211 		RETURN_FALSE;
2212 	}
2213 
2214 	hex_serial = BN_bn2hex(bn_serial);
2215 	BN_free(bn_serial);
2216 	/* Can return NULL on error or memory allocation failure */
2217 	if (!hex_serial) {
2218 		php_openssl_store_errors();
2219 		RETURN_FALSE;
2220 	}
2221 
2222 	str_serial = i2s_ASN1_INTEGER(NULL, asn1_serial);
2223 	add_assoc_string(return_value, "serialNumber", str_serial);
2224 	OPENSSL_free(str_serial);
2225 
2226 	/* Return the hex representation of the serial number, as defined by OpenSSL */
2227 	add_assoc_string(return_value, "serialNumberHex", hex_serial);
2228 	OPENSSL_free(hex_serial);
2229 
2230 	php_openssl_add_assoc_asn1_string(return_value, "validFrom", 	X509_getm_notBefore(cert));
2231 	php_openssl_add_assoc_asn1_string(return_value, "validTo", 		X509_getm_notAfter(cert));
2232 
2233 	add_assoc_long(return_value, "validFrom_time_t", php_openssl_asn1_time_to_time_t(X509_getm_notBefore(cert)));
2234 	add_assoc_long(return_value, "validTo_time_t",  php_openssl_asn1_time_to_time_t(X509_getm_notAfter(cert)));
2235 
2236 	tmpstr = (char *)X509_alias_get0(cert, NULL);
2237 	if (tmpstr) {
2238 		add_assoc_string(return_value, "alias", tmpstr);
2239 	}
2240 
2241 	sig_nid = X509_get_signature_nid(cert);
2242 	add_assoc_string(return_value, "signatureTypeSN", (char*)OBJ_nid2sn(sig_nid));
2243 	add_assoc_string(return_value, "signatureTypeLN", (char*)OBJ_nid2ln(sig_nid));
2244 	add_assoc_long(return_value, "signatureTypeNID", sig_nid);
2245 	array_init(&subitem);
2246 
2247 	/* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines
2248 	   in x509v3.h */
2249 	for (i = 0; i < X509_PURPOSE_get_count(); i++) {
2250 		int id, purpset;
2251 		char * pname;
2252 		X509_PURPOSE * purp;
2253 		zval subsub;
2254 
2255 		array_init(&subsub);
2256 
2257 		purp = X509_PURPOSE_get0(i);
2258 		id = X509_PURPOSE_get_id(purp);
2259 
2260 		purpset = X509_check_purpose(cert, id, 0);
2261 		add_index_bool(&subsub, 0, purpset);
2262 
2263 		purpset = X509_check_purpose(cert, id, 1);
2264 		add_index_bool(&subsub, 1, purpset);
2265 
2266 		pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp);
2267 		add_index_string(&subsub, 2, pname);
2268 
2269 		/* NOTE: if purpset > 1 then it's a warning - we should mention it ? */
2270 
2271 		add_index_zval(&subitem, id, &subsub);
2272 	}
2273 	add_assoc_zval(return_value, "purposes", &subitem);
2274 
2275 	array_init(&subitem);
2276 
2277 
2278 	for (i = 0; i < X509_get_ext_count(cert); i++) {
2279 		int nid;
2280 		extension = X509_get_ext(cert, i);
2281 		nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
2282 		if (nid != NID_undef) {
2283 			extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension)));
2284 		} else {
2285 			OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1);
2286 			extname = buf;
2287 		}
2288 		bio_out = BIO_new(BIO_s_mem());
2289 		if (bio_out == NULL) {
2290 			php_openssl_store_errors();
2291 			RETURN_FALSE;
2292 		}
2293 		if (nid == NID_subject_alt_name) {
2294 			if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
2295 				BIO_get_mem_ptr(bio_out, &bio_buf);
2296 				add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
2297 			} else {
2298 				zend_array_destroy(Z_ARR_P(return_value));
2299 				BIO_free(bio_out);
2300 				if (cert_str) {
2301 					X509_free(cert);
2302 				}
2303 				RETURN_FALSE;
2304 			}
2305 		}
2306 		else if (X509V3_EXT_print(bio_out, extension, 0, 0)) {
2307 			BIO_get_mem_ptr(bio_out, &bio_buf);
2308 			add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
2309 		} else {
2310 			php_openssl_add_assoc_asn1_string(&subitem, extname, X509_EXTENSION_get_data(extension));
2311 		}
2312 		BIO_free(bio_out);
2313 	}
2314 	add_assoc_zval(return_value, "extensions", &subitem);
2315 	if (cert_str) {
2316 		X509_free(cert);
2317 	}
2318 }
2319 /* }}} */
2320 
2321 /* {{{ php_openssl_load_all_certs_from_file */
STACK_OF(X509)2322 static STACK_OF(X509) *php_openssl_load_all_certs_from_file(
2323 		char *cert_file, size_t cert_file_len, uint32_t arg_num)
2324 {
2325 	STACK_OF(X509_INFO) *sk=NULL;
2326 	STACK_OF(X509) *stack=NULL, *ret=NULL;
2327 	BIO *in=NULL;
2328 	X509_INFO *xi;
2329 	char cert_path[MAXPATHLEN];
2330 
2331 	if(!(stack = sk_X509_new_null())) {
2332 		php_openssl_store_errors();
2333 		php_error_docref(NULL, E_ERROR, "Memory allocation failure");
2334 		goto end;
2335 	}
2336 
2337 	if (!php_openssl_check_path(cert_file, cert_file_len, cert_path, arg_num)) {
2338 		sk_X509_free(stack);
2339 		goto end;
2340 	}
2341 
2342 	if (!(in = BIO_new_file(cert_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY)))) {
2343 		php_openssl_store_errors();
2344 		php_error_docref(NULL, E_WARNING, "Error opening the file, %s", cert_path);
2345 		sk_X509_free(stack);
2346 		goto end;
2347 	}
2348 
2349 	/* This loads from a file, a stack of x509/crl/pkey sets */
2350 	if (!(sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
2351 		php_openssl_store_errors();
2352 		php_error_docref(NULL, E_WARNING, "Error reading the file, %s", cert_path);
2353 		sk_X509_free(stack);
2354 		goto end;
2355 	}
2356 
2357 	/* scan over it and pull out the certs */
2358 	while (sk_X509_INFO_num(sk)) {
2359 		xi=sk_X509_INFO_shift(sk);
2360 		if (xi->x509 != NULL) {
2361 			sk_X509_push(stack,xi->x509);
2362 			xi->x509=NULL;
2363 		}
2364 		X509_INFO_free(xi);
2365 	}
2366 	if (!sk_X509_num(stack)) {
2367 		php_error_docref(NULL, E_WARNING, "No certificates in file, %s", cert_path);
2368 		sk_X509_free(stack);
2369 		goto end;
2370 	}
2371 	ret = stack;
2372 end:
2373 	BIO_free(in);
2374 	sk_X509_INFO_free(sk);
2375 
2376 	return ret;
2377 }
2378 /* }}} */
2379 
2380 /* {{{ check_cert */
check_cert(X509_STORE * ctx,X509 * x,STACK_OF (X509)* untrustedchain,int purpose)2381 static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose)
2382 {
2383 	int ret=0;
2384 	X509_STORE_CTX *csc;
2385 
2386 	csc = X509_STORE_CTX_new();
2387 	if (csc == NULL) {
2388 		php_openssl_store_errors();
2389 		php_error_docref(NULL, E_ERROR, "Memory allocation failure");
2390 		return 0;
2391 	}
2392 	if (!X509_STORE_CTX_init(csc, ctx, x, untrustedchain)) {
2393 		php_openssl_store_errors();
2394 		php_error_docref(NULL, E_WARNING, "Certificate store initialization failed");
2395 		return 0;
2396 	}
2397 	if (purpose >= 0 && !X509_STORE_CTX_set_purpose(csc, purpose)) {
2398 		php_openssl_store_errors();
2399 	}
2400 	ret = X509_verify_cert(csc);
2401 	if (ret < 0) {
2402 		php_openssl_store_errors();
2403 	}
2404 	X509_STORE_CTX_free(csc);
2405 
2406 	return ret;
2407 }
2408 /* }}} */
2409 
2410 /* {{{ Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs */
PHP_FUNCTION(openssl_x509_checkpurpose)2411 PHP_FUNCTION(openssl_x509_checkpurpose)
2412 {
2413 	X509 *cert;
2414 	zend_object *cert_obj;
2415 	zend_string *cert_str;
2416 	zval *zcainfo = NULL;
2417 	X509_STORE *cainfo = NULL;
2418 	STACK_OF(X509) *untrustedchain = NULL;
2419 	zend_long purpose;
2420 	char * untrusted = NULL;
2421 	size_t untrusted_len = 0;
2422 	int ret;
2423 
2424 	ZEND_PARSE_PARAMETERS_START(2, 4)
2425 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2426 		Z_PARAM_LONG(purpose)
2427 		Z_PARAM_OPTIONAL
2428 		Z_PARAM_ARRAY(zcainfo)
2429 		Z_PARAM_STRING_OR_NULL(untrusted, untrusted_len)
2430 	ZEND_PARSE_PARAMETERS_END();
2431 
2432 	RETVAL_LONG(-1);
2433 
2434 	if (untrusted) {
2435 		untrustedchain = php_openssl_load_all_certs_from_file(untrusted, untrusted_len, 4);
2436 		if (untrustedchain == NULL) {
2437 			goto clean_exit;
2438 		}
2439 	}
2440 
2441 	cainfo = php_openssl_setup_verify(zcainfo, 3);
2442 	if (cainfo == NULL) {
2443 		goto clean_exit;
2444 	}
2445 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2446 	if (cert == NULL) {
2447 		// TODO Add Warning?
2448 		goto clean_exit;
2449 	}
2450 
2451 	ret = check_cert(cainfo, cert, untrustedchain, (int)purpose);
2452 	if (ret != 0 && ret != 1) {
2453 		RETVAL_LONG(ret);
2454 	} else {
2455 		RETVAL_BOOL(ret);
2456 	}
2457 	if (cert_str) {
2458 		X509_free(cert);
2459 	}
2460 clean_exit:
2461 	if (cainfo) {
2462 		X509_STORE_free(cainfo);
2463 	}
2464 	if (untrustedchain) {
2465 		sk_X509_pop_free(untrustedchain, X509_free);
2466 	}
2467 }
2468 /* }}} */
2469 
2470 /* {{{ php_openssl_setup_verify
2471  * calist is an array containing file and directory names.  create a
2472  * certificate store and add those certs to it for use in verification.
2473 */
php_openssl_setup_verify(zval * calist,uint32_t arg_num)2474 static X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num)
2475 {
2476 	X509_STORE *store;
2477 	X509_LOOKUP * dir_lookup, * file_lookup;
2478 	int ndirs = 0, nfiles = 0;
2479 	zval * item;
2480 	zend_stat_t sb;
2481 	char file_path[MAXPATHLEN];
2482 
2483 	store = X509_STORE_new();
2484 
2485 	if (store == NULL) {
2486 		php_openssl_store_errors();
2487 		return NULL;
2488 	}
2489 
2490 	if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) {
2491 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(calist), item) {
2492 			zend_string *str = zval_try_get_string(item);
2493 			if (UNEXPECTED(!str)) {
2494 				return NULL;
2495 			}
2496 
2497 			if (!php_openssl_check_path_str_ex(str, file_path, arg_num, false, true, NULL)) {
2498 				zend_string_release(str);
2499 				continue;
2500 			}
2501 			zend_string_release(str);
2502 
2503 			if (VCWD_STAT(file_path, &sb) == -1) {
2504 				php_error_docref(NULL, E_WARNING, "Unable to stat %s", file_path);
2505 				continue;
2506 			}
2507 
2508 			if ((sb.st_mode & S_IFREG) == S_IFREG) {
2509 				file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
2510 				if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, file_path, X509_FILETYPE_PEM)) {
2511 					php_openssl_store_errors();
2512 					php_error_docref(NULL, E_WARNING, "Error loading file %s", file_path);
2513 				} else {
2514 					nfiles++;
2515 				}
2516 				file_lookup = NULL;
2517 			} else {
2518 				dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
2519 				if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, file_path, X509_FILETYPE_PEM)) {
2520 					php_openssl_store_errors();
2521 					php_error_docref(NULL, E_WARNING, "Error loading directory %s", file_path);
2522 				} else {
2523 					ndirs++;
2524 				}
2525 				dir_lookup = NULL;
2526 			}
2527 		} ZEND_HASH_FOREACH_END();
2528 	}
2529 	if (nfiles == 0) {
2530 		file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
2531 		if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT)) {
2532 			php_openssl_store_errors();
2533 		}
2534 	}
2535 	if (ndirs == 0) {
2536 		dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
2537 		if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT)) {
2538 			php_openssl_store_errors();
2539 		}
2540 	}
2541 	return store;
2542 }
2543 /* }}} */
2544 
2545 /* {{{ Reads X.509 certificates */
PHP_FUNCTION(openssl_x509_read)2546 PHP_FUNCTION(openssl_x509_read)
2547 {
2548 	X509 *cert;
2549 	php_openssl_certificate_object *x509_cert_obj;
2550 	zend_object *cert_obj;
2551 	zend_string *cert_str;
2552 
2553 	ZEND_PARSE_PARAMETERS_START(1, 1)
2554 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2555 	ZEND_PARSE_PARAMETERS_END();
2556 
2557 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2558 	if (cert == NULL) {
2559 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
2560 		RETURN_FALSE;
2561 	}
2562 
2563 	object_init_ex(return_value, php_openssl_certificate_ce);
2564 	x509_cert_obj = Z_OPENSSL_CERTIFICATE_P(return_value);
2565 	x509_cert_obj->x509 = cert_obj ? X509_dup(cert) : cert;
2566 }
2567 /* }}} */
2568 
2569 /* {{{ Frees X.509 certificates */
PHP_FUNCTION(openssl_x509_free)2570 PHP_FUNCTION(openssl_x509_free)
2571 {
2572 	zval *x509;
2573 
2574 	ZEND_PARSE_PARAMETERS_START(1, 1)
2575 		Z_PARAM_OBJECT_OF_CLASS(x509, php_openssl_certificate_ce)
2576 	ZEND_PARSE_PARAMETERS_END();
2577 }
2578 /* }}} */
2579 
2580 /* }}} */
2581 
2582 /* Pop all X509 from Stack and free them, free the stack afterwards */
php_sk_X509_free(STACK_OF (X509)* sk)2583 static void php_sk_X509_free(STACK_OF(X509) * sk) /* {{{ */
2584 {
2585 	for (;;) {
2586 		X509* x = sk_X509_pop(sk);
2587 		if (!x) break;
2588 		X509_free(x);
2589 	}
2590 	sk_X509_free(sk);
2591 }
2592 /* }}} */
2593 
STACK_OF(X509)2594 static STACK_OF(X509) *php_array_to_X509_sk(zval * zcerts, uint32_t arg_num, const char *option_name) /* {{{ */
2595 {
2596 	zval * zcertval;
2597 	STACK_OF(X509) * sk = NULL;
2598 	X509 * cert;
2599 	bool free_cert;
2600 
2601 	sk = sk_X509_new_null();
2602 
2603 	/* get certs */
2604 	if (Z_TYPE_P(zcerts) == IS_ARRAY) {
2605 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zcerts), zcertval) {
2606 			cert = php_openssl_x509_from_zval(zcertval, &free_cert, arg_num, true, option_name);
2607 			if (cert == NULL) {
2608 				// TODO Add Warning?
2609 				goto clean_exit;
2610 			}
2611 
2612 			if (!free_cert) {
2613 				cert = X509_dup(cert);
2614 
2615 				if (cert == NULL) {
2616 					php_openssl_store_errors();
2617 					goto clean_exit;
2618 				}
2619 
2620 			}
2621 			sk_X509_push(sk, cert);
2622 		} ZEND_HASH_FOREACH_END();
2623 	} else {
2624 		/* a single certificate */
2625 		cert = php_openssl_x509_from_zval(zcerts, &free_cert, arg_num, false, option_name);
2626 
2627 		if (cert == NULL) {
2628 			// TODO Add Warning?
2629 			goto clean_exit;
2630 		}
2631 
2632 		if (!free_cert) {
2633 			cert = X509_dup(cert);
2634 			if (cert == NULL) {
2635 				php_openssl_store_errors();
2636 				goto clean_exit;
2637 			}
2638 		}
2639 		sk_X509_push(sk, cert);
2640 	}
2641 
2642 clean_exit:
2643 	return sk;
2644 }
2645 /* }}} */
2646 
2647 /* {{{ Creates and exports a PKCS to file */
PHP_FUNCTION(openssl_pkcs12_export_to_file)2648 PHP_FUNCTION(openssl_pkcs12_export_to_file)
2649 {
2650 	X509 *cert;
2651 	zend_object *cert_obj;
2652 	zend_string *cert_str;
2653 	BIO * bio_out = NULL;
2654 	PKCS12 * p12 = NULL;
2655 	char * filename, file_path[MAXPATHLEN];
2656 	char * friendly_name = NULL;
2657 	size_t filename_len;
2658 	char * pass;
2659 	size_t pass_len;
2660 	zval *zpkey = NULL, *args = NULL;
2661 	EVP_PKEY *priv_key = NULL;
2662 	zval * item;
2663 	STACK_OF(X509) *ca = NULL;
2664 
2665 	ZEND_PARSE_PARAMETERS_START(4, 5)
2666 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2667 		Z_PARAM_PATH(filename, filename_len)
2668 		Z_PARAM_ZVAL(zpkey)
2669 		Z_PARAM_STRING(pass, pass_len)
2670 		Z_PARAM_OPTIONAL
2671 		Z_PARAM_ARRAY(args)
2672 	ZEND_PARSE_PARAMETERS_END();
2673 
2674 	RETVAL_FALSE;
2675 
2676 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2677 	if (cert == NULL) {
2678 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
2679 		return;
2680 	}
2681 
2682 	priv_key = php_openssl_pkey_from_zval(zpkey, 0, "", 0, 3);
2683 	if (priv_key == NULL) {
2684 		if (!EG(exception)) {
2685 			php_error_docref(NULL, E_WARNING, "Cannot get private key from parameter 3");
2686 		}
2687 		goto cleanup;
2688 	}
2689 	if (!X509_check_private_key(cert, priv_key)) {
2690 		php_openssl_store_errors();
2691 		php_error_docref(NULL, E_WARNING, "Private key does not correspond to cert");
2692 		goto cleanup;
2693 	}
2694 	if (!php_openssl_check_path(filename, filename_len, file_path, 2)) {
2695 		goto cleanup;
2696 	}
2697 
2698 	/* parse extra config from args array, promote this to an extra function */
2699 	if (args &&
2700 		(item = zend_hash_str_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name")-1)) != NULL &&
2701 		Z_TYPE_P(item) == IS_STRING
2702 	) {
2703 		friendly_name = Z_STRVAL_P(item);
2704 	}
2705 	/* certpbe (default RC2-40)
2706 	   keypbe (default 3DES)
2707 	   friendly_caname
2708 	*/
2709 
2710 	if (args && (item = zend_hash_str_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts")-1)) != NULL) {
2711 		ca = php_array_to_X509_sk(item, 5, "extracerts");
2712 	}
2713 	/* end parse extra config */
2714 
2715 	/*PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ca,
2716 				int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/
2717 
2718 	p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
2719 	if (p12 != NULL) {
2720 		bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
2721 		if (bio_out != NULL) {
2722 
2723 			i2d_PKCS12_bio(bio_out, p12);
2724 			BIO_free(bio_out);
2725 
2726 			RETVAL_TRUE;
2727 		} else {
2728 			php_openssl_store_errors();
2729 			php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
2730 		}
2731 
2732 		PKCS12_free(p12);
2733 	} else {
2734 		php_openssl_store_errors();
2735 	}
2736 
2737 	php_sk_X509_free(ca);
2738 
2739 cleanup:
2740 	EVP_PKEY_free(priv_key);
2741 
2742 	if (cert_str) {
2743 		X509_free(cert);
2744 	}
2745 }
2746 /* }}} */
2747 
2748 /* {{{ Creates and exports a PKCS12 to a var */
PHP_FUNCTION(openssl_pkcs12_export)2749 PHP_FUNCTION(openssl_pkcs12_export)
2750 {
2751 	X509 *cert;
2752 	zend_object *cert_obj;
2753 	zend_string *cert_str;
2754 	BIO * bio_out;
2755 	PKCS12 * p12 = NULL;
2756 	zval *zout = NULL, *zpkey, *args = NULL;
2757 	EVP_PKEY *priv_key = NULL;
2758 	char * pass;
2759 	size_t pass_len;
2760 	char * friendly_name = NULL;
2761 	zval * item;
2762 	STACK_OF(X509) *ca = NULL;
2763 
2764 	ZEND_PARSE_PARAMETERS_START(4, 5)
2765 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2766 		Z_PARAM_ZVAL(zout)
2767 		Z_PARAM_ZVAL(zpkey)
2768 		Z_PARAM_STRING(pass, pass_len)
2769 		Z_PARAM_OPTIONAL
2770 		Z_PARAM_ARRAY(args)
2771 	ZEND_PARSE_PARAMETERS_END();
2772 
2773 	RETVAL_FALSE;
2774 
2775 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2776 	if (cert == NULL) {
2777 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
2778 		return;
2779 	}
2780 
2781 	priv_key = php_openssl_pkey_from_zval(zpkey, 0, "", 0, 3);
2782 	if (priv_key == NULL) {
2783 		if (!EG(exception)) {
2784 			php_error_docref(NULL, E_WARNING, "Cannot get private key from parameter 3");
2785 		}
2786 		goto cleanup;
2787 	}
2788 	if (!X509_check_private_key(cert, priv_key)) {
2789 		php_error_docref(NULL, E_WARNING, "Private key does not correspond to cert");
2790 		goto cleanup;
2791 	}
2792 
2793 	/* parse extra config from args array, promote this to an extra function */
2794 	if (args &&
2795 		(item = zend_hash_str_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name")-1)) != NULL &&
2796 		Z_TYPE_P(item) == IS_STRING
2797 	) {
2798 		friendly_name = Z_STRVAL_P(item);
2799 	}
2800 
2801 	if (args && (item = zend_hash_str_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts")-1)) != NULL) {
2802 		ca = php_array_to_X509_sk(item, 5, "extracerts");
2803 	}
2804 	/* end parse extra config */
2805 
2806 	p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
2807 
2808 	if (p12 != NULL) {
2809 		bio_out = BIO_new(BIO_s_mem());
2810 		if (i2d_PKCS12_bio(bio_out, p12)) {
2811 			BUF_MEM *bio_buf;
2812 
2813 			BIO_get_mem_ptr(bio_out, &bio_buf);
2814 			ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
2815 
2816 			RETVAL_TRUE;
2817 		} else {
2818 			php_openssl_store_errors();
2819 		}
2820 
2821 		BIO_free(bio_out);
2822 		PKCS12_free(p12);
2823 	} else {
2824 		php_openssl_store_errors();
2825 	}
2826 	php_sk_X509_free(ca);
2827 
2828 cleanup:
2829 	EVP_PKEY_free(priv_key);
2830 	if (cert_str) {
2831 		X509_free(cert);
2832 	}
2833 }
2834 /* }}} */
2835 
2836 /* {{{ Parses a PKCS12 to an array */
PHP_FUNCTION(openssl_pkcs12_read)2837 PHP_FUNCTION(openssl_pkcs12_read)
2838 {
2839 	zval *zout = NULL, zextracerts, zcert, zpkey;
2840 	char *pass, *zp12;
2841 	size_t pass_len, zp12_len;
2842 	PKCS12 * p12 = NULL;
2843 	EVP_PKEY * pkey = NULL;
2844 	X509 * cert = NULL;
2845 	STACK_OF(X509) * ca = NULL;
2846 	BIO * bio_in = NULL;
2847 	int i;
2848 
2849 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szs", &zp12, &zp12_len, &zout, &pass, &pass_len) == FAILURE) {
2850 		RETURN_THROWS();
2851 	}
2852 
2853 	RETVAL_FALSE;
2854 
2855 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(zp12_len, pkcs12, 1);
2856 
2857 	bio_in = BIO_new(BIO_s_mem());
2858 
2859 	if (0 >= BIO_write(bio_in, zp12, (int)zp12_len)) {
2860 		php_openssl_store_errors();
2861 		goto cleanup;
2862 	}
2863 
2864 	if (d2i_PKCS12_bio(bio_in, &p12) && PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
2865 		BIO * bio_out;
2866 		int cert_num;
2867 
2868 		zout = zend_try_array_init(zout);
2869 		if (!zout) {
2870 			goto cleanup;
2871 		}
2872 
2873 		if (cert) {
2874 			bio_out = BIO_new(BIO_s_mem());
2875 			if (PEM_write_bio_X509(bio_out, cert)) {
2876 				BUF_MEM *bio_buf;
2877 				BIO_get_mem_ptr(bio_out, &bio_buf);
2878 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
2879 				add_assoc_zval(zout, "cert", &zcert);
2880 			} else {
2881 				php_openssl_store_errors();
2882 			}
2883 			BIO_free(bio_out);
2884 		}
2885 
2886 		if (pkey) {
2887 			bio_out = BIO_new(BIO_s_mem());
2888 			if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
2889 				BUF_MEM *bio_buf;
2890 				BIO_get_mem_ptr(bio_out, &bio_buf);
2891 				ZVAL_STRINGL(&zpkey, bio_buf->data, bio_buf->length);
2892 				add_assoc_zval(zout, "pkey", &zpkey);
2893 			} else {
2894 				php_openssl_store_errors();
2895 			}
2896 			BIO_free(bio_out);
2897 		}
2898 
2899 		cert_num = sk_X509_num(ca);
2900 		if (ca && cert_num) {
2901 			array_init(&zextracerts);
2902 
2903 			for (i = 0; i < cert_num; i++) {
2904 				zval zextracert;
2905 				X509* aCA = sk_X509_pop(ca);
2906 				if (!aCA) break;
2907 
2908 				bio_out = BIO_new(BIO_s_mem());
2909 				if (PEM_write_bio_X509(bio_out, aCA)) {
2910 					BUF_MEM *bio_buf;
2911 					BIO_get_mem_ptr(bio_out, &bio_buf);
2912 					ZVAL_STRINGL(&zextracert, bio_buf->data, bio_buf->length);
2913 					add_index_zval(&zextracerts, i, &zextracert);
2914 				}
2915 
2916 				X509_free(aCA);
2917 				BIO_free(bio_out);
2918 			}
2919 
2920 			sk_X509_free(ca);
2921 			add_assoc_zval(zout, "extracerts", &zextracerts);
2922 		}
2923 
2924 		RETVAL_TRUE;
2925 	} else {
2926 		php_openssl_store_errors();
2927 	}
2928 
2929 cleanup:
2930 	BIO_free(bio_in);
2931 	EVP_PKEY_free(pkey);
2932 	if (cert) {
2933 		X509_free(cert);
2934 	}
2935 	if (p12) {
2936 		PKCS12_free(p12);
2937 	}
2938 }
2939 /* }}} */
2940 
2941 /* {{{ x509 CSR functions */
2942 
2943 /* {{{ php_openssl_make_REQ */
php_openssl_make_REQ(struct php_x509_request * req,X509_REQ * csr,zval * dn,zval * attribs)2944 static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs)
2945 {
2946 	STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
2947 	char * str, *dn_sect, *attr_sect;
2948 
2949 	dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name");
2950 	if (dn_sect == NULL) {
2951 		php_openssl_store_errors();
2952 		return FAILURE;
2953 	}
2954 	dn_sk = CONF_get_section(req->req_config, dn_sect);
2955 	if (dn_sk == NULL) {
2956 		php_openssl_store_errors();
2957 		return FAILURE;
2958 	}
2959 	attr_sect = php_openssl_conf_get_string(req->req_config, req->section_name, "attributes");
2960 	if (attr_sect == NULL) {
2961 		attr_sk = NULL;
2962 	} else {
2963 		attr_sk = CONF_get_section(req->req_config, attr_sect);
2964 		if (attr_sk == NULL) {
2965 			php_openssl_store_errors();
2966 			return FAILURE;
2967 		}
2968 	}
2969 	/* setup the version number: version 1 */
2970 	if (X509_REQ_set_version(csr, 0L)) {
2971 		int i, nid;
2972 		char * type;
2973 		CONF_VALUE * v;
2974 		X509_NAME * subj;
2975 		zval * item;
2976 		zend_string * strindex = NULL;
2977 
2978 		subj = X509_REQ_get_subject_name(csr);
2979 		/* apply values from the dn hash */
2980 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(dn), strindex, item) {
2981 			if (strindex) {
2982 				int nid = OBJ_txt2nid(ZSTR_VAL(strindex));
2983 				if (nid != NID_undef) {
2984 					zend_string *str_item = zval_try_get_string(item);
2985 					if (UNEXPECTED(!str_item)) {
2986 						return FAILURE;
2987 					}
2988 					if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8,
2989 								(unsigned char*)ZSTR_VAL(str_item), -1, -1, 0))
2990 					{
2991 						php_openssl_store_errors();
2992 						php_error_docref(NULL, E_WARNING,
2993 							"dn: add_entry_by_NID %d -> %s (failed; check error"
2994 							" queue and value of string_mask OpenSSL option "
2995 							"if illegal characters are reported)",
2996 							nid, ZSTR_VAL(str_item));
2997 						zend_string_release(str_item);
2998 						return FAILURE;
2999 					}
3000 					zend_string_release(str_item);
3001 				} else {
3002 					php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex));
3003 				}
3004 			}
3005 		} ZEND_HASH_FOREACH_END();
3006 
3007 		/* Finally apply defaults from config file */
3008 		for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
3009 			size_t len;
3010 			char buffer[200 + 1]; /*200 + \0 !*/
3011 
3012 			v = sk_CONF_VALUE_value(dn_sk, i);
3013 			type = v->name;
3014 
3015 			len = strlen(type);
3016 			if (len < sizeof("_default")) {
3017 				continue;
3018 			}
3019 			len -= sizeof("_default") - 1;
3020 			if (strcmp("_default", type + len) != 0) {
3021 				continue;
3022 			}
3023 			if (len > 200) {
3024 				len = 200;
3025 			}
3026 			memcpy(buffer, type, len);
3027 			buffer[len] = '\0';
3028 			type = buffer;
3029 
3030 			/* Skip past any leading X. X: X, etc to allow for multiple
3031 			 * instances */
3032 			for (str = type; *str; str++) {
3033 				if (*str == ':' || *str == ',' || *str == '.') {
3034 					str++;
3035 					if (*str) {
3036 						type = str;
3037 					}
3038 					break;
3039 				}
3040 			}
3041 			/* if it is already set, skip this */
3042 			nid = OBJ_txt2nid(type);
3043 			if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) {
3044 				continue;
3045 			}
3046 			if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) {
3047 				php_openssl_store_errors();
3048 				php_error_docref(NULL, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
3049 				return FAILURE;
3050 			}
3051 			if (!X509_NAME_entry_count(subj)) {
3052 				php_error_docref(NULL, E_WARNING, "No objects specified in config file");
3053 				return FAILURE;
3054 			}
3055 		}
3056 		if (attribs) {
3057 			ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(attribs), strindex, item) {
3058 				int nid;
3059 
3060 				if (NULL == strindex) {
3061 					php_error_docref(NULL, E_WARNING, "dn: numeric fild names are not supported");
3062 					continue;
3063 				}
3064 
3065 				nid = OBJ_txt2nid(ZSTR_VAL(strindex));
3066 				if (nid != NID_undef) {
3067 					zend_string *str_item = zval_try_get_string(item);
3068 					if (UNEXPECTED(!str_item)) {
3069 						return FAILURE;
3070 					}
3071 					if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)ZSTR_VAL(str_item), -1, -1, 0)) {
3072 						php_openssl_store_errors();
3073 						php_error_docref(NULL, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, ZSTR_VAL(str_item));
3074 						zend_string_release(str_item);
3075 						return FAILURE;
3076 					}
3077 					zend_string_release(str_item);
3078 				} else {
3079 					php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex));
3080 				}
3081 			} ZEND_HASH_FOREACH_END();
3082 			for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
3083 				v = sk_CONF_VALUE_value(attr_sk, i);
3084 				/* if it is already set, skip this */
3085 				nid = OBJ_txt2nid(v->name);
3086 				if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) {
3087 					continue;
3088 				}
3089 				if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) {
3090 					php_openssl_store_errors();
3091 					php_error_docref(NULL, E_WARNING,
3092 						"add1_attr_by_txt %s -> %s (failed; check error queue "
3093 						"and value of string_mask OpenSSL option if illegal "
3094 						"characters are reported)",
3095 						v->name, v->value);
3096 					return FAILURE;
3097 				}
3098 			}
3099 		}
3100 	} else {
3101 		php_openssl_store_errors();
3102 	}
3103 
3104 	if (!X509_REQ_set_pubkey(csr, req->priv_key)) {
3105 		php_openssl_store_errors();
3106 	}
3107 	return SUCCESS;
3108 }
3109 /* }}} */
3110 
3111 
php_openssl_csr_from_str(zend_string * csr_str,uint32_t arg_num)3112 static X509_REQ *php_openssl_csr_from_str(zend_string *csr_str, uint32_t arg_num)
3113 {
3114 	X509_REQ * csr = NULL;
3115 	char file_path[MAXPATHLEN];
3116 	BIO * in;
3117 
3118 	if (ZSTR_LEN(csr_str) > 7 && memcmp(ZSTR_VAL(csr_str), "file://", sizeof("file://") - 1) == 0) {
3119 		if (!php_openssl_check_path_str(csr_str, file_path, arg_num)) {
3120 			return NULL;
3121 		}
3122 		in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3123 	} else {
3124 		in = BIO_new_mem_buf(ZSTR_VAL(csr_str), (int) ZSTR_LEN(csr_str));
3125 	}
3126 
3127 	if (in == NULL) {
3128 		php_openssl_store_errors();
3129 		return NULL;
3130 	}
3131 
3132 	csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
3133 	if (csr == NULL) {
3134 		php_openssl_store_errors();
3135 	}
3136 
3137 	BIO_free(in);
3138 
3139 	return csr;
3140 }
3141 
php_openssl_csr_from_param(zend_object * csr_obj,zend_string * csr_str,uint32_t arg_num)3142 static X509_REQ *php_openssl_csr_from_param(
3143 		zend_object *csr_obj, zend_string *csr_str, uint32_t arg_num)
3144 {
3145 	if (csr_obj) {
3146 		return php_openssl_request_from_obj(csr_obj)->csr;
3147 	}
3148 
3149 	ZEND_ASSERT(csr_str);
3150 
3151 	return php_openssl_csr_from_str(csr_str, arg_num);
3152 }
3153 
3154 /* {{{ Exports a CSR to file */
PHP_FUNCTION(openssl_csr_export_to_file)3155 PHP_FUNCTION(openssl_csr_export_to_file)
3156 {
3157 	X509_REQ *csr;
3158 	zend_object *csr_obj;
3159 	zend_string *csr_str;
3160 	zend_bool notext = 1;
3161 	char * filename = NULL;
3162 	size_t filename_len;
3163 	char file_path[MAXPATHLEN];
3164 	BIO * bio_out;
3165 
3166 	ZEND_PARSE_PARAMETERS_START(2, 3)
3167 		Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3168 		Z_PARAM_PATH(filename, filename_len)
3169 		Z_PARAM_OPTIONAL
3170 		Z_PARAM_BOOL(notext)
3171 	ZEND_PARSE_PARAMETERS_END();
3172 
3173 	RETVAL_FALSE;
3174 
3175 	csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3176 	if (csr == NULL) {
3177 		php_error_docref(NULL, E_WARNING, "X.509 Certificate Signing Request cannot be retrieved");
3178 		return;
3179 	}
3180 
3181 	if (!php_openssl_check_path(filename, filename_len, file_path, 2)) {
3182 		return;
3183 	}
3184 
3185 	bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
3186 	if (bio_out != NULL) {
3187 		if (!notext && !X509_REQ_print(bio_out, csr)) {
3188 			php_openssl_store_errors();
3189 		}
3190 		if (!PEM_write_bio_X509_REQ(bio_out, csr)) {
3191 			php_error_docref(NULL, E_WARNING, "Error writing PEM to file %s", file_path);
3192 			php_openssl_store_errors();
3193 		} else {
3194 			RETVAL_TRUE;
3195 		}
3196 		BIO_free(bio_out);
3197 	} else {
3198 		php_openssl_store_errors();
3199 		php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
3200 	}
3201 
3202 	if (csr_str) {
3203 		X509_REQ_free(csr);
3204 	}
3205 }
3206 /* }}} */
3207 
3208 /* {{{ Exports a CSR to file or a var */
PHP_FUNCTION(openssl_csr_export)3209 PHP_FUNCTION(openssl_csr_export)
3210 {
3211 	X509_REQ *csr;
3212 	zend_object *csr_obj;
3213 	zend_string *csr_str;
3214 	zval *zout;
3215 	zend_bool notext = 1;
3216 	BIO * bio_out;
3217 
3218 	ZEND_PARSE_PARAMETERS_START(2, 3)
3219 		Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3220 		Z_PARAM_ZVAL(zout)
3221 		Z_PARAM_OPTIONAL
3222 		Z_PARAM_BOOL(notext)
3223 	ZEND_PARSE_PARAMETERS_END();
3224 
3225 	RETVAL_FALSE;
3226 
3227 	csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3228 	if (csr == NULL) {
3229 		php_error_docref(NULL, E_WARNING, "X.509 Certificate Signing Request cannot be retrieved");
3230 		return;
3231 	}
3232 
3233 	/* export to a var */
3234 
3235 	bio_out = BIO_new(BIO_s_mem());
3236 	if (!notext && !X509_REQ_print(bio_out, csr)) {
3237 		php_openssl_store_errors();
3238 	}
3239 
3240 	if (PEM_write_bio_X509_REQ(bio_out, csr)) {
3241 		BUF_MEM *bio_buf;
3242 
3243 		BIO_get_mem_ptr(bio_out, &bio_buf);
3244 		ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
3245 
3246 		RETVAL_TRUE;
3247 	} else {
3248 		php_openssl_store_errors();
3249 	}
3250 
3251 	if (csr_str) {
3252 		X509_REQ_free(csr);
3253 	}
3254 	BIO_free(bio_out);
3255 }
3256 /* }}} */
3257 
3258 /* {{{ Signs a cert with another CERT */
PHP_FUNCTION(openssl_csr_sign)3259 PHP_FUNCTION(openssl_csr_sign)
3260 {
3261 	X509_REQ *csr;
3262 	zend_object *csr_obj;
3263 	zend_string *csr_str;
3264 
3265 	php_openssl_certificate_object *cert_object;
3266 	zend_object *cert_obj;
3267 	zend_string *cert_str;
3268 	zval *zpkey, *args = NULL;
3269 	zend_long num_days;
3270 	zend_long serial = Z_L(0);
3271 	X509 *cert = NULL, *new_cert = NULL;
3272 	EVP_PKEY * key = NULL, *priv_key = NULL;
3273 	int i;
3274 	struct php_x509_request req;
3275 
3276 	ZEND_PARSE_PARAMETERS_START(4, 6)
3277 		Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3278 		Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(cert_obj, php_openssl_certificate_ce, cert_str)
3279 		Z_PARAM_ZVAL(zpkey)
3280 		Z_PARAM_LONG(num_days)
3281 		Z_PARAM_OPTIONAL
3282 		Z_PARAM_ARRAY_OR_NULL(args)
3283 		Z_PARAM_LONG(serial)
3284 	ZEND_PARSE_PARAMETERS_END();
3285 
3286 	RETVAL_FALSE;
3287 
3288 	csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3289 	if (csr == NULL) {
3290 		php_error_docref(NULL, E_WARNING, "X.509 Certificate Signing Request cannot be retrieved");
3291 		return;
3292 	}
3293 
3294 	PHP_SSL_REQ_INIT(&req);
3295 
3296 	if (cert_str || cert_obj) {
3297 		cert = php_openssl_x509_from_param(cert_obj, cert_str, 2);
3298 		if (cert == NULL) {
3299 			php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
3300 			goto cleanup;
3301 		}
3302 	}
3303 
3304 	priv_key = php_openssl_pkey_from_zval(zpkey, 0, "", 0, 3);
3305 	if (priv_key == NULL) {
3306 		if (!EG(exception)) {
3307 			php_error_docref(NULL, E_WARNING, "Cannot get private key from parameter 3");
3308 		}
3309 		goto cleanup;
3310 	}
3311 	if (cert && !X509_check_private_key(cert, priv_key)) {
3312 		php_openssl_store_errors();
3313 		php_error_docref(NULL, E_WARNING, "Private key does not correspond to signing cert");
3314 		goto cleanup;
3315 	}
3316 
3317 	if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) {
3318 		goto cleanup;
3319 	}
3320 	/* Check that the request matches the signature */
3321 	key = X509_REQ_get_pubkey(csr);
3322 	if (key == NULL) {
3323 		php_openssl_store_errors();
3324 		php_error_docref(NULL, E_WARNING, "Error unpacking public key");
3325 		goto cleanup;
3326 	}
3327 	i = X509_REQ_verify(csr, key);
3328 
3329 	if (i < 0) {
3330 		php_openssl_store_errors();
3331 		php_error_docref(NULL, E_WARNING, "Signature verification problems");
3332 		goto cleanup;
3333 	}
3334 	else if (i == 0) {
3335 		php_error_docref(NULL, E_WARNING, "Signature did not match the certificate request");
3336 		goto cleanup;
3337 	}
3338 
3339 	/* Now we can get on with it */
3340 
3341 	new_cert = X509_new();
3342 	if (new_cert == NULL) {
3343 		php_openssl_store_errors();
3344 		php_error_docref(NULL, E_WARNING, "No memory");
3345 		goto cleanup;
3346 	}
3347 	/* Version 3 cert */
3348 	if (!X509_set_version(new_cert, 2)) {
3349 		goto cleanup;
3350 	}
3351 
3352 #if PHP_OPENSSL_API_VERSION >= 0x10100 && !defined (LIBRESSL_VERSION_NUMBER)
3353 	ASN1_INTEGER_set_int64(X509_get_serialNumber(new_cert), serial);
3354 #else
3355 	ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial);
3356 #endif
3357 
3358 	X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr));
3359 
3360 	if (cert == NULL) {
3361 		cert = new_cert;
3362 	}
3363 	if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
3364 		php_openssl_store_errors();
3365 		goto cleanup;
3366 	}
3367 	X509_gmtime_adj(X509_getm_notBefore(new_cert), 0);
3368 	X509_gmtime_adj(X509_getm_notAfter(new_cert), 60*60*24*(long)num_days);
3369 	i = X509_set_pubkey(new_cert, key);
3370 	if (!i) {
3371 		php_openssl_store_errors();
3372 		goto cleanup;
3373 	}
3374 	if (req.extensions_section) {
3375 		X509V3_CTX ctx;
3376 
3377 		X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
3378 		X509V3_set_conf_lhash(&ctx, req.req_config);
3379 		if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) {
3380 			php_openssl_store_errors();
3381 			goto cleanup;
3382 		}
3383 	}
3384 
3385 	/* Now sign it */
3386 	if (!X509_sign(new_cert, priv_key, req.digest)) {
3387 		php_openssl_store_errors();
3388 		php_error_docref(NULL, E_WARNING, "Failed to sign it");
3389 		goto cleanup;
3390 	}
3391 
3392 	object_init_ex(return_value, php_openssl_certificate_ce);
3393 	cert_object = Z_OPENSSL_CERTIFICATE_P(return_value);
3394 	cert_object->x509 = new_cert;
3395 
3396 cleanup:
3397 
3398 	if (cert == new_cert) {
3399 		cert = NULL;
3400 	}
3401 
3402 	PHP_SSL_REQ_DISPOSE(&req);
3403 	EVP_PKEY_free(priv_key);
3404 	EVP_PKEY_free(key);
3405 	if (csr_str) {
3406 		X509_REQ_free(csr);
3407 	}
3408 	if (cert_str && cert) {
3409 		X509_free(cert);
3410 	}
3411 }
3412 /* }}} */
3413 
3414 /* {{{ Generates a privkey and CSR */
PHP_FUNCTION(openssl_csr_new)3415 PHP_FUNCTION(openssl_csr_new)
3416 {
3417 	struct php_x509_request req;
3418 	php_openssl_request_object *x509_request_obj;
3419 	zval *args = NULL, *dn, *attribs = NULL;
3420 	zval *out_pkey;
3421 	X509_REQ *csr = NULL;
3422 
3423 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) {
3424 		RETURN_THROWS();
3425 	}
3426 	RETVAL_FALSE;
3427 
3428 	PHP_SSL_REQ_INIT(&req);
3429 
3430 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3431 		int we_made_the_key = 0;
3432 		zval *out_pkey_val = out_pkey;
3433 		ZVAL_DEREF(out_pkey_val);
3434 
3435 		/* Generate or use a private key */
3436 		if (Z_TYPE_P(out_pkey_val) != IS_NULL) {
3437 			req.priv_key = php_openssl_pkey_from_zval(out_pkey_val, 0, NULL, 0, 2);
3438 		}
3439 		if (req.priv_key == NULL) {
3440 			php_openssl_generate_private_key(&req);
3441 			we_made_the_key = 1;
3442 		}
3443 		if (req.priv_key == NULL) {
3444 			php_error_docref(NULL, E_WARNING, "Unable to generate a private key");
3445 		} else {
3446 			csr = X509_REQ_new();
3447 			if (csr) {
3448 				if (php_openssl_make_REQ(&req, csr, dn, attribs) == SUCCESS) {
3449 					X509V3_CTX ext_ctx;
3450 
3451 					X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0);
3452 					X509V3_set_conf_lhash(&ext_ctx, req.req_config);
3453 
3454 					/* Add extensions */
3455 					if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
3456 								&ext_ctx, req.request_extensions_section, csr))
3457 					{
3458 						php_openssl_store_errors();
3459 						php_error_docref(NULL, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
3460 					} else {
3461 						RETVAL_TRUE;
3462 
3463 						if (X509_REQ_sign(csr, req.priv_key, req.digest)) {
3464 							object_init_ex(return_value, php_openssl_request_ce);
3465 							x509_request_obj = Z_OPENSSL_REQUEST_P(return_value);
3466 							x509_request_obj->csr = csr;
3467 							csr = NULL;
3468 						} else {
3469 							php_openssl_store_errors();
3470 							php_error_docref(NULL, E_WARNING, "Error signing request");
3471 						}
3472 
3473 						if (we_made_the_key) {
3474 							/* and an object for the private key */
3475 							zval zkey_object;
3476 							php_openssl_pkey_object *key_object;
3477 							object_init_ex(&zkey_object, php_openssl_pkey_ce);
3478 							key_object = Z_OPENSSL_PKEY_P(&zkey_object);
3479 							key_object->pkey = req.priv_key;
3480 
3481 							ZEND_TRY_ASSIGN_REF_TMP(out_pkey, &zkey_object);
3482 							req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
3483 						}
3484 					}
3485 				}
3486 			} else {
3487 				php_openssl_store_errors();
3488 			}
3489 
3490 		}
3491 	}
3492 	if (csr) {
3493 		X509_REQ_free(csr);
3494 	}
3495 	PHP_SSL_REQ_DISPOSE(&req);
3496 }
3497 /* }}} */
3498 
3499 /* {{{ Returns the subject of a CERT or FALSE on error */
PHP_FUNCTION(openssl_csr_get_subject)3500 PHP_FUNCTION(openssl_csr_get_subject)
3501 {
3502 	X509_REQ *csr;
3503 	zend_object *csr_obj;
3504 	zend_string *csr_str;
3505 	zend_bool use_shortnames = 1;
3506 	X509_NAME *subject;
3507 
3508 	ZEND_PARSE_PARAMETERS_START(1, 2)
3509 		Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3510 		Z_PARAM_OPTIONAL
3511 		Z_PARAM_BOOL(use_shortnames)
3512 	ZEND_PARSE_PARAMETERS_END();
3513 
3514 	csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3515 	if (csr == NULL) {
3516 		RETURN_FALSE;
3517 	}
3518 
3519 	subject = X509_REQ_get_subject_name(csr);
3520 
3521 	array_init(return_value);
3522 	php_openssl_add_assoc_name_entry(return_value, NULL, subject, use_shortnames);
3523 
3524 	if (csr_str) {
3525 		X509_REQ_free(csr);
3526 	}
3527 }
3528 /* }}} */
3529 
3530 /* {{{ Returns the subject of a CERT or FALSE on error */
PHP_FUNCTION(openssl_csr_get_public_key)3531 PHP_FUNCTION(openssl_csr_get_public_key)
3532 {
3533 	X509_REQ *orig_csr, *csr;
3534 	zend_object *csr_obj;
3535 	zend_string *csr_str;
3536 	zend_bool use_shortnames = 1;
3537 
3538 	php_openssl_pkey_object *key_object;
3539 	EVP_PKEY *tpubkey;
3540 
3541 	ZEND_PARSE_PARAMETERS_START(1, 2)
3542 		Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3543 		Z_PARAM_OPTIONAL
3544 		Z_PARAM_BOOL(use_shortnames)
3545 	ZEND_PARSE_PARAMETERS_END();
3546 
3547 	orig_csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3548 	if (orig_csr == NULL) {
3549 		RETURN_FALSE;
3550 	}
3551 
3552 #if PHP_OPENSSL_API_VERSION >= 0x10100
3553 	/* Due to changes in OpenSSL 1.1 related to locking when decoding CSR,
3554 	 * the pub key is not changed after assigning. It means if we pass
3555 	 * a private key, it will be returned including the private part.
3556 	 * If we duplicate it, then we get just the public part which is
3557 	 * the same behavior as for OpenSSL 1.0 */
3558 	csr = X509_REQ_dup(orig_csr);
3559 #else
3560 	csr = orig_csr;
3561 #endif
3562 
3563 	/* Retrieve the public key from the CSR */
3564 	tpubkey = X509_REQ_get_pubkey(csr);
3565 
3566 	if (csr != orig_csr) {
3567 		/* We need to free the duplicated CSR */
3568 		X509_REQ_free(csr);
3569 	}
3570 
3571 	if (csr_str) {
3572 		/* We also need to free the original CSR if it was freshly created */
3573 		X509_REQ_free(orig_csr);
3574 	}
3575 
3576 	if (tpubkey == NULL) {
3577 		php_openssl_store_errors();
3578 		RETURN_FALSE;
3579 	}
3580 
3581 	object_init_ex(return_value, php_openssl_pkey_ce);
3582 	key_object = Z_OPENSSL_PKEY_P(return_value);
3583 	key_object->pkey = tpubkey;
3584 }
3585 /* }}} */
3586 
3587 /* }}} */
3588 
3589 /* {{{ EVP Public/Private key functions */
3590 
3591 struct php_openssl_pem_password {
3592 	char *key;
3593 	int len;
3594 };
3595 
3596 /* {{{ php_openssl_pem_password_cb */
php_openssl_pem_password_cb(char * buf,int size,int rwflag,void * userdata)3597 static int php_openssl_pem_password_cb(char *buf, int size, int rwflag, void *userdata)
3598 {
3599 	struct php_openssl_pem_password *password = userdata;
3600 
3601 	if (password == NULL || password->key == NULL) {
3602 		return -1;
3603 	}
3604 
3605 	size = (password->len > size) ? size : password->len;
3606 	memcpy(buf, password->key, size);
3607 
3608 	return size;
3609 }
3610 /* }}} */
3611 
php_openssl_pkey_from_zval(zval * val,int public_key,char * passphrase,size_t passphrase_len,uint32_t arg_num)3612 static EVP_PKEY *php_openssl_pkey_from_zval(
3613 		zval *val, int public_key, char *passphrase, size_t passphrase_len, uint32_t arg_num)
3614 {
3615 	EVP_PKEY *key = NULL;
3616 	X509 *cert = NULL;
3617 	bool free_cert = false, is_file = false;
3618 	char file_path[MAXPATHLEN];
3619 	zval tmp;
3620 
3621 	ZVAL_NULL(&tmp);
3622 
3623 #define TMP_CLEAN \
3624 	if (Z_TYPE(tmp) == IS_STRING) {\
3625 		zval_ptr_dtor_str(&tmp); \
3626 	} \
3627 	return NULL;
3628 
3629 	if (Z_TYPE_P(val) == IS_ARRAY) {
3630 		zval * zphrase;
3631 
3632 		/* get passphrase */
3633 
3634 		if ((zphrase = zend_hash_index_find(Z_ARRVAL_P(val), 1)) == NULL) {
3635 			zend_value_error("Key array must be of the form array(0 => key, 1 => phrase)");
3636 			return NULL;
3637 		}
3638 
3639 		if (Z_TYPE_P(zphrase) == IS_STRING) {
3640 			passphrase = Z_STRVAL_P(zphrase);
3641 			passphrase_len = Z_STRLEN_P(zphrase);
3642 		} else {
3643 			ZVAL_COPY(&tmp, zphrase);
3644 			if (!try_convert_to_string(&tmp)) {
3645 				return NULL;
3646 			}
3647 
3648 			passphrase = Z_STRVAL(tmp);
3649 			passphrase_len = Z_STRLEN(tmp);
3650 		}
3651 
3652 		/* now set val to be the key param and continue */
3653 		if ((val = zend_hash_index_find(Z_ARRVAL_P(val), 0)) == NULL) {
3654 			zend_value_error("Key array must be of the form array(0 => key, 1 => phrase)");
3655 			TMP_CLEAN;
3656 		}
3657 	}
3658 
3659 	if (Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_pkey_ce) {
3660 		int is_priv;
3661 
3662 		key = php_openssl_pkey_from_obj(Z_OBJ_P(val))->pkey;
3663 		is_priv = php_openssl_is_private_key(key);
3664 
3665 		/* check whether it is actually a private key if requested */
3666 		if (!public_key && !is_priv) {
3667 			php_error_docref(NULL, E_WARNING, "Supplied key param is a public key");
3668 			TMP_CLEAN;
3669 		}
3670 
3671 		if (public_key && is_priv) {
3672 			php_error_docref(NULL, E_WARNING, "Don't know how to get public key from this private key");
3673 			TMP_CLEAN;
3674 		} else {
3675 			if (Z_TYPE(tmp) == IS_STRING) {
3676 				zval_ptr_dtor_str(&tmp);
3677 			}
3678 
3679 			EVP_PKEY_up_ref(key);
3680 			return key;
3681 		}
3682 	} else if (Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_certificate_ce) {
3683 		cert = php_openssl_certificate_from_obj(Z_OBJ_P(val))->x509;
3684 	} else {
3685 		/* force it to be a string and check if it refers to a file */
3686 		/* passing non string values leaks, object uses toString, it returns NULL
3687 		 * See bug38255.phpt
3688 		 */
3689 		if (!(Z_TYPE_P(val) == IS_STRING || Z_TYPE_P(val) == IS_OBJECT)) {
3690 			TMP_CLEAN;
3691 		}
3692 		if (!try_convert_to_string(val)) {
3693 			TMP_CLEAN;
3694 		}
3695 
3696 		if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {
3697 			if (!php_openssl_check_path_str(Z_STR_P(val), file_path, arg_num)) {
3698 				TMP_CLEAN;
3699 			}
3700 			is_file = true;
3701 		}
3702 		/* it's an X509 file/cert of some kind, and we need to extract the data from that */
3703 		if (public_key) {
3704 			cert = php_openssl_x509_from_str(Z_STR_P(val), arg_num, false, NULL);
3705 
3706 			if (cert) {
3707 				free_cert = 1;
3708 			} else {
3709 				/* not a X509 certificate, try to retrieve public key */
3710 				BIO* in;
3711 				if (is_file) {
3712 					in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3713 				} else {
3714 					in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
3715 				}
3716 				if (in == NULL) {
3717 					php_openssl_store_errors();
3718 					TMP_CLEAN;
3719 				}
3720 				key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
3721 				BIO_free(in);
3722 			}
3723 		} else {
3724 			/* we want the private key */
3725 			BIO *in;
3726 
3727 			if (is_file) {
3728 				in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3729 			} else {
3730 				in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
3731 			}
3732 
3733 			if (in == NULL) {
3734 				TMP_CLEAN;
3735 			}
3736 			if (passphrase == NULL) {
3737 				key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
3738 			} else {
3739 				struct php_openssl_pem_password password;
3740 				password.key = passphrase;
3741 				password.len = passphrase_len;
3742 				key = PEM_read_bio_PrivateKey(in, NULL, php_openssl_pem_password_cb, &password);
3743 			}
3744 			BIO_free(in);
3745 		}
3746 	}
3747 
3748 	if (key == NULL) {
3749 		php_openssl_store_errors();
3750 
3751 		if (public_key && cert) {
3752 			/* extract public key from X509 cert */
3753 			key = (EVP_PKEY *) X509_get_pubkey(cert);
3754 			if (key == NULL) {
3755 				php_openssl_store_errors();
3756 			}
3757 		}
3758 	}
3759 
3760 	if (free_cert) {
3761 		X509_free(cert);
3762 	}
3763 
3764 	if (Z_TYPE(tmp) == IS_STRING) {
3765 		zval_ptr_dtor_str(&tmp);
3766 	}
3767 
3768 	return key;
3769 }
3770 
3771 /* {{{ php_openssl_generate_private_key */
php_openssl_generate_private_key(struct php_x509_request * req)3772 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req)
3773 {
3774 	char * randfile = NULL;
3775 	int egdsocket, seeded;
3776 	EVP_PKEY * return_val = NULL;
3777 
3778 	if (req->priv_key_bits < MIN_KEY_LENGTH) {
3779 		php_error_docref(NULL, E_WARNING, "Private key length must be at least %d bits, configured to %d",
3780 			MIN_KEY_LENGTH, req->priv_key_bits);
3781 		return NULL;
3782 	}
3783 
3784 	randfile = php_openssl_conf_get_string(req->req_config, req->section_name, "RANDFILE");
3785 	php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
3786 
3787 	if ((req->priv_key = EVP_PKEY_new()) != NULL) {
3788 		switch(req->priv_key_type) {
3789 			case OPENSSL_KEYTYPE_RSA:
3790 				{
3791 					RSA* rsaparam;
3792 #if OPENSSL_VERSION_NUMBER < 0x10002000L
3793 					/* OpenSSL 1.0.2 deprecates RSA_generate_key */
3794 					PHP_OPENSSL_RAND_ADD_TIME();
3795 					rsaparam = (RSA*)RSA_generate_key(req->priv_key_bits, RSA_F4, NULL, NULL);
3796 #else
3797 					{
3798 						BIGNUM *bne = (BIGNUM *)BN_new();
3799 						if (BN_set_word(bne, RSA_F4) != 1) {
3800 							BN_free(bne);
3801 							php_error_docref(NULL, E_WARNING, "Failed setting exponent");
3802 							return NULL;
3803 						}
3804 						rsaparam = RSA_new();
3805 						PHP_OPENSSL_RAND_ADD_TIME();
3806 						if (rsaparam == NULL || !RSA_generate_key_ex(rsaparam, req->priv_key_bits, bne, NULL)) {
3807 							php_openssl_store_errors();
3808 							RSA_free(rsaparam);
3809 							rsaparam = NULL;
3810 						}
3811 						BN_free(bne);
3812 					}
3813 #endif
3814 					if (rsaparam && EVP_PKEY_assign_RSA(req->priv_key, rsaparam)) {
3815 						return_val = req->priv_key;
3816 					} else {
3817 						php_openssl_store_errors();
3818 					}
3819 				}
3820 				break;
3821 #if !defined(NO_DSA)
3822 			case OPENSSL_KEYTYPE_DSA:
3823 				PHP_OPENSSL_RAND_ADD_TIME();
3824 				{
3825 					DSA *dsaparam = DSA_new();
3826 					if (dsaparam && DSA_generate_parameters_ex(dsaparam, req->priv_key_bits, NULL, 0, NULL, NULL, NULL)) {
3827 						DSA_set_method(dsaparam, DSA_get_default_method());
3828 						if (DSA_generate_key(dsaparam)) {
3829 							if (EVP_PKEY_assign_DSA(req->priv_key, dsaparam)) {
3830 								return_val = req->priv_key;
3831 							} else {
3832 								php_openssl_store_errors();
3833 							}
3834 						} else {
3835 							php_openssl_store_errors();
3836 							DSA_free(dsaparam);
3837 						}
3838 					} else {
3839 						php_openssl_store_errors();
3840 					}
3841 				}
3842 				break;
3843 #endif
3844 #if !defined(NO_DH)
3845 			case OPENSSL_KEYTYPE_DH:
3846 				PHP_OPENSSL_RAND_ADD_TIME();
3847 				{
3848 					int codes = 0;
3849 					DH *dhparam = DH_new();
3850 					if (dhparam && DH_generate_parameters_ex(dhparam, req->priv_key_bits, 2, NULL)) {
3851 						DH_set_method(dhparam, DH_get_default_method());
3852 						if (DH_check(dhparam, &codes) && codes == 0 && DH_generate_key(dhparam)) {
3853 							if (EVP_PKEY_assign_DH(req->priv_key, dhparam)) {
3854 								return_val = req->priv_key;
3855 							} else {
3856 								php_openssl_store_errors();
3857 							}
3858 						} else {
3859 							php_openssl_store_errors();
3860 							DH_free(dhparam);
3861 						}
3862 					} else {
3863 						php_openssl_store_errors();
3864 					}
3865 				}
3866 				break;
3867 #endif
3868 #ifdef HAVE_EVP_PKEY_EC
3869 			case OPENSSL_KEYTYPE_EC:
3870 				{
3871 					EC_KEY *eckey;
3872 					if (req->curve_name == NID_undef) {
3873 						php_error_docref(NULL, E_WARNING, "Missing configuration value: \"curve_name\" not set");
3874 						return NULL;
3875 					}
3876 					eckey = EC_KEY_new_by_curve_name(req->curve_name);
3877 					if (eckey) {
3878 						EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
3879 						if (EC_KEY_generate_key(eckey) &&
3880 							EVP_PKEY_assign_EC_KEY(req->priv_key, eckey)) {
3881 							return_val = req->priv_key;
3882 						} else {
3883 							EC_KEY_free(eckey);
3884 						}
3885 					}
3886 				}
3887 				break;
3888 #endif
3889 			default:
3890 				php_error_docref(NULL, E_WARNING, "Unsupported private key type");
3891 		}
3892 	} else {
3893 		php_openssl_store_errors();
3894 	}
3895 
3896 	php_openssl_write_rand_file(randfile, egdsocket, seeded);
3897 
3898 	if (return_val == NULL) {
3899 		EVP_PKEY_free(req->priv_key);
3900 		req->priv_key = NULL;
3901 		return NULL;
3902 	}
3903 
3904 	return return_val;
3905 }
3906 /* }}} */
3907 
3908 /* {{{ php_openssl_is_private_key
3909 	Check whether the supplied key is a private key by checking if the secret prime factors are set */
php_openssl_is_private_key(EVP_PKEY * pkey)3910 static int php_openssl_is_private_key(EVP_PKEY* pkey)
3911 {
3912 	assert(pkey != NULL);
3913 
3914 	switch (EVP_PKEY_id(pkey)) {
3915 		case EVP_PKEY_RSA:
3916 		case EVP_PKEY_RSA2:
3917 			{
3918 				RSA *rsa = EVP_PKEY_get0_RSA(pkey);
3919 				if (rsa != NULL) {
3920 					const BIGNUM *p, *q;
3921 
3922 					RSA_get0_factors(rsa, &p, &q);
3923 					 if (p == NULL || q == NULL) {
3924 						return 0;
3925 					 }
3926 				}
3927 			}
3928 			break;
3929 		case EVP_PKEY_DSA:
3930 		case EVP_PKEY_DSA1:
3931 		case EVP_PKEY_DSA2:
3932 		case EVP_PKEY_DSA3:
3933 		case EVP_PKEY_DSA4:
3934 			{
3935 				DSA *dsa = EVP_PKEY_get0_DSA(pkey);
3936 				if (dsa != NULL) {
3937 					const BIGNUM *p, *q, *g, *pub_key, *priv_key;
3938 
3939 					DSA_get0_pqg(dsa, &p, &q, &g);
3940 					if (p == NULL || q == NULL) {
3941 						return 0;
3942 					}
3943 
3944 					DSA_get0_key(dsa, &pub_key, &priv_key);
3945 					if (priv_key == NULL) {
3946 						return 0;
3947 					}
3948 				}
3949 			}
3950 			break;
3951 		case EVP_PKEY_DH:
3952 			{
3953 				DH *dh = EVP_PKEY_get0_DH(pkey);
3954 				if (dh != NULL) {
3955 					const BIGNUM *p, *q, *g, *pub_key, *priv_key;
3956 
3957 					DH_get0_pqg(dh, &p, &q, &g);
3958 					if (p == NULL) {
3959 						return 0;
3960 					}
3961 
3962 					DH_get0_key(dh, &pub_key, &priv_key);
3963 					if (priv_key == NULL) {
3964 						return 0;
3965 					}
3966 				}
3967 			}
3968 			break;
3969 #ifdef HAVE_EVP_PKEY_EC
3970 		case EVP_PKEY_EC:
3971 			{
3972 				EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
3973 				if (ec != NULL && NULL == EC_KEY_get0_private_key(ec)) {
3974 					return 0;
3975 				}
3976 			}
3977 			break;
3978 #endif
3979 		default:
3980 			php_error_docref(NULL, E_WARNING, "Key type not supported in this PHP build!");
3981 			break;
3982 	}
3983 	return 1;
3984 }
3985 /* }}} */
3986 
3987 #define OPENSSL_GET_BN(_array, _bn, _name) do { \
3988 		if (_bn != NULL) { \
3989 			int len = BN_num_bytes(_bn); \
3990 			zend_string *str = zend_string_alloc(len, 0); \
3991 			BN_bn2bin(_bn, (unsigned char*)ZSTR_VAL(str)); \
3992 			ZSTR_VAL(str)[len] = 0; \
3993 			add_assoc_str(&_array, #_name, str); \
3994 		} \
3995 	} while (0);
3996 
3997 #define OPENSSL_PKEY_GET_BN(_type, _name) OPENSSL_GET_BN(_type, _name, _name)
3998 
3999 #define OPENSSL_PKEY_SET_BN(_data, _name) do { \
4000 		zval *bn; \
4001 		if ((bn = zend_hash_str_find(Z_ARRVAL_P(_data), #_name, sizeof(#_name)-1)) != NULL && \
4002 				Z_TYPE_P(bn) == IS_STRING) { \
4003 			_name = BN_bin2bn( \
4004 				(unsigned char*)Z_STRVAL_P(bn), \
4005 				(int)Z_STRLEN_P(bn), NULL); \
4006 		} else { \
4007 			_name = NULL; \
4008 		} \
4009 	} while (0);
4010 
4011 /* {{{ php_openssl_pkey_init_rsa */
php_openssl_pkey_init_and_assign_rsa(EVP_PKEY * pkey,RSA * rsa,zval * data)4012 static zend_bool php_openssl_pkey_init_and_assign_rsa(EVP_PKEY *pkey, RSA *rsa, zval *data)
4013 {
4014 	BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
4015 
4016 	OPENSSL_PKEY_SET_BN(data, n);
4017 	OPENSSL_PKEY_SET_BN(data, e);
4018 	OPENSSL_PKEY_SET_BN(data, d);
4019 	if (!n || !d || !RSA_set0_key(rsa, n, e, d)) {
4020 		return 0;
4021 	}
4022 
4023 	OPENSSL_PKEY_SET_BN(data, p);
4024 	OPENSSL_PKEY_SET_BN(data, q);
4025 	if ((p || q) && !RSA_set0_factors(rsa, p, q)) {
4026 		return 0;
4027 	}
4028 
4029 	OPENSSL_PKEY_SET_BN(data, dmp1);
4030 	OPENSSL_PKEY_SET_BN(data, dmq1);
4031 	OPENSSL_PKEY_SET_BN(data, iqmp);
4032 	if ((dmp1 || dmq1 || iqmp) && !RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp)) {
4033 		return 0;
4034 	}
4035 
4036 	if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
4037 		php_openssl_store_errors();
4038 		return 0;
4039 	}
4040 
4041 	return 1;
4042 }
4043 
4044 /* {{{ php_openssl_pkey_init_dsa */
php_openssl_pkey_init_dsa(DSA * dsa,zval * data)4045 static zend_bool php_openssl_pkey_init_dsa(DSA *dsa, zval *data)
4046 {
4047 	BIGNUM *p, *q, *g, *priv_key, *pub_key;
4048 	const BIGNUM *priv_key_const, *pub_key_const;
4049 
4050 	OPENSSL_PKEY_SET_BN(data, p);
4051 	OPENSSL_PKEY_SET_BN(data, q);
4052 	OPENSSL_PKEY_SET_BN(data, g);
4053 	if (!p || !q || !g || !DSA_set0_pqg(dsa, p, q, g)) {
4054 		return 0;
4055 	}
4056 
4057 	OPENSSL_PKEY_SET_BN(data, pub_key);
4058 	OPENSSL_PKEY_SET_BN(data, priv_key);
4059 	if (pub_key) {
4060 		return DSA_set0_key(dsa, pub_key, priv_key);
4061 	}
4062 
4063 	/* generate key */
4064 	PHP_OPENSSL_RAND_ADD_TIME();
4065 	if (!DSA_generate_key(dsa)) {
4066 		php_openssl_store_errors();
4067 		return 0;
4068 	}
4069 
4070 	/* if BN_mod_exp return -1, then DSA_generate_key succeed for failed key
4071 	 * so we need to double check that public key is created */
4072 	DSA_get0_key(dsa, &pub_key_const, &priv_key_const);
4073 	if (!pub_key_const || BN_is_zero(pub_key_const)) {
4074 		return 0;
4075 	}
4076 	/* all good */
4077 	return 1;
4078 }
4079 /* }}} */
4080 
4081 /* {{{ php_openssl_dh_pub_from_priv */
php_openssl_dh_pub_from_priv(BIGNUM * priv_key,BIGNUM * g,BIGNUM * p)4082 static BIGNUM *php_openssl_dh_pub_from_priv(BIGNUM *priv_key, BIGNUM *g, BIGNUM *p)
4083 {
4084 	BIGNUM *pub_key, *priv_key_const_time;
4085 	BN_CTX *ctx;
4086 
4087 	pub_key = BN_new();
4088 	if (pub_key == NULL) {
4089 		php_openssl_store_errors();
4090 		return NULL;
4091 	}
4092 
4093 	priv_key_const_time = BN_new();
4094 	if (priv_key_const_time == NULL) {
4095 		BN_free(pub_key);
4096 		php_openssl_store_errors();
4097 		return NULL;
4098 	}
4099 	ctx = BN_CTX_new();
4100 	if (ctx == NULL) {
4101 		BN_free(pub_key);
4102 		BN_free(priv_key_const_time);
4103 		php_openssl_store_errors();
4104 		return NULL;
4105 	}
4106 
4107 	BN_with_flags(priv_key_const_time, priv_key, BN_FLG_CONSTTIME);
4108 
4109 	if (!BN_mod_exp_mont(pub_key, g, priv_key_const_time, p, ctx, NULL)) {
4110 		BN_free(pub_key);
4111 		php_openssl_store_errors();
4112 		pub_key = NULL;
4113 	}
4114 
4115 	BN_free(priv_key_const_time);
4116 	BN_CTX_free(ctx);
4117 
4118 	return pub_key;
4119 }
4120 /* }}} */
4121 
4122 /* {{{ php_openssl_pkey_init_dh */
php_openssl_pkey_init_dh(DH * dh,zval * data)4123 static zend_bool php_openssl_pkey_init_dh(DH *dh, zval *data)
4124 {
4125 	BIGNUM *p, *q, *g, *priv_key, *pub_key;
4126 
4127 	OPENSSL_PKEY_SET_BN(data, p);
4128 	OPENSSL_PKEY_SET_BN(data, q);
4129 	OPENSSL_PKEY_SET_BN(data, g);
4130 	if (!p || !g || !DH_set0_pqg(dh, p, q, g)) {
4131 		return 0;
4132 	}
4133 
4134 	OPENSSL_PKEY_SET_BN(data, priv_key);
4135 	OPENSSL_PKEY_SET_BN(data, pub_key);
4136 	if (pub_key) {
4137 		return DH_set0_key(dh, pub_key, priv_key);
4138 	}
4139 	if (priv_key) {
4140 		pub_key = php_openssl_dh_pub_from_priv(priv_key, g, p);
4141 		if (pub_key == NULL) {
4142 			return 0;
4143 		}
4144 		return DH_set0_key(dh, pub_key, priv_key);
4145 	}
4146 
4147 	/* generate key */
4148 	PHP_OPENSSL_RAND_ADD_TIME();
4149 	if (!DH_generate_key(dh)) {
4150 		php_openssl_store_errors();
4151 		return 0;
4152 	}
4153 	/* all good */
4154 	return 1;
4155 }
4156 /* }}} */
4157 
4158 /* {{{ Generates a new private key */
PHP_FUNCTION(openssl_pkey_new)4159 PHP_FUNCTION(openssl_pkey_new)
4160 {
4161 	struct php_x509_request req;
4162 	zval * args = NULL;
4163 	zval *data;
4164 	php_openssl_pkey_object *key_object;
4165 
4166 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &args) == FAILURE) {
4167 		RETURN_THROWS();
4168 	}
4169 	RETVAL_FALSE;
4170 
4171 	if (args && Z_TYPE_P(args) == IS_ARRAY) {
4172 		EVP_PKEY *pkey;
4173 
4174 		if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa")-1)) != NULL &&
4175 			Z_TYPE_P(data) == IS_ARRAY) {
4176 			pkey = EVP_PKEY_new();
4177 			if (pkey) {
4178 				RSA *rsa = RSA_new();
4179 				if (rsa) {
4180 					if (php_openssl_pkey_init_and_assign_rsa(pkey, rsa, data)) {
4181 						object_init_ex(return_value, php_openssl_pkey_ce);
4182 						key_object = Z_OPENSSL_PKEY_P(return_value);
4183 						key_object->pkey = pkey;
4184 						return;
4185 					}
4186 					RSA_free(rsa);
4187 				} else {
4188 					php_openssl_store_errors();
4189 				}
4190 				EVP_PKEY_free(pkey);
4191 			} else {
4192 				php_openssl_store_errors();
4193 			}
4194 			RETURN_FALSE;
4195 		} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa") - 1)) != NULL &&
4196 			Z_TYPE_P(data) == IS_ARRAY) {
4197 			pkey = EVP_PKEY_new();
4198 			if (pkey) {
4199 				DSA *dsa = DSA_new();
4200 				if (dsa) {
4201 					if (php_openssl_pkey_init_dsa(dsa, data)) {
4202 						if (EVP_PKEY_assign_DSA(pkey, dsa)) {
4203 							object_init_ex(return_value, php_openssl_pkey_ce);
4204 							key_object = Z_OPENSSL_PKEY_P(return_value);
4205 							key_object->pkey = pkey;
4206 							return;
4207 						} else {
4208 							php_openssl_store_errors();
4209 						}
4210 					}
4211 					DSA_free(dsa);
4212 				} else {
4213 					php_openssl_store_errors();
4214 				}
4215 				EVP_PKEY_free(pkey);
4216 			} else {
4217 				php_openssl_store_errors();
4218 			}
4219 			RETURN_FALSE;
4220 		} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dh", sizeof("dh") - 1)) != NULL &&
4221 			Z_TYPE_P(data) == IS_ARRAY) {
4222 			pkey = EVP_PKEY_new();
4223 			if (pkey) {
4224 				DH *dh = DH_new();
4225 				if (dh) {
4226 					if (php_openssl_pkey_init_dh(dh, data)) {
4227 						if (EVP_PKEY_assign_DH(pkey, dh)) {
4228 							php_openssl_pkey_object *key_object;
4229 
4230 							object_init_ex(return_value, php_openssl_pkey_ce);
4231 							key_object = Z_OPENSSL_PKEY_P(return_value);
4232 							key_object->pkey = pkey;
4233 							return;
4234 						} else {
4235 							php_openssl_store_errors();
4236 						}
4237 					}
4238 					DH_free(dh);
4239 				} else {
4240 					php_openssl_store_errors();
4241 				}
4242 				EVP_PKEY_free(pkey);
4243 			} else {
4244 				php_openssl_store_errors();
4245 			}
4246 			RETURN_FALSE;
4247 #ifdef HAVE_EVP_PKEY_EC
4248 		} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "ec", sizeof("ec") - 1)) != NULL &&
4249 			Z_TYPE_P(data) == IS_ARRAY) {
4250 			EC_KEY *eckey = NULL;
4251 			EC_GROUP *group = NULL;
4252 			EC_POINT *pnt = NULL;
4253 			BIGNUM *d = NULL;
4254 			pkey = EVP_PKEY_new();
4255 			if (pkey) {
4256 				eckey = EC_KEY_new();
4257 				if (eckey) {
4258 					EC_GROUP *group = NULL;
4259 					zval *bn;
4260 					zval *x;
4261 					zval *y;
4262 
4263 					if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1)) != NULL &&
4264 							Z_TYPE_P(bn) == IS_STRING) {
4265 						int nid = OBJ_sn2nid(Z_STRVAL_P(bn));
4266 						if (nid != NID_undef) {
4267 							group = EC_GROUP_new_by_curve_name(nid);
4268 							if (!group) {
4269 								php_openssl_store_errors();
4270 								goto clean_exit;
4271 							}
4272 							EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
4273 							EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
4274 							if (!EC_KEY_set_group(eckey, group)) {
4275 								php_openssl_store_errors();
4276 								goto clean_exit;
4277 							}
4278 						}
4279 					}
4280 
4281 					if (group == NULL) {
4282 						php_error_docref(NULL, E_WARNING, "Unknown curve name");
4283 						goto clean_exit;
4284 					}
4285 
4286 					// The public key 'pnt' can be calculated from 'd' or is defined by 'x' and 'y'
4287 					if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "d", sizeof("d") - 1)) != NULL &&
4288 							Z_TYPE_P(bn) == IS_STRING) {
4289 						d = BN_bin2bn((unsigned char*) Z_STRVAL_P(bn), Z_STRLEN_P(bn), NULL);
4290 						if (!EC_KEY_set_private_key(eckey, d)) {
4291 							php_openssl_store_errors();
4292 							goto clean_exit;
4293 						}
4294 						// Calculate the public key by multiplying the Point Q with the public key
4295 						// P = d * Q
4296 						pnt = EC_POINT_new(group);
4297 						if (!pnt || !EC_POINT_mul(group, pnt, d, NULL, NULL, NULL)) {
4298 							php_openssl_store_errors();
4299 							goto clean_exit;
4300 						}
4301 
4302 						BN_free(d);
4303 					} else if ((x = zend_hash_str_find(Z_ARRVAL_P(data), "x", sizeof("x") - 1)) != NULL &&
4304 							Z_TYPE_P(x) == IS_STRING &&
4305 							(y = zend_hash_str_find(Z_ARRVAL_P(data), "y", sizeof("y") - 1)) != NULL &&
4306 							Z_TYPE_P(y) == IS_STRING) {
4307 						pnt = EC_POINT_new(group);
4308 						if (pnt == NULL) {
4309 							php_openssl_store_errors();
4310 							goto clean_exit;
4311 						}
4312 						if (!EC_POINT_set_affine_coordinates_GFp(
4313 								group, pnt, BN_bin2bn((unsigned char*) Z_STRVAL_P(x), Z_STRLEN_P(x), NULL),
4314 								BN_bin2bn((unsigned char*) Z_STRVAL_P(y), Z_STRLEN_P(y), NULL), NULL)) {
4315 							php_openssl_store_errors();
4316 							goto clean_exit;
4317 						}
4318 					}
4319 
4320 					if (pnt != NULL) {
4321 						if (!EC_KEY_set_public_key(eckey, pnt)) {
4322 							php_openssl_store_errors();
4323 							goto clean_exit;
4324 						}
4325 						EC_POINT_free(pnt);
4326 						pnt = NULL;
4327 					}
4328 
4329 					if (!EC_KEY_check_key(eckey)) {
4330 						PHP_OPENSSL_RAND_ADD_TIME();
4331 						EC_KEY_generate_key(eckey);
4332 						php_openssl_store_errors();
4333 					}
4334 					if (EC_KEY_check_key(eckey) && EVP_PKEY_assign_EC_KEY(pkey, eckey)) {
4335 						EC_GROUP_free(group);
4336 
4337 						object_init_ex(return_value, php_openssl_pkey_ce);
4338 						key_object = Z_OPENSSL_PKEY_P(return_value);
4339 						key_object->pkey = pkey;
4340 						return;
4341 					} else {
4342 						php_openssl_store_errors();
4343 					}
4344 				} else {
4345 					php_openssl_store_errors();
4346 				}
4347 			} else {
4348 				php_openssl_store_errors();
4349 			}
4350 clean_exit:
4351 			if (d != NULL) {
4352 				BN_free(d);
4353 			}
4354 			if (pnt != NULL) {
4355 				EC_POINT_free(pnt);
4356 			}
4357 			if (group != NULL) {
4358 				EC_GROUP_free(group);
4359 			}
4360 			if (eckey != NULL) {
4361 				EC_KEY_free(eckey);
4362 			}
4363 			EVP_PKEY_free(pkey);
4364 			RETURN_FALSE;
4365 #endif
4366 		}
4367 	}
4368 
4369 	PHP_SSL_REQ_INIT(&req);
4370 
4371 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4372 		if (php_openssl_generate_private_key(&req)) {
4373 			/* pass back a key resource */
4374 			object_init_ex(return_value, php_openssl_pkey_ce);
4375 			key_object = Z_OPENSSL_PKEY_P(return_value);
4376 			key_object->pkey = req.priv_key;
4377 			/* make sure the cleanup code doesn't zap it! */
4378 			req.priv_key = NULL;
4379 		}
4380 	}
4381 	PHP_SSL_REQ_DISPOSE(&req);
4382 }
4383 /* }}} */
4384 
4385 /* {{{ Gets an exportable representation of a key into a file */
PHP_FUNCTION(openssl_pkey_export_to_file)4386 PHP_FUNCTION(openssl_pkey_export_to_file)
4387 {
4388 	struct php_x509_request req;
4389 	zval * zpkey, * args = NULL;
4390 	char * passphrase = NULL;
4391 	size_t passphrase_len = 0;
4392 	char * filename = NULL, file_path[MAXPATHLEN];
4393 	size_t filename_len = 0;
4394 	int pem_write = 0;
4395 	EVP_PKEY * key;
4396 	BIO * bio_out = NULL;
4397 	const EVP_CIPHER * cipher;
4398 
4399 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zp|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) {
4400 		RETURN_THROWS();
4401 	}
4402 	RETVAL_FALSE;
4403 
4404 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase, 3);
4405 
4406 	key = php_openssl_pkey_from_zval(zpkey, 0, passphrase, passphrase_len, 1);
4407 	if (key == NULL) {
4408 		if (!EG(exception)) {
4409 			php_error_docref(NULL, E_WARNING, "Cannot get key from parameter 1");
4410 		}
4411 		RETURN_FALSE;
4412 	}
4413 
4414 	if (!php_openssl_check_path(filename, filename_len, file_path, 2)) {
4415 		RETURN_FALSE;
4416 	}
4417 
4418 	PHP_SSL_REQ_INIT(&req);
4419 
4420 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4421 		bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
4422 		if (bio_out == NULL) {
4423 			php_openssl_store_errors();
4424 			goto clean_exit;
4425 		}
4426 
4427 		if (passphrase && req.priv_key_encrypt) {
4428 			if (req.priv_key_encrypt_cipher) {
4429 				cipher = req.priv_key_encrypt_cipher;
4430 			} else {
4431 				cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
4432 			}
4433 		} else {
4434 			cipher = NULL;
4435 		}
4436 
4437 		switch (EVP_PKEY_base_id(key)) {
4438 #ifdef HAVE_EVP_PKEY_EC
4439 			case EVP_PKEY_EC:
4440 				pem_write = PEM_write_bio_ECPrivateKey(
4441 						bio_out, EVP_PKEY_get0_EC_KEY(key), cipher,
4442 						(unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4443 				break;
4444 #endif
4445 			default:
4446 				pem_write = PEM_write_bio_PrivateKey(
4447 						bio_out, key, cipher,
4448 						(unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4449 				break;
4450 		}
4451 
4452 		if (pem_write) {
4453 			/* Success!
4454 			 * If returning the output as a string, do so now */
4455 			RETVAL_TRUE;
4456 		} else {
4457 			php_openssl_store_errors();
4458 		}
4459 	}
4460 
4461 clean_exit:
4462 	PHP_SSL_REQ_DISPOSE(&req);
4463 	EVP_PKEY_free(key);
4464 	BIO_free(bio_out);
4465 }
4466 /* }}} */
4467 
4468 /* {{{ Gets an exportable representation of a key into a string or file */
PHP_FUNCTION(openssl_pkey_export)4469 PHP_FUNCTION(openssl_pkey_export)
4470 {
4471 	struct php_x509_request req;
4472 	zval * zpkey, * args = NULL, *out;
4473 	char * passphrase = NULL; size_t passphrase_len = 0;
4474 	int pem_write = 0;
4475 	EVP_PKEY * key;
4476 	BIO * bio_out = NULL;
4477 	const EVP_CIPHER * cipher;
4478 
4479 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) {
4480 		RETURN_THROWS();
4481 	}
4482 	RETVAL_FALSE;
4483 
4484 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase, 3);
4485 
4486 	key = php_openssl_pkey_from_zval(zpkey, 0, passphrase, passphrase_len, 1);
4487 	if (key == NULL) {
4488 		if (!EG(exception)) {
4489 			php_error_docref(NULL, E_WARNING, "Cannot get key from parameter 1");
4490 		}
4491 		RETURN_FALSE;
4492 	}
4493 
4494 	PHP_SSL_REQ_INIT(&req);
4495 
4496 	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4497 		bio_out = BIO_new(BIO_s_mem());
4498 
4499 		if (passphrase && req.priv_key_encrypt) {
4500 			if (req.priv_key_encrypt_cipher) {
4501 				cipher = req.priv_key_encrypt_cipher;
4502 			} else {
4503 				cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
4504 			}
4505 		} else {
4506 			cipher = NULL;
4507 		}
4508 
4509 		switch (EVP_PKEY_base_id(key)) {
4510 #ifdef HAVE_EVP_PKEY_EC
4511 			case EVP_PKEY_EC:
4512 				pem_write = PEM_write_bio_ECPrivateKey(
4513 						bio_out, EVP_PKEY_get0_EC_KEY(key), cipher,
4514 						(unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4515 				break;
4516 #endif
4517 			default:
4518 				pem_write = PEM_write_bio_PrivateKey(
4519 						bio_out, key, cipher,
4520 						(unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4521 				break;
4522 		}
4523 
4524 		if (pem_write) {
4525 			/* Success!
4526 			 * If returning the output as a string, do so now */
4527 
4528 			char * bio_mem_ptr;
4529 			long bio_mem_len;
4530 			RETVAL_TRUE;
4531 
4532 			bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
4533 			ZEND_TRY_ASSIGN_REF_STRINGL(out, bio_mem_ptr, bio_mem_len);
4534 		} else {
4535 			php_openssl_store_errors();
4536 		}
4537 	}
4538 	PHP_SSL_REQ_DISPOSE(&req);
4539 	EVP_PKEY_free(key);
4540 	BIO_free(bio_out);
4541 }
4542 /* }}} */
4543 
4544 /* {{{ Gets public key from X.509 certificate */
PHP_FUNCTION(openssl_pkey_get_public)4545 PHP_FUNCTION(openssl_pkey_get_public)
4546 {
4547 	zval *cert;
4548 	EVP_PKEY *pkey;
4549 	php_openssl_pkey_object *key_object;
4550 
4551 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &cert) == FAILURE) {
4552 		RETURN_THROWS();
4553 	}
4554 	pkey = php_openssl_pkey_from_zval(cert, 1, NULL, 0, 1);
4555 	if (pkey == NULL) {
4556 		RETURN_FALSE;
4557 	}
4558 
4559 	object_init_ex(return_value, php_openssl_pkey_ce);
4560 	key_object = Z_OPENSSL_PKEY_P(return_value);
4561 	key_object->pkey = pkey;
4562 }
4563 /* }}} */
4564 
4565 /* {{{ Frees a key */
PHP_FUNCTION(openssl_pkey_free)4566 PHP_FUNCTION(openssl_pkey_free)
4567 {
4568 	zval *key;
4569 
4570 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &key, php_openssl_pkey_ce) == FAILURE) {
4571 		RETURN_THROWS();
4572 	}
4573 }
4574 /* }}} */
4575 
4576 /* {{{ Gets private keys */
PHP_FUNCTION(openssl_pkey_get_private)4577 PHP_FUNCTION(openssl_pkey_get_private)
4578 {
4579 	zval *cert;
4580 	EVP_PKEY *pkey;
4581 	char * passphrase = "";
4582 	size_t passphrase_len = sizeof("")-1;
4583 	php_openssl_pkey_object *key_object;
4584 
4585 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!", &cert, &passphrase, &passphrase_len) == FAILURE) {
4586 		RETURN_THROWS();
4587 	}
4588 
4589 	if (passphrase) {
4590 		PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase, 2);
4591 	}
4592 
4593 	pkey = php_openssl_pkey_from_zval(cert, 0, passphrase, passphrase_len, 1);
4594 	if (pkey == NULL) {
4595 		RETURN_FALSE;
4596 	}
4597 
4598 	object_init_ex(return_value, php_openssl_pkey_ce);
4599 	key_object = Z_OPENSSL_PKEY_P(return_value);
4600 	key_object->pkey = pkey;
4601 }
4602 
4603 /* }}} */
4604 
4605 /* {{{ returns an array with the key details (bits, pkey, type)*/
PHP_FUNCTION(openssl_pkey_get_details)4606 PHP_FUNCTION(openssl_pkey_get_details)
4607 {
4608 	zval *key;
4609 	EVP_PKEY *pkey;
4610 	BIO *out;
4611 	unsigned int pbio_len;
4612 	char *pbio;
4613 	zend_long ktype;
4614 
4615 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &key, php_openssl_pkey_ce) == FAILURE) {
4616 		RETURN_THROWS();
4617 	}
4618 
4619 	pkey = Z_OPENSSL_PKEY_P(key)->pkey;
4620 
4621 	out = BIO_new(BIO_s_mem());
4622 	if (!PEM_write_bio_PUBKEY(out, pkey)) {
4623 		BIO_free(out);
4624 		php_openssl_store_errors();
4625 		RETURN_FALSE;
4626 	}
4627 	pbio_len = BIO_get_mem_data(out, &pbio);
4628 
4629 	array_init(return_value);
4630 	add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey));
4631 	add_assoc_stringl(return_value, "key", pbio, pbio_len);
4632 	/*TODO: Use the real values once the openssl constants are used
4633 	 * See the enum at the top of this file
4634 	 */
4635 	switch (EVP_PKEY_base_id(pkey)) {
4636 		case EVP_PKEY_RSA:
4637 		case EVP_PKEY_RSA2:
4638 			{
4639 				RSA *rsa = EVP_PKEY_get0_RSA(pkey);
4640 				ktype = OPENSSL_KEYTYPE_RSA;
4641 
4642 				if (rsa != NULL) {
4643 					zval z_rsa;
4644 					const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
4645 
4646 					RSA_get0_key(rsa, &n, &e, &d);
4647 					RSA_get0_factors(rsa, &p, &q);
4648 					RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
4649 
4650 					array_init(&z_rsa);
4651 					OPENSSL_PKEY_GET_BN(z_rsa, n);
4652 					OPENSSL_PKEY_GET_BN(z_rsa, e);
4653 					OPENSSL_PKEY_GET_BN(z_rsa, d);
4654 					OPENSSL_PKEY_GET_BN(z_rsa, p);
4655 					OPENSSL_PKEY_GET_BN(z_rsa, q);
4656 					OPENSSL_PKEY_GET_BN(z_rsa, dmp1);
4657 					OPENSSL_PKEY_GET_BN(z_rsa, dmq1);
4658 					OPENSSL_PKEY_GET_BN(z_rsa, iqmp);
4659 					add_assoc_zval(return_value, "rsa", &z_rsa);
4660 				}
4661 			}
4662 			break;
4663 		case EVP_PKEY_DSA:
4664 		case EVP_PKEY_DSA2:
4665 		case EVP_PKEY_DSA3:
4666 		case EVP_PKEY_DSA4:
4667 			{
4668 				DSA *dsa = EVP_PKEY_get0_DSA(pkey);
4669 				ktype = OPENSSL_KEYTYPE_DSA;
4670 
4671 				if (dsa != NULL) {
4672 					zval z_dsa;
4673 					const BIGNUM *p, *q, *g, *priv_key, *pub_key;
4674 
4675 					DSA_get0_pqg(dsa, &p, &q, &g);
4676 					DSA_get0_key(dsa, &pub_key, &priv_key);
4677 
4678 					array_init(&z_dsa);
4679 					OPENSSL_PKEY_GET_BN(z_dsa, p);
4680 					OPENSSL_PKEY_GET_BN(z_dsa, q);
4681 					OPENSSL_PKEY_GET_BN(z_dsa, g);
4682 					OPENSSL_PKEY_GET_BN(z_dsa, priv_key);
4683 					OPENSSL_PKEY_GET_BN(z_dsa, pub_key);
4684 					add_assoc_zval(return_value, "dsa", &z_dsa);
4685 				}
4686 			}
4687 			break;
4688 		case EVP_PKEY_DH:
4689 			{
4690 				DH *dh = EVP_PKEY_get0_DH(pkey);
4691 				ktype = OPENSSL_KEYTYPE_DH;
4692 
4693 				if (dh != NULL) {
4694 					zval z_dh;
4695 					const BIGNUM *p, *q, *g, *priv_key, *pub_key;
4696 
4697 					DH_get0_pqg(dh, &p, &q, &g);
4698 					DH_get0_key(dh, &pub_key, &priv_key);
4699 
4700 					array_init(&z_dh);
4701 					OPENSSL_PKEY_GET_BN(z_dh, p);
4702 					OPENSSL_PKEY_GET_BN(z_dh, g);
4703 					OPENSSL_PKEY_GET_BN(z_dh, priv_key);
4704 					OPENSSL_PKEY_GET_BN(z_dh, pub_key);
4705 					add_assoc_zval(return_value, "dh", &z_dh);
4706 				}
4707 			}
4708 			break;
4709 #ifdef HAVE_EVP_PKEY_EC
4710 		case EVP_PKEY_EC:
4711 			ktype = OPENSSL_KEYTYPE_EC;
4712 			if (EVP_PKEY_get0_EC_KEY(pkey) != NULL) {
4713 				zval ec;
4714 				const EC_GROUP *ec_group;
4715 				const EC_POINT *pub;
4716 				int nid;
4717 				char *crv_sn;
4718 				ASN1_OBJECT *obj;
4719 				// openssl recommends a buffer length of 80
4720 				char oir_buf[80];
4721 				const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
4722 				BIGNUM *x = BN_new();
4723 				BIGNUM *y = BN_new();
4724 				const BIGNUM *d;
4725 
4726 				ec_group = EC_KEY_get0_group(ec_key);
4727 
4728 				// Curve nid (numerical identifier) used for ASN1 mapping
4729 				nid = EC_GROUP_get_curve_name(ec_group);
4730 				if (nid == NID_undef) {
4731 					break;
4732 				}
4733 				array_init(&ec);
4734 
4735 				// Short object name
4736 				crv_sn = (char*) OBJ_nid2sn(nid);
4737 				if (crv_sn != NULL) {
4738 					add_assoc_string(&ec, "curve_name", crv_sn);
4739 				}
4740 
4741 				obj = OBJ_nid2obj(nid);
4742 				if (obj != NULL) {
4743 					int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
4744 					add_assoc_stringl(&ec, "curve_oid", (char*) oir_buf, oir_len);
4745 					ASN1_OBJECT_free(obj);
4746 				}
4747 
4748 				pub = EC_KEY_get0_public_key(ec_key);
4749 
4750 				if (EC_POINT_get_affine_coordinates_GFp(ec_group, pub, x, y, NULL)) {
4751 					OPENSSL_GET_BN(ec, x, x);
4752 					OPENSSL_GET_BN(ec, y, y);
4753 				} else {
4754 					php_openssl_store_errors();
4755 				}
4756 
4757 				if ((d = EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(pkey))) != NULL) {
4758 					OPENSSL_GET_BN(ec, d, d);
4759 				}
4760 
4761 				add_assoc_zval(return_value, "ec", &ec);
4762 
4763 				BN_free(x);
4764 				BN_free(y);
4765 			}
4766 			break;
4767 #endif
4768 		default:
4769 			ktype = -1;
4770 			break;
4771 	}
4772 	add_assoc_long(return_value, "type", ktype);
4773 
4774 	BIO_free(out);
4775 }
4776 /* }}} */
4777 
4778 /* {{{ Computes shared secret for public value of remote DH key and local DH key */
PHP_FUNCTION(openssl_dh_compute_key)4779 PHP_FUNCTION(openssl_dh_compute_key)
4780 {
4781 	zval *key;
4782 	char *pub_str;
4783 	size_t pub_len;
4784 	DH *dh;
4785 	EVP_PKEY *pkey;
4786 	BIGNUM *pub;
4787 	zend_string *data;
4788 	int len;
4789 
4790 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sO", &pub_str, &pub_len, &key, php_openssl_pkey_ce) == FAILURE) {
4791 		RETURN_THROWS();
4792 	}
4793 
4794 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(pub_len, pub_key, 1);
4795 
4796 	pkey = Z_OPENSSL_PKEY_P(key)->pkey;
4797 
4798 	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) {
4799 		RETURN_FALSE;
4800 	}
4801 	dh = EVP_PKEY_get0_DH(pkey);
4802 	if (dh == NULL) {
4803 		RETURN_FALSE;
4804 	}
4805 
4806 	pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
4807 
4808 	data = zend_string_alloc(DH_size(dh), 0);
4809 	len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, dh);
4810 
4811 	if (len >= 0) {
4812 		ZSTR_LEN(data) = len;
4813 		ZSTR_VAL(data)[len] = 0;
4814 		RETVAL_NEW_STR(data);
4815 	} else {
4816 		php_openssl_store_errors();
4817 		zend_string_release_ex(data, 0);
4818 		RETVAL_FALSE;
4819 	}
4820 
4821 	BN_free(pub);
4822 }
4823 /* }}} */
4824 
4825 /* {{{ Computes shared secret for public value of remote and local DH or ECDH key */
PHP_FUNCTION(openssl_pkey_derive)4826 PHP_FUNCTION(openssl_pkey_derive)
4827 {
4828 	zval *priv_key;
4829 	zval *peer_pub_key;
4830 	EVP_PKEY *pkey = NULL;
4831 	EVP_PKEY *peer_key = NULL;
4832 	EVP_PKEY_CTX *ctx = NULL;
4833 	size_t key_size;
4834 	zend_long key_len = 0;
4835 	zend_string *result;
4836 
4837 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &peer_pub_key, &priv_key, &key_len) == FAILURE) {
4838 		RETURN_THROWS();
4839 	}
4840 
4841 	RETVAL_FALSE;
4842 	if (key_len < 0) {
4843 		zend_argument_value_error(3, "must be greater than or equal to 0");
4844 		RETURN_THROWS();
4845 	}
4846 
4847 	key_size = key_len;
4848 	pkey = php_openssl_pkey_from_zval(priv_key, 0, "", 0, 2);
4849 	if (!pkey) {
4850 		goto cleanup;
4851 	}
4852 
4853 	peer_key = php_openssl_pkey_from_zval(peer_pub_key, 1, NULL, 0, 1);
4854 	if (!peer_key) {
4855 		goto cleanup;
4856 	}
4857 
4858 	ctx = EVP_PKEY_CTX_new(pkey, NULL);
4859 	if (!ctx) {
4860 		goto cleanup;
4861 	}
4862 
4863 	if (EVP_PKEY_derive_init(ctx) > 0
4864 		&& EVP_PKEY_derive_set_peer(ctx, peer_key) > 0
4865 		&& (key_size > 0 || EVP_PKEY_derive(ctx, NULL, &key_size) > 0)
4866 		&& (result = zend_string_alloc(key_size, 0)) != NULL) {
4867 		if (EVP_PKEY_derive(ctx, (unsigned char*)ZSTR_VAL(result), &key_size) > 0) {
4868 			ZSTR_LEN(result) = key_size;
4869 			ZSTR_VAL(result)[key_size] = 0;
4870 			RETVAL_NEW_STR(result);
4871 		} else {
4872 			php_openssl_store_errors();
4873 			zend_string_release_ex(result, 0);
4874 			RETVAL_FALSE;
4875 		}
4876 	}
4877 
4878 cleanup:
4879 	EVP_PKEY_free(pkey);
4880 	EVP_PKEY_free(peer_key);
4881 	if (ctx) {
4882 		EVP_PKEY_CTX_free(ctx);
4883 	}
4884 }
4885 /* }}} */
4886 
4887 
4888 /* {{{ Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
PHP_FUNCTION(openssl_pbkdf2)4889 PHP_FUNCTION(openssl_pbkdf2)
4890 {
4891 	zend_long key_length = 0, iterations = 0;
4892 	char *password;
4893 	size_t password_len;
4894 	char *salt;
4895 	size_t salt_len;
4896 	char *method;
4897 	size_t method_len = 0;
4898 	zend_string *out_buffer;
4899 
4900 	const EVP_MD *digest;
4901 
4902 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssll|s",
4903 				&password, &password_len,
4904 				&salt, &salt_len,
4905 				&key_length, &iterations,
4906 				&method, &method_len) == FAILURE) {
4907 		RETURN_THROWS();
4908 	}
4909 
4910 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password, 1);
4911 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(salt_len, salt, 2);
4912 	PHP_OPENSSL_CHECK_LONG_TO_INT(key_length, key, 3);
4913 	PHP_OPENSSL_CHECK_LONG_TO_INT(iterations, iterations, 4);
4914 
4915 	if (key_length <= 0) {
4916 		zend_argument_value_error(3, "must be greater than 0");
4917 		RETURN_THROWS();
4918 	}
4919 
4920 	if (method_len) {
4921 		digest = EVP_get_digestbyname(method);
4922 	} else {
4923 		digest = EVP_sha1();
4924 	}
4925 
4926 	if (!digest) {
4927 		php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
4928 		RETURN_FALSE;
4929 	}
4930 
4931 	out_buffer = zend_string_alloc(key_length, 0);
4932 
4933 	if (PKCS5_PBKDF2_HMAC(password, (int)password_len, (unsigned char *)salt, (int)salt_len, (int)iterations, digest, (int)key_length, (unsigned char*)ZSTR_VAL(out_buffer)) == 1) {
4934 		ZSTR_VAL(out_buffer)[key_length] = 0;
4935 		RETURN_NEW_STR(out_buffer);
4936 	} else {
4937 		php_openssl_store_errors();
4938 		zend_string_release_ex(out_buffer, 0);
4939 		RETURN_FALSE;
4940 	}
4941 }
4942 /* }}} */
4943 
4944 /** openssl bio new file helper */
php_openssl_bio_new_file(const char * filename,size_t filename_len,uint32_t arg_num,const char * mode)4945 static BIO *php_openssl_bio_new_file(
4946 		const char *filename, size_t filename_len, uint32_t arg_num, const char *mode) {
4947 	char file_path[MAXPATHLEN];
4948 	BIO *bio;
4949 
4950 	if (!php_openssl_check_path(filename, filename_len, file_path, arg_num)) {
4951 		return NULL;
4952 	}
4953 
4954 	bio = BIO_new_file(file_path, mode);
4955 	if (bio == NULL) {
4956 		php_openssl_store_errors();
4957 		return NULL;
4958 	}
4959 
4960 	return bio;
4961 }
4962 
4963 /* {{{ PKCS7 S/MIME functions */
4964 
4965 /* {{{ Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */
PHP_FUNCTION(openssl_pkcs7_verify)4966 PHP_FUNCTION(openssl_pkcs7_verify)
4967 {
4968 	X509_STORE * store = NULL;
4969 	zval * cainfo = NULL;
4970 	STACK_OF(X509) *signers= NULL;
4971 	STACK_OF(X509) *others = NULL;
4972 	PKCS7 * p7 = NULL;
4973 	BIO * in = NULL, * datain = NULL, * dataout = NULL, * p7bout  = NULL;
4974 	zend_long flags = 0;
4975 	char * filename;
4976 	size_t filename_len;
4977 	char * extracerts = NULL;
4978 	size_t extracerts_len = 0;
4979 	char * signersfilename = NULL;
4980 	size_t signersfilename_len = 0;
4981 	char * datafilename = NULL;
4982 	size_t datafilename_len = 0;
4983 	char * p7bfilename = NULL;
4984 	size_t p7bfilename_len = 0;
4985 
4986 	RETVAL_LONG(-1);
4987 
4988 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|p!ap!p!p!", &filename, &filename_len,
4989 				&flags, &signersfilename, &signersfilename_len, &cainfo,
4990 				&extracerts, &extracerts_len, &datafilename, &datafilename_len, &p7bfilename, &p7bfilename_len) == FAILURE) {
4991 		RETURN_THROWS();
4992 	}
4993 
4994 	if (extracerts) {
4995 		others = php_openssl_load_all_certs_from_file(extracerts, extracerts_len, 5);
4996 		if (others == NULL) {
4997 			goto clean_exit;
4998 		}
4999 	}
5000 
5001 	flags = flags & ~PKCS7_DETACHED;
5002 
5003 	store = php_openssl_setup_verify(cainfo, 4);
5004 
5005 	if (!store) {
5006 		goto clean_exit;
5007 	}
5008 
5009 	in = php_openssl_bio_new_file(filename, filename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5010 	if (in == NULL) {
5011 		goto clean_exit;
5012 	}
5013 
5014 	p7 = SMIME_read_PKCS7(in, &datain);
5015 	if (p7 == NULL) {
5016 #if DEBUG_SMIME
5017 		zend_printf("SMIME_read_PKCS7 failed\n");
5018 #endif
5019 		php_openssl_store_errors();
5020 		goto clean_exit;
5021 	}
5022 	if (datafilename) {
5023 		dataout = php_openssl_bio_new_file(
5024 				datafilename, datafilename_len, 6, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5025 		if (dataout == NULL) {
5026 			goto clean_exit;
5027 		}
5028 	}
5029 	if (p7bfilename) {
5030 		p7bout = php_openssl_bio_new_file(
5031 				p7bfilename, p7bfilename_len, 7, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5032 		if (p7bout == NULL) {
5033 			goto clean_exit;
5034 		}
5035 	}
5036 #if DEBUG_SMIME
5037 	zend_printf("Calling PKCS7 verify\n");
5038 #endif
5039 
5040 	if (PKCS7_verify(p7, others, store, datain, dataout, (int)flags)) {
5041 
5042 		RETVAL_TRUE;
5043 
5044 		if (signersfilename) {
5045 			BIO *certout;
5046 
5047 			certout = php_openssl_bio_new_file(
5048 					signersfilename, signersfilename_len, 3, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5049 			if (certout) {
5050 				int i;
5051 				signers = PKCS7_get0_signers(p7, NULL, (int)flags);
5052 				if (signers != NULL) {
5053 
5054 					for (i = 0; i < sk_X509_num(signers); i++) {
5055 						if (!PEM_write_bio_X509(certout, sk_X509_value(signers, i))) {
5056 							php_openssl_store_errors();
5057 							RETVAL_LONG(-1);
5058 							php_error_docref(NULL, E_WARNING, "Failed to write signer %d", i);
5059 						}
5060 					}
5061 
5062 					sk_X509_free(signers);
5063 				} else {
5064 					RETVAL_LONG(-1);
5065 					php_openssl_store_errors();
5066 				}
5067 
5068 				BIO_free(certout);
5069 			} else {
5070 				php_error_docref(NULL, E_WARNING, "Signature OK, but cannot open %s for writing", signersfilename);
5071 				RETVAL_LONG(-1);
5072 			}
5073 
5074 			if (p7bout) {
5075 				PEM_write_bio_PKCS7(p7bout, p7);
5076 			}
5077 		}
5078 	} else {
5079 		php_openssl_store_errors();
5080 		RETVAL_FALSE;
5081 	}
5082 clean_exit:
5083 	if (p7bout) {
5084 		BIO_free(p7bout);
5085 	}
5086 	X509_STORE_free(store);
5087 	BIO_free(datain);
5088 	BIO_free(in);
5089 	BIO_free(dataout);
5090 	PKCS7_free(p7);
5091 	sk_X509_pop_free(others, X509_free);
5092 }
5093 /* }}} */
5094 
5095 /* {{{ Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */
PHP_FUNCTION(openssl_pkcs7_encrypt)5096 PHP_FUNCTION(openssl_pkcs7_encrypt)
5097 {
5098 	zval * zrecipcerts, * zheaders = NULL;
5099 	STACK_OF(X509) * recipcerts = NULL;
5100 	BIO * infile = NULL, * outfile = NULL;
5101 	zend_long flags = 0;
5102 	PKCS7 * p7 = NULL;
5103 	zval * zcertval;
5104 	X509 * cert;
5105 	const EVP_CIPHER *cipher = NULL;
5106 	zend_long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
5107 	zend_string * strindex;
5108 	char * infilename = NULL;
5109 	size_t infilename_len;
5110 	char * outfilename = NULL;
5111 	size_t outfilename_len;
5112 
5113 	RETVAL_FALSE;
5114 
5115 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppza!|ll", &infilename, &infilename_len,
5116 				&outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE) {
5117 		RETURN_THROWS();
5118 	}
5119 
5120 	infile = php_openssl_bio_new_file(infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5121 	if (infile == NULL) {
5122 		goto clean_exit;
5123 	}
5124 
5125 	outfile = php_openssl_bio_new_file(outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(flags));
5126 	if (outfile == NULL) {
5127 		goto clean_exit;
5128 	}
5129 
5130 	recipcerts = sk_X509_new_null();
5131 
5132 	/* get certs */
5133 	if (Z_TYPE_P(zrecipcerts) == IS_ARRAY) {
5134 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zrecipcerts), zcertval) {
5135 			bool free_cert;
5136 
5137 			cert = php_openssl_x509_from_zval(zcertval, &free_cert, 3, true, NULL);
5138 			if (cert == NULL) {
5139 				// TODO Add warning?
5140 				goto clean_exit;
5141 			}
5142 
5143 			if (!free_cert) {
5144 				/* we shouldn't free this particular cert, as it is a resource.
5145 					make a copy and push that on the stack instead */
5146 				cert = X509_dup(cert);
5147 				if (cert == NULL) {
5148 					php_openssl_store_errors();
5149 					goto clean_exit;
5150 				}
5151 			}
5152 			sk_X509_push(recipcerts, cert);
5153 		} ZEND_HASH_FOREACH_END();
5154 	} else {
5155 		/* a single certificate */
5156 		bool free_cert;
5157 
5158 		cert = php_openssl_x509_from_zval(zrecipcerts, &free_cert, 3, false, NULL);
5159 		if (cert == NULL) {
5160 			// TODO Add warning?
5161 			goto clean_exit;
5162 		}
5163 
5164 		if (!free_cert) {
5165 			/* we shouldn't free this particular cert, as it is a resource.
5166 				make a copy and push that on the stack instead */
5167 			cert = X509_dup(cert);
5168 			if (cert == NULL) {
5169 				php_openssl_store_errors();
5170 				goto clean_exit;
5171 			}
5172 		}
5173 		sk_X509_push(recipcerts, cert);
5174 	}
5175 
5176 	/* sanity check the cipher */
5177 	cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
5178 	if (cipher == NULL) {
5179 		/* shouldn't happen */
5180 		php_error_docref(NULL, E_WARNING, "Failed to get cipher");
5181 		goto clean_exit;
5182 	}
5183 
5184 	p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, (int)flags);
5185 
5186 	if (p7 == NULL) {
5187 		php_openssl_store_errors();
5188 		goto clean_exit;
5189 	}
5190 
5191 	/* tack on extra headers */
5192 	if (zheaders) {
5193 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, zcertval) {
5194 			zend_string *str = zval_try_get_string(zcertval);
5195 			if (UNEXPECTED(!str)) {
5196 				goto clean_exit;
5197 			}
5198 			if (strindex) {
5199 				BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
5200 			} else {
5201 				BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
5202 			}
5203 			zend_string_release(str);
5204 		} ZEND_HASH_FOREACH_END();
5205 	}
5206 
5207 	(void)BIO_reset(infile);
5208 
5209 	/* write the encrypted data */
5210 	if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
5211 		php_openssl_store_errors();
5212 		goto clean_exit;
5213 	}
5214 
5215 	RETVAL_TRUE;
5216 
5217 clean_exit:
5218 	PKCS7_free(p7);
5219 	BIO_free(infile);
5220 	BIO_free(outfile);
5221 	if (recipcerts) {
5222 		sk_X509_pop_free(recipcerts, X509_free);
5223 	}
5224 }
5225 /* }}} */
5226 
5227 /* {{{ Exports the PKCS7 file to an array of PEM certificates */
PHP_FUNCTION(openssl_pkcs7_read)5228 PHP_FUNCTION(openssl_pkcs7_read)
5229 {
5230 	zval * zout = NULL, zcert;
5231 	char *p7b;
5232 	size_t p7b_len;
5233 	STACK_OF(X509) *certs = NULL;
5234 	STACK_OF(X509_CRL) *crls = NULL;
5235 	BIO * bio_in = NULL, * bio_out = NULL;
5236 	PKCS7 * p7 = NULL;
5237 	int i;
5238 
5239 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &p7b, &p7b_len,
5240 				&zout) == FAILURE) {
5241 		RETURN_THROWS();
5242 	}
5243 
5244 	RETVAL_FALSE;
5245 
5246 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(p7b_len, p7b, 1);
5247 
5248 	bio_in = BIO_new(BIO_s_mem());
5249 	if (bio_in == NULL) {
5250 		goto clean_exit;
5251 	}
5252 
5253 	if (0 >= BIO_write(bio_in, p7b, (int)p7b_len)) {
5254 		php_openssl_store_errors();
5255 		goto clean_exit;
5256 	}
5257 
5258 	p7 = PEM_read_bio_PKCS7(bio_in, NULL, NULL, NULL);
5259 	if (p7 == NULL) {
5260 		php_openssl_store_errors();
5261 		goto clean_exit;
5262 	}
5263 
5264 	switch (OBJ_obj2nid(p7->type)) {
5265 		case NID_pkcs7_signed:
5266 			if (p7->d.sign != NULL) {
5267 				certs = p7->d.sign->cert;
5268 				crls = p7->d.sign->crl;
5269 			}
5270 			break;
5271 		case NID_pkcs7_signedAndEnveloped:
5272 			if (p7->d.signed_and_enveloped != NULL) {
5273 				certs = p7->d.signed_and_enveloped->cert;
5274 				crls = p7->d.signed_and_enveloped->crl;
5275 			}
5276 			break;
5277 		default:
5278 			break;
5279 	}
5280 
5281 	zout = zend_try_array_init(zout);
5282 	if (!zout) {
5283 		goto clean_exit;
5284 	}
5285 
5286 	if (certs != NULL) {
5287 		for (i = 0; i < sk_X509_num(certs); i++) {
5288 			X509* ca = sk_X509_value(certs, i);
5289 
5290 			bio_out = BIO_new(BIO_s_mem());
5291 			if (bio_out && PEM_write_bio_X509(bio_out, ca)) {
5292 				BUF_MEM *bio_buf;
5293 				BIO_get_mem_ptr(bio_out, &bio_buf);
5294 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
5295 				add_index_zval(zout, i, &zcert);
5296 				BIO_free(bio_out);
5297 			}
5298 		}
5299 	}
5300 
5301 	if (crls != NULL) {
5302 		for (i = 0; i < sk_X509_CRL_num(crls); i++) {
5303 			X509_CRL* crl = sk_X509_CRL_value(crls, i);
5304 
5305 			bio_out = BIO_new(BIO_s_mem());
5306 			if (bio_out && PEM_write_bio_X509_CRL(bio_out, crl)) {
5307 				BUF_MEM *bio_buf;
5308 				BIO_get_mem_ptr(bio_out, &bio_buf);
5309 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
5310 				add_index_zval(zout, i, &zcert);
5311 				BIO_free(bio_out);
5312 			}
5313 		}
5314 	}
5315 
5316 	RETVAL_TRUE;
5317 
5318 clean_exit:
5319 	BIO_free(bio_in);
5320 
5321 	if (p7 != NULL) {
5322 		PKCS7_free(p7);
5323 	}
5324 }
5325 /* }}} */
5326 
5327 /* {{{ Signs the MIME message in the file named infile with signcert/signkey and output the result to file name outfile. headers lists plain text headers to exclude from the signed portion of the message, and should include to, from and subject as a minimum */
5328 
PHP_FUNCTION(openssl_pkcs7_sign)5329 PHP_FUNCTION(openssl_pkcs7_sign)
5330 {
5331 	X509 *cert = NULL;
5332 	zend_object *cert_obj;
5333 	zend_string *cert_str;
5334 	zval *zprivkey, * zheaders;
5335 	zval * hval;
5336 	EVP_PKEY * privkey = NULL;
5337 	zend_long flags = PKCS7_DETACHED;
5338 	PKCS7 * p7 = NULL;
5339 	BIO * infile = NULL, * outfile = NULL;
5340 	STACK_OF(X509) *others = NULL;
5341 	zend_string * strindex;
5342 	char * infilename;
5343 	size_t infilename_len;
5344 	char * outfilename;
5345 	size_t outfilename_len;
5346 	char * extracertsfilename = NULL;
5347 	size_t extracertsfilename_len;
5348 
5349 	ZEND_PARSE_PARAMETERS_START(5, 7)
5350 		Z_PARAM_PATH(infilename, infilename_len)
5351 		Z_PARAM_PATH(outfilename, outfilename_len)
5352 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
5353 		Z_PARAM_ZVAL(zprivkey)
5354 		Z_PARAM_ARRAY_OR_NULL(zheaders)
5355 		Z_PARAM_OPTIONAL
5356 		Z_PARAM_LONG(flags)
5357 		Z_PARAM_PATH_OR_NULL(extracertsfilename, extracertsfilename_len)
5358 	ZEND_PARSE_PARAMETERS_END();
5359 
5360 	RETVAL_FALSE;
5361 
5362 	if (extracertsfilename) {
5363 		others = php_openssl_load_all_certs_from_file(
5364 				extracertsfilename, extracertsfilename_len, 7);
5365 		if (others == NULL) {
5366 			goto clean_exit;
5367 		}
5368 	}
5369 
5370 	privkey = php_openssl_pkey_from_zval(zprivkey, 0, "", 0, 4);
5371 	if (privkey == NULL) {
5372 		if (!EG(exception)) {
5373 			php_error_docref(NULL, E_WARNING, "Error getting private key");
5374 		}
5375 		goto clean_exit;
5376 	}
5377 
5378 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 3);
5379 	if (cert == NULL) {
5380 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
5381 		goto clean_exit;
5382 	}
5383 
5384 	infile = php_openssl_bio_new_file(infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5385 	if (infile == NULL) {
5386 		php_error_docref(NULL, E_WARNING, "Error opening input file %s!", infilename);
5387 		goto clean_exit;
5388 	}
5389 
5390 	outfile = php_openssl_bio_new_file(outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5391 	if (outfile == NULL) {
5392 		php_error_docref(NULL, E_WARNING, "Error opening output file %s!", outfilename);
5393 		goto clean_exit;
5394 	}
5395 
5396 	p7 = PKCS7_sign(cert, privkey, others, infile, (int)flags);
5397 	if (p7 == NULL) {
5398 		php_openssl_store_errors();
5399 		php_error_docref(NULL, E_WARNING, "Error creating PKCS7 structure!");
5400 		goto clean_exit;
5401 	}
5402 
5403 	(void)BIO_reset(infile);
5404 
5405 	/* tack on extra headers */
5406 	if (zheaders) {
5407 		int ret;
5408 
5409 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) {
5410 			zend_string *str = zval_try_get_string(hval);
5411 			if (UNEXPECTED(!str)) {
5412 				goto clean_exit;
5413 			}
5414 			if (strindex) {
5415 				ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
5416 			} else {
5417 				ret = BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
5418 			}
5419 			zend_string_release(str);
5420 			if (ret < 0) {
5421 				php_openssl_store_errors();
5422 			}
5423 		} ZEND_HASH_FOREACH_END();
5424 	}
5425 	/* write the signed data */
5426 	if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
5427 		php_openssl_store_errors();
5428 		goto clean_exit;
5429 	}
5430 
5431 	RETVAL_TRUE;
5432 
5433 clean_exit:
5434 	PKCS7_free(p7);
5435 	BIO_free(infile);
5436 	BIO_free(outfile);
5437 	if (others) {
5438 		sk_X509_pop_free(others, X509_free);
5439 	}
5440 	EVP_PKEY_free(privkey);
5441 	if (cert && cert_str) {
5442 		X509_free(cert);
5443 	}
5444 }
5445 /* }}} */
5446 
5447 /* {{{ Decrypts the S/MIME message in the file name infilename and output the results to the file name outfilename.  recipcert is a CERT for one of the recipients. recipkey specifies the private key matching recipcert, if recipcert does not include the key */
5448 
PHP_FUNCTION(openssl_pkcs7_decrypt)5449 PHP_FUNCTION(openssl_pkcs7_decrypt)
5450 {
5451 	X509 *cert;
5452 	zval *recipcert, *recipkey = NULL;
5453 	bool free_recipcert;
5454 	EVP_PKEY * key = NULL;
5455 	BIO * in = NULL, *out = NULL, *datain = NULL;
5456 	PKCS7 * p7 = NULL;
5457 	char * infilename;
5458 	size_t infilename_len;
5459 	char * outfilename;
5460 	size_t outfilename_len;
5461 
5462 	ZEND_PARSE_PARAMETERS_START(3, 4)
5463 		Z_PARAM_PATH(infilename, infilename_len)
5464 		Z_PARAM_PATH(outfilename, outfilename_len)
5465 		Z_PARAM_ZVAL(recipcert)
5466 		Z_PARAM_OPTIONAL
5467 		Z_PARAM_ZVAL_OR_NULL(recipkey)
5468 	ZEND_PARSE_PARAMETERS_END();
5469 
5470 	RETVAL_FALSE;
5471 
5472 	cert = php_openssl_x509_from_zval(recipcert, &free_recipcert, 3, false, NULL);
5473 	if (cert == NULL) {
5474 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
5475 		goto clean_exit;
5476 	}
5477 
5478 	key = php_openssl_pkey_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, 4);
5479 	if (key == NULL) {
5480 		if (!EG(exception)) {
5481 			php_error_docref(NULL, E_WARNING, "Unable to get private key");
5482 		}
5483 		goto clean_exit;
5484 	}
5485 
5486 	in = php_openssl_bio_new_file(
5487 			infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
5488 	if (in == NULL) {
5489 		goto clean_exit;
5490 	}
5491 
5492 	out = php_openssl_bio_new_file(
5493 			outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5494 	if (out == NULL) {
5495 		goto clean_exit;
5496 	}
5497 
5498 	p7 = SMIME_read_PKCS7(in, &datain);
5499 
5500 	if (p7 == NULL) {
5501 		php_openssl_store_errors();
5502 		goto clean_exit;
5503 	}
5504 	if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
5505 		RETVAL_TRUE;
5506 	} else {
5507 		php_openssl_store_errors();
5508 	}
5509 clean_exit:
5510 	PKCS7_free(p7);
5511 	BIO_free(datain);
5512 	BIO_free(in);
5513 	BIO_free(out);
5514 	if (cert && free_recipcert) {
5515 		X509_free(cert);
5516 	}
5517 	EVP_PKEY_free(key);
5518 }
5519 /* }}} */
5520 
5521 /* }}} */
5522 
5523 /* {{{ CMS S/MIME functions taken from PKCS#7 functions */
5524 
5525 /* {{{ Verifies that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */
PHP_FUNCTION(openssl_cms_verify)5526 PHP_FUNCTION(openssl_cms_verify)
5527 {
5528 	X509_STORE * store = NULL;
5529 	zval * cainfo = NULL;
5530 	STACK_OF(X509) *signers= NULL;
5531 	STACK_OF(X509) *others = NULL;
5532 	CMS_ContentInfo * cms = NULL;
5533 	BIO * in = NULL, * datain = NULL, * dataout = NULL, * p7bout  = NULL;
5534 	BIO *certout = NULL, *sigbio = NULL;
5535 	zend_long flags = 0;
5536 	char * filename;
5537 	size_t filename_len;
5538 	char * extracerts = NULL;
5539 	size_t extracerts_len = 0;
5540 	char * signersfilename = NULL;
5541 	size_t signersfilename_len = 0;
5542 	char * datafilename = NULL;
5543 	size_t datafilename_len = 0;
5544 	char * p7bfilename = NULL;
5545 	size_t p7bfilename_len = 0;
5546 	char * sigfile = NULL;
5547 	size_t sigfile_len = 0;
5548 	zend_long encoding = ENCODING_SMIME;
5549 
5550 	RETVAL_FALSE;
5551 
5552 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|p!ap!p!p!p!l", &filename, &filename_len,
5553 				&flags, &signersfilename, &signersfilename_len, &cainfo,
5554 				&extracerts, &extracerts_len, &datafilename, &datafilename_len,
5555 				&p7bfilename, &p7bfilename_len,
5556 				&sigfile, &sigfile_len, &encoding) == FAILURE) {
5557 		RETURN_THROWS();
5558 	}
5559 
5560 	in = php_openssl_bio_new_file(filename, filename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5561 	if (in == NULL) {
5562 		goto clean_exit;
5563 	}
5564 	if (sigfile && (flags & CMS_DETACHED)) {
5565 		sigbio = php_openssl_bio_new_file(sigfile, sigfile_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5566 		if (encoding == ENCODING_SMIME)  {
5567 			php_error_docref(NULL, E_WARNING,
5568 					 "Detached signatures not possible with S/MIME encoding");
5569 			goto clean_exit;
5570 		}
5571 	} else  {
5572 		sigbio = in;	/* non-detached signature */
5573 	}
5574 
5575 	switch (encoding) {
5576 		case ENCODING_PEM:
5577 			cms = PEM_read_bio_CMS(sigbio, NULL, 0, NULL);
5578 				datain = in;
5579 				break;
5580 			case ENCODING_DER:
5581 				cms = d2i_CMS_bio(sigbio, NULL);
5582 				datain = in;
5583 				break;
5584 			case ENCODING_SMIME:
5585 				cms = SMIME_read_CMS(sigbio, &datain);
5586 				break;
5587 			default:
5588 				php_error_docref(NULL, E_WARNING, "Unknown encoding");
5589 				goto clean_exit;
5590 	}
5591 	if (cms == NULL) {
5592 		php_openssl_store_errors();
5593 		goto clean_exit;
5594 	}
5595 	if (encoding != ENCODING_SMIME && !(flags & CMS_DETACHED)) {
5596 		datain = NULL; /* when not detached, don't pass a real BIO */
5597 	}
5598 
5599 	if (extracerts) {
5600 		others = php_openssl_load_all_certs_from_file(extracerts, extracerts_len, 5);
5601 		if (others == NULL) {
5602 			goto clean_exit;
5603 		}
5604 	}
5605 
5606 	store = php_openssl_setup_verify(cainfo, 4);
5607 
5608 	if (!store) {
5609 		goto clean_exit;
5610 	}
5611 
5612 	if (datafilename) {
5613 		dataout = php_openssl_bio_new_file(
5614 				datafilename, datafilename_len, 6, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
5615 		if (dataout == NULL) {
5616 			goto clean_exit;
5617 		}
5618 	}
5619 
5620 	if (p7bfilename) {
5621 		p7bout = php_openssl_bio_new_file(
5622 				p7bfilename, p7bfilename_len, 7, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
5623 		if (p7bout == NULL) {
5624 			goto clean_exit;
5625 		}
5626 	}
5627 #if DEBUG_SMIME
5628 	zend_printf("Calling CMS verify\n");
5629 #endif
5630 	if (CMS_verify(cms, others, store, datain, dataout, (unsigned int)flags)) {
5631 		RETVAL_TRUE;
5632 
5633 		if (signersfilename) {
5634 			certout = php_openssl_bio_new_file(
5635 					signersfilename, signersfilename_len, 3, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
5636 			if (certout) {
5637 				int i;
5638 				signers = CMS_get0_signers(cms);
5639 				if (signers != NULL) {
5640 
5641 					for (i = 0; i < sk_X509_num(signers); i++) {
5642 						if (!PEM_write_bio_X509(certout, sk_X509_value(signers, i))) {
5643 							php_openssl_store_errors();
5644 							RETVAL_FALSE;
5645 							php_error_docref(NULL, E_WARNING, "Failed to write signer %d", i);
5646 						}
5647 					}
5648 
5649 					sk_X509_free(signers);
5650 				} else {
5651 					RETVAL_FALSE;
5652 					php_openssl_store_errors();
5653 				}
5654 			} else {
5655 				php_error_docref(NULL, E_WARNING, "Signature OK, but cannot open %s for writing", signersfilename);
5656 				RETVAL_FALSE;
5657 			}
5658 
5659 			if (p7bout) {
5660 				PEM_write_bio_CMS(p7bout, cms);
5661 			}
5662 		}
5663 	} else {
5664 		php_openssl_store_errors();
5665 		RETVAL_FALSE;
5666 	}
5667 clean_exit:
5668 	BIO_free(p7bout);
5669 	if (store) {
5670 		X509_STORE_free(store);
5671 	}
5672 	if (datain != in) {
5673 		BIO_free(datain);
5674 	}
5675 	if (sigbio != in) {
5676 		BIO_free(sigbio);
5677 	}
5678 	BIO_free(in);
5679 	BIO_free(dataout);
5680 	BIO_free(certout);
5681 	if (cms) {
5682 		CMS_ContentInfo_free(cms);
5683 	}
5684 	if (others) {
5685 		sk_X509_pop_free(others, X509_free);
5686 	}
5687 }
5688 /* }}} */
5689 
5690 /* {{{ Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */
PHP_FUNCTION(openssl_cms_encrypt)5691 PHP_FUNCTION(openssl_cms_encrypt)
5692 {
5693 	zval * zrecipcerts, * zheaders = NULL;
5694 	STACK_OF(X509) * recipcerts = NULL;
5695 	BIO * infile = NULL, * outfile = NULL;
5696 	zend_long flags = 0;
5697 	zend_long encoding = ENCODING_SMIME;
5698 	CMS_ContentInfo * cms = NULL;
5699 	zval * zcertval;
5700 	X509 * cert;
5701 	const EVP_CIPHER *cipher = NULL;
5702 	zend_long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
5703 	zend_string * strindex;
5704 	char * infilename = NULL;
5705 	size_t infilename_len;
5706 	char * outfilename = NULL;
5707 	size_t outfilename_len;
5708 	int need_final = 0;
5709 
5710 	RETVAL_FALSE;
5711 
5712 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppza!|lll", &infilename, &infilename_len,
5713 				  &outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &encoding, &cipherid) == FAILURE) {
5714 		RETURN_THROWS();
5715 	}
5716 
5717 
5718 	infile = php_openssl_bio_new_file(
5719 			infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5720 	if (infile == NULL) {
5721 		goto clean_exit;
5722 	}
5723 
5724 	outfile = php_openssl_bio_new_file(
5725 			outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(flags));
5726 	if (outfile == NULL) {
5727 		goto clean_exit;
5728 	}
5729 
5730 	recipcerts = sk_X509_new_null();
5731 
5732 	/* get certs */
5733 	if (Z_TYPE_P(zrecipcerts) == IS_ARRAY) {
5734 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zrecipcerts), zcertval) {
5735 			bool free_cert;
5736 
5737 			cert = php_openssl_x509_from_zval(zcertval, &free_cert, 3, true, NULL);
5738 			if (cert == NULL) {
5739 				goto clean_exit;
5740 			}
5741 
5742 			if (!free_cert) {
5743 				/* we shouldn't free this particular cert, as it is a resource.
5744 					make a copy and push that on the stack instead */
5745 				cert = X509_dup(cert);
5746 				if (cert == NULL) {
5747 					php_openssl_store_errors();
5748 					goto clean_exit;
5749 				}
5750 			}
5751 			sk_X509_push(recipcerts, cert);
5752 		} ZEND_HASH_FOREACH_END();
5753 	} else {
5754 		/* a single certificate */
5755 		bool free_cert;
5756 
5757 		cert = php_openssl_x509_from_zval(zrecipcerts, &free_cert, 3, false, NULL);
5758 		if (cert == NULL) {
5759 			goto clean_exit;
5760 		}
5761 
5762 		if (!free_cert) {
5763 			/* we shouldn't free this particular cert, as it is a resource.
5764 				make a copy and push that on the stack instead */
5765 			cert = X509_dup(cert);
5766 			if (cert == NULL) {
5767 				php_openssl_store_errors();
5768 				goto clean_exit;
5769 			}
5770 		}
5771 		sk_X509_push(recipcerts, cert);
5772 	}
5773 
5774 	/* sanity check the cipher */
5775 	cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
5776 	if (cipher == NULL) {
5777 		/* shouldn't happen */
5778 		php_error_docref(NULL, E_WARNING, "Failed to get cipher");
5779 		goto clean_exit;
5780 	}
5781 
5782 	cms = CMS_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, (unsigned int)flags);
5783 
5784 	if (cms == NULL) {
5785 		php_openssl_store_errors();
5786 		goto clean_exit;
5787 	}
5788 
5789 	if (flags & CMS_PARTIAL && !(flags & CMS_STREAM)) {
5790 		need_final=1;
5791 	}
5792 
5793 	/* tack on extra headers */
5794 	if (zheaders && encoding == ENCODING_SMIME) {
5795 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, zcertval) {
5796 			zend_string *str = zval_try_get_string(zcertval);
5797 			if (UNEXPECTED(!str)) {
5798 				goto clean_exit;
5799 			}
5800 			if (strindex) {
5801 				BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
5802 			} else {
5803 				BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
5804 			}
5805 			zend_string_release(str);
5806 		} ZEND_HASH_FOREACH_END();
5807 	}
5808 
5809 	(void)BIO_reset(infile);
5810 
5811 	switch (encoding) {
5812 		case ENCODING_SMIME:
5813 			if (!SMIME_write_CMS(outfile, cms, infile, (int)flags)) {
5814 				php_openssl_store_errors();
5815 					goto clean_exit;
5816 			}
5817 			break;
5818 		case ENCODING_DER:
5819 			if (need_final) {
5820 				if (CMS_final(cms, infile, NULL, (unsigned int) flags) != 1) {
5821 					php_openssl_store_errors();
5822 					goto clean_exit;
5823 				}
5824 			}
5825 			if (i2d_CMS_bio(outfile, cms) != 1) {
5826 				php_openssl_store_errors();
5827 				goto clean_exit;
5828 			}
5829 			break;
5830 		case ENCODING_PEM:
5831 			if (need_final) {
5832 				if (CMS_final(cms, infile, NULL, (unsigned int) flags) != 1) {
5833 					php_openssl_store_errors();
5834 					goto clean_exit;
5835 				}
5836 			}
5837 			if (flags & CMS_STREAM) {
5838 				if (PEM_write_bio_CMS_stream(outfile, cms, infile, flags) == 0) {
5839 					php_openssl_store_errors();
5840 					goto clean_exit;
5841 				}
5842 			} else {
5843 				if (PEM_write_bio_CMS(outfile, cms) == 0) {
5844 					php_openssl_store_errors();
5845 					goto clean_exit;
5846 				}
5847 			}
5848 			break;
5849 		default:
5850 			php_error_docref(NULL, E_WARNING, "Unknown OPENSSL encoding");
5851 			goto clean_exit;
5852 	}
5853 
5854 	RETVAL_TRUE;
5855 
5856 clean_exit:
5857 	if (cms) {
5858 		CMS_ContentInfo_free(cms);
5859 	}
5860 	BIO_free(infile);
5861 	BIO_free(outfile);
5862 	if (recipcerts) {
5863 		sk_X509_pop_free(recipcerts, X509_free);
5864 	}
5865 }
5866 /* }}} */
5867 
5868 /* {{{ Exports the CMS file to an array of PEM certificates */
PHP_FUNCTION(openssl_cms_read)5869 PHP_FUNCTION(openssl_cms_read)
5870 {
5871 	zval * zout = NULL, zcert;
5872 	char *p7b;
5873 	size_t p7b_len;
5874 	STACK_OF(X509) *certs = NULL;
5875 	STACK_OF(X509_CRL) *crls = NULL;
5876 	BIO * bio_in = NULL, * bio_out = NULL;
5877 	CMS_ContentInfo * cms = NULL;
5878 	int i;
5879 
5880 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &p7b, &p7b_len,
5881 				&zout) == FAILURE) {
5882 		RETURN_THROWS();
5883 	}
5884 
5885 	RETVAL_FALSE;
5886 
5887 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(p7b_len, p7b, 1);
5888 
5889 	bio_in = BIO_new(BIO_s_mem());
5890 	if (bio_in == NULL) {
5891 		goto clean_exit;
5892 	}
5893 
5894 	if (0 >= BIO_write(bio_in, p7b, (int)p7b_len)) {
5895 		php_openssl_store_errors();
5896 		goto clean_exit;
5897 	}
5898 
5899 	cms = PEM_read_bio_CMS(bio_in, NULL, NULL, NULL);
5900 	if (cms == NULL) {
5901 		php_openssl_store_errors();
5902 		goto clean_exit;
5903 	}
5904 
5905 	switch (OBJ_obj2nid(CMS_get0_type(cms))) {
5906 		case NID_pkcs7_signed:
5907 		case NID_pkcs7_signedAndEnveloped:
5908 			certs = CMS_get1_certs(cms);
5909 			crls = CMS_get1_crls(cms);
5910 			break;
5911 		default:
5912 			break;
5913 	}
5914 
5915 	zout = zend_try_array_init(zout);
5916 	if (!zout) {
5917 		goto clean_exit;
5918 	}
5919 
5920 	if (certs != NULL) {
5921 		for (i = 0; i < sk_X509_num(certs); i++) {
5922 			X509* ca = sk_X509_value(certs, i);
5923 
5924 			bio_out = BIO_new(BIO_s_mem());
5925 			if (bio_out && PEM_write_bio_X509(bio_out, ca)) {
5926 				BUF_MEM *bio_buf;
5927 				BIO_get_mem_ptr(bio_out, &bio_buf);
5928 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
5929 				add_index_zval(zout, i, &zcert);
5930 				BIO_free(bio_out);
5931 			}
5932 		}
5933 	}
5934 
5935 	if (crls != NULL) {
5936 		for (i = 0; i < sk_X509_CRL_num(crls); i++) {
5937 			X509_CRL* crl = sk_X509_CRL_value(crls, i);
5938 
5939 			bio_out = BIO_new(BIO_s_mem());
5940 			if (bio_out && PEM_write_bio_X509_CRL(bio_out, crl)) {
5941 				BUF_MEM *bio_buf;
5942 				BIO_get_mem_ptr(bio_out, &bio_buf);
5943 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
5944 				add_index_zval(zout, i, &zcert);
5945 				BIO_free(bio_out);
5946 			}
5947 		}
5948 	}
5949 
5950 	RETVAL_TRUE;
5951 
5952 clean_exit:
5953 	BIO_free(bio_in);
5954 	if (cms != NULL) {
5955 		CMS_ContentInfo_free(cms);
5956 	}
5957 	if (certs != NULL) {
5958 		sk_X509_pop_free(certs, X509_free);
5959 	}
5960 	if (crls != NULL) {
5961 		sk_X509_CRL_pop_free(crls, X509_CRL_free);
5962 	}
5963 }
5964 /* }}} */
5965 
5966 /* {{{ Signs the MIME message in the file named infile with signcert/signkey and output the result to file name outfile. headers lists plain text headers to exclude from the signed portion of the message, and should include to, from and subject as a minimum */
5967 
PHP_FUNCTION(openssl_cms_sign)5968 PHP_FUNCTION(openssl_cms_sign)
5969 {
5970 	X509 *cert = NULL;
5971 	zend_object *cert_obj;
5972 	zend_string *cert_str;
5973 	zval *zprivkey, *zheaders;
5974 	zval * hval;
5975 	EVP_PKEY * privkey = NULL;
5976 	zend_long flags = 0;
5977 	zend_long encoding = ENCODING_SMIME;
5978 	CMS_ContentInfo * cms = NULL;
5979 	BIO * infile = NULL, * outfile = NULL;
5980 	STACK_OF(X509) *others = NULL;
5981 	zend_string * strindex;
5982 	char * infilename;
5983 	size_t infilename_len;
5984 	char * outfilename;
5985 	size_t outfilename_len;
5986 	char * extracertsfilename = NULL;
5987 	size_t extracertsfilename_len;
5988 	int need_final = 0;
5989 
5990 	ZEND_PARSE_PARAMETERS_START(5, 8)
5991 		Z_PARAM_PATH(infilename, infilename_len)
5992 		Z_PARAM_PATH(outfilename, outfilename_len)
5993 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
5994 		Z_PARAM_ZVAL(zprivkey)
5995 		Z_PARAM_ARRAY_OR_NULL(zheaders)
5996 		Z_PARAM_OPTIONAL
5997 		Z_PARAM_LONG(flags)
5998 		Z_PARAM_LONG(encoding)
5999 		Z_PARAM_PATH_OR_NULL(extracertsfilename, extracertsfilename_len)
6000 	ZEND_PARSE_PARAMETERS_END();
6001 
6002 	RETVAL_FALSE;
6003 
6004 	if (extracertsfilename) {
6005 		others = php_openssl_load_all_certs_from_file(
6006 				extracertsfilename, extracertsfilename_len, 8);
6007 		if (others == NULL) {
6008 			goto clean_exit;
6009 		}
6010 	}
6011 
6012 	privkey = php_openssl_pkey_from_zval(zprivkey, 0, "", 0, 4);
6013 	if (privkey == NULL) {
6014 		if (!EG(exception)) {
6015 			php_error_docref(NULL, E_WARNING, "Error getting private key");
6016 		}
6017 		goto clean_exit;
6018 	}
6019 
6020 	cert = php_openssl_x509_from_param(cert_obj, cert_str, 3);
6021 	if (cert == NULL) {
6022 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
6023 		goto clean_exit;
6024 	}
6025 
6026 	if ((encoding & ENCODING_SMIME) && (flags & CMS_DETACHED)) {
6027 		php_error_docref(NULL,
6028 				 E_WARNING, "Detached signatures not possible with S/MIME encoding");
6029 		goto clean_exit;
6030 	}
6031 
6032 	/* a CMS struct will not be complete if either CMS_PARTIAL or CMS_STREAM is set.
6033 	 * However, CMS_PARTIAL requires a CMS_final call whereas CMS_STREAM requires
6034 	 * a different write routine below.  There may be a more efficient way to do this
6035 	 * with function pointers, but the readability goes down.
6036 	 * References: CMS_sign(3SSL), CMS_final(3SSL)
6037 	 */
6038 
6039 	if (flags & CMS_PARTIAL && !(flags & CMS_STREAM)) {
6040 		need_final=1;
6041 	}
6042 
6043 	infile = php_openssl_bio_new_file(
6044 			infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
6045 	if (infile == NULL) {
6046 		php_error_docref(NULL, E_WARNING, "Error opening input file %s!", infilename);
6047 		goto clean_exit;
6048 	}
6049 
6050 	outfile = php_openssl_bio_new_file(
6051 			outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
6052 	if (outfile == NULL) {
6053 		php_error_docref(NULL, E_WARNING, "Error opening output file %s!", outfilename);
6054 		goto clean_exit;
6055 	}
6056 
6057 	cms = CMS_sign(cert, privkey, others, infile, (unsigned int)flags);
6058 	if (cms == NULL) {
6059 		php_openssl_store_errors();
6060 		php_error_docref(NULL, E_WARNING, "Error creating CMS structure!");
6061 		goto clean_exit;
6062 	}
6063 	if (BIO_reset(infile) != 0) {
6064 		php_openssl_store_errors();
6065 		goto clean_exit;
6066 	}
6067 
6068 	/* tack on extra headers */
6069 	if (zheaders && encoding == ENCODING_SMIME) {
6070 		int ret;
6071 
6072 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) {
6073 			zend_string *str = zval_try_get_string(hval);
6074 			if (UNEXPECTED(!str)) {
6075 				goto clean_exit;
6076 			}
6077 			if (strindex) {
6078 				ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
6079 			} else {
6080 				ret = BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
6081 			}
6082 			zend_string_release(str);
6083 			if (ret < 0) {
6084 				php_openssl_store_errors();
6085 			}
6086 		} ZEND_HASH_FOREACH_END();
6087 	}
6088 	/* writing the signed data depends on the encoding */
6089 	switch (encoding) {
6090 		case ENCODING_SMIME:
6091 			if (!SMIME_write_CMS(outfile, cms, infile, (int)flags)) {
6092 				php_openssl_store_errors();
6093 					goto clean_exit;
6094 			}
6095 			break;
6096 		case ENCODING_DER:
6097 			if (need_final) {
6098 				if (CMS_final(cms, infile, NULL, (unsigned int) flags) != 1) {
6099 					php_openssl_store_errors();
6100 					goto clean_exit;
6101 				}
6102 			}
6103 			if (i2d_CMS_bio(outfile, cms) != 1) {
6104 				php_openssl_store_errors();
6105 				goto clean_exit;
6106 			}
6107 			break;
6108 		case ENCODING_PEM:
6109 			if (need_final) {
6110 				if (CMS_final(cms, infile, NULL, (unsigned int) flags) != 1) {
6111 					php_openssl_store_errors();
6112 					goto clean_exit;
6113 				}
6114 			}
6115 			if (flags & CMS_STREAM) {
6116 				if (PEM_write_bio_CMS_stream(outfile, cms, infile, flags) == 0) {
6117 					php_openssl_store_errors();
6118 					goto clean_exit;
6119 				}
6120 			} else {
6121 				if (PEM_write_bio_CMS(outfile, cms) == 0) {
6122 					php_openssl_store_errors();
6123 					goto clean_exit;
6124 				}
6125 			}
6126 			break;
6127 		default:
6128 			php_error_docref(NULL, E_WARNING, "Unknown OPENSSL encoding");
6129 			goto clean_exit;
6130 	}
6131 	RETVAL_TRUE;
6132 
6133 clean_exit:
6134 	if (cms) {
6135 		CMS_ContentInfo_free(cms);
6136 	}
6137 	BIO_free(infile);
6138 	BIO_free(outfile);
6139 	if (others) {
6140 		sk_X509_pop_free(others, X509_free);
6141 	}
6142 	EVP_PKEY_free(privkey);
6143 	if (cert && cert_str) {
6144 		X509_free(cert);
6145 	}
6146 }
6147 /* }}} */
6148 
6149 /* {{{ Decrypts the S/MIME message in the file name infilename and output the results to the file name outfilename.  recipcert is a CERT for one of the recipients. recipkey specifies the private key matching recipcert, if recipcert does not include the key */
6150 
PHP_FUNCTION(openssl_cms_decrypt)6151 PHP_FUNCTION(openssl_cms_decrypt)
6152 {
6153 	X509 *cert;
6154 	zval *recipcert, *recipkey = NULL;
6155 	bool free_recipcert;
6156 	EVP_PKEY * key = NULL;
6157 	zend_long encoding = ENCODING_SMIME;
6158 	BIO * in = NULL, * out = NULL, * datain = NULL;
6159 	CMS_ContentInfo * cms = NULL;
6160 	char * infilename;
6161 	size_t infilename_len;
6162 	char * outfilename;
6163 	size_t outfilename_len;
6164 
6165 	ZEND_PARSE_PARAMETERS_START(3, 5)
6166 		Z_PARAM_PATH(infilename, infilename_len)
6167 		Z_PARAM_PATH(outfilename, outfilename_len)
6168 		Z_PARAM_ZVAL(recipcert)
6169 		Z_PARAM_OPTIONAL
6170 		Z_PARAM_ZVAL_OR_NULL(recipkey)
6171 		Z_PARAM_LONG(encoding)
6172 	ZEND_PARSE_PARAMETERS_END();
6173 
6174 	RETVAL_FALSE;
6175 
6176 	cert = php_openssl_x509_from_zval(recipcert, &free_recipcert, 3, false, NULL);
6177 	if (cert == NULL) {
6178 		php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
6179 		goto clean_exit;
6180 	}
6181 
6182 	key = php_openssl_pkey_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, recipkey ? 4 : 3);
6183 	if (key == NULL) {
6184 		if (!EG(exception)) {
6185 			php_error_docref(NULL, E_WARNING, "Unable to get private key");
6186 		}
6187 		goto clean_exit;
6188 	}
6189 
6190 	in = php_openssl_bio_new_file(
6191 			infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(CMS_BINARY));
6192 	if (in == NULL) {
6193 		goto clean_exit;
6194 	}
6195 
6196 	out = php_openssl_bio_new_file(
6197 			outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
6198 	if (out == NULL) {
6199 		goto clean_exit;
6200 	}
6201 
6202 	switch (encoding) {
6203 		case ENCODING_DER:
6204 			cms = d2i_CMS_bio(in, NULL);
6205 			break;
6206 		case ENCODING_PEM:
6207                         cms = PEM_read_bio_CMS(in, NULL, 0, NULL);
6208 			break;
6209 		case ENCODING_SMIME:
6210 			cms = SMIME_read_CMS(in, &datain);
6211 			break;
6212 		default:
6213 			zend_argument_value_error(5, "must be an OPENSSL_ENCODING_* constant");
6214 			goto clean_exit;
6215 	}
6216 
6217 	if (cms == NULL) {
6218 		php_openssl_store_errors();
6219 		goto clean_exit;
6220 	}
6221 	if (CMS_decrypt(cms, key, cert, NULL, out, 0)) {
6222 		RETVAL_TRUE;
6223 	} else {
6224 		php_openssl_store_errors();
6225 	}
6226 clean_exit:
6227 	if (cms) {
6228 		CMS_ContentInfo_free(cms);
6229 	}
6230 	BIO_free(datain);
6231 	BIO_free(in);
6232 	BIO_free(out);
6233 	if (cert && free_recipcert) {
6234 		X509_free(cert);
6235 	}
6236 	EVP_PKEY_free(key);
6237 }
6238 /* }}} */
6239 
6240 /* }}} */
6241 
6242 
6243 
6244 /* {{{ Encrypts data with private key */
PHP_FUNCTION(openssl_private_encrypt)6245 PHP_FUNCTION(openssl_private_encrypt)
6246 {
6247 	zval *key, *crypted;
6248 	EVP_PKEY *pkey;
6249 	int cryptedlen;
6250 	zend_string *cryptedbuf = NULL;
6251 	int successful = 0;
6252 	char * data;
6253 	size_t data_len;
6254 	zend_long padding = RSA_PKCS1_PADDING;
6255 
6256 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
6257 		RETURN_THROWS();
6258 	}
6259 
6260 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
6261 
6262 	RETVAL_FALSE;
6263 
6264 	pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3);
6265 
6266 	if (pkey == NULL) {
6267 		if (!EG(exception)) {
6268 			php_error_docref(NULL, E_WARNING, "key param is not a valid private key");
6269 		}
6270 		RETURN_FALSE;
6271 	}
6272 
6273 	cryptedlen = EVP_PKEY_size(pkey);
6274 	cryptedbuf = zend_string_alloc(cryptedlen, 0);
6275 
6276 	switch (EVP_PKEY_id(pkey)) {
6277 		case EVP_PKEY_RSA:
6278 		case EVP_PKEY_RSA2:
6279 			successful = (RSA_private_encrypt((int)data_len,
6280 						(unsigned char *)data,
6281 						(unsigned char *)ZSTR_VAL(cryptedbuf),
6282 						EVP_PKEY_get0_RSA(pkey),
6283 						(int)padding) == cryptedlen);
6284 			break;
6285 		default:
6286 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
6287 	}
6288 
6289 	if (successful) {
6290 		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
6291 		ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, cryptedbuf);
6292 		cryptedbuf = NULL;
6293 		RETVAL_TRUE;
6294 	} else {
6295 		php_openssl_store_errors();
6296 	}
6297 	if (cryptedbuf) {
6298 		zend_string_release_ex(cryptedbuf, 0);
6299 	}
6300 	EVP_PKEY_free(pkey);
6301 }
6302 /* }}} */
6303 
6304 /* {{{ Decrypts data with private key */
PHP_FUNCTION(openssl_private_decrypt)6305 PHP_FUNCTION(openssl_private_decrypt)
6306 {
6307 	zval *key, *crypted;
6308 	EVP_PKEY *pkey;
6309 	int cryptedlen;
6310 	zend_string *cryptedbuf = NULL;
6311 	unsigned char *crypttemp;
6312 	int successful = 0;
6313 	zend_long padding = RSA_PKCS1_PADDING;
6314 	char * data;
6315 	size_t data_len;
6316 
6317 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
6318 		RETURN_THROWS();
6319 	}
6320 
6321 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
6322 
6323 	RETVAL_FALSE;
6324 
6325 	pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3);
6326 	if (pkey == NULL) {
6327 		if (!EG(exception)) {
6328 			php_error_docref(NULL, E_WARNING, "key parameter is not a valid private key");
6329 		}
6330 		RETURN_FALSE;
6331 	}
6332 
6333 	cryptedlen = EVP_PKEY_size(pkey);
6334 	crypttemp = emalloc(cryptedlen + 1);
6335 
6336 	switch (EVP_PKEY_id(pkey)) {
6337 		case EVP_PKEY_RSA:
6338 		case EVP_PKEY_RSA2:
6339 			cryptedlen = RSA_private_decrypt((int)data_len,
6340 					(unsigned char *)data,
6341 					crypttemp,
6342 					EVP_PKEY_get0_RSA(pkey),
6343 					(int)padding);
6344 			if (cryptedlen != -1) {
6345 				cryptedbuf = zend_string_alloc(cryptedlen, 0);
6346 				memcpy(ZSTR_VAL(cryptedbuf), crypttemp, cryptedlen);
6347 				successful = 1;
6348 			}
6349 			break;
6350 		default:
6351 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
6352 	}
6353 
6354 	efree(crypttemp);
6355 
6356 	if (successful) {
6357 		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
6358 		ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, cryptedbuf);
6359 		cryptedbuf = NULL;
6360 		RETVAL_TRUE;
6361 	} else {
6362 		php_openssl_store_errors();
6363 	}
6364 
6365 	EVP_PKEY_free(pkey);
6366 	if (cryptedbuf) {
6367 		zend_string_release_ex(cryptedbuf, 0);
6368 	}
6369 }
6370 /* }}} */
6371 
6372 /* {{{ Encrypts data with public key */
PHP_FUNCTION(openssl_public_encrypt)6373 PHP_FUNCTION(openssl_public_encrypt)
6374 {
6375 	zval *key, *crypted;
6376 	EVP_PKEY *pkey;
6377 	int cryptedlen;
6378 	zend_string *cryptedbuf;
6379 	int successful = 0;
6380 	zend_long padding = RSA_PKCS1_PADDING;
6381 	char * data;
6382 	size_t data_len;
6383 
6384 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
6385 		RETURN_THROWS();
6386 	}
6387 
6388 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
6389 
6390 	RETVAL_FALSE;
6391 
6392 	pkey = php_openssl_pkey_from_zval(key, 1, NULL, 0, 3);
6393 	if (pkey == NULL) {
6394 		if (!EG(exception)) {
6395 			php_error_docref(NULL, E_WARNING, "key parameter is not a valid public key");
6396 		}
6397 		RETURN_FALSE;
6398 	}
6399 
6400 	cryptedlen = EVP_PKEY_size(pkey);
6401 	cryptedbuf = zend_string_alloc(cryptedlen, 0);
6402 
6403 	switch (EVP_PKEY_id(pkey)) {
6404 		case EVP_PKEY_RSA:
6405 		case EVP_PKEY_RSA2:
6406 			successful = (RSA_public_encrypt((int)data_len,
6407 						(unsigned char *)data,
6408 						(unsigned char *)ZSTR_VAL(cryptedbuf),
6409 						EVP_PKEY_get0_RSA(pkey),
6410 						(int)padding) == cryptedlen);
6411 			break;
6412 		default:
6413 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
6414 
6415 	}
6416 
6417 	if (successful) {
6418 		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
6419 		ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, cryptedbuf);
6420 		cryptedbuf = NULL;
6421 		RETVAL_TRUE;
6422 	} else {
6423 		php_openssl_store_errors();
6424 	}
6425 	EVP_PKEY_free(pkey);
6426 	if (cryptedbuf) {
6427 		zend_string_release_ex(cryptedbuf, 0);
6428 	}
6429 }
6430 /* }}} */
6431 
6432 /* {{{ Decrypts data with public key */
PHP_FUNCTION(openssl_public_decrypt)6433 PHP_FUNCTION(openssl_public_decrypt)
6434 {
6435 	zval *key, *crypted;
6436 	EVP_PKEY *pkey;
6437 	int cryptedlen;
6438 	zend_string *cryptedbuf = NULL;
6439 	unsigned char *crypttemp;
6440 	int successful = 0;
6441 	zend_long padding = RSA_PKCS1_PADDING;
6442 	char * data;
6443 	size_t data_len;
6444 
6445 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
6446 		RETURN_THROWS();
6447 	}
6448 
6449 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
6450 
6451 	RETVAL_FALSE;
6452 
6453 	pkey = php_openssl_pkey_from_zval(key, 1, NULL, 0, 3);
6454 	if (pkey == NULL) {
6455 		if (!EG(exception)) {
6456 			php_error_docref(NULL, E_WARNING, "key parameter is not a valid public key");
6457 		}
6458 		RETURN_FALSE;
6459 	}
6460 
6461 	cryptedlen = EVP_PKEY_size(pkey);
6462 	crypttemp = emalloc(cryptedlen + 1);
6463 
6464 	switch (EVP_PKEY_id(pkey)) {
6465 		case EVP_PKEY_RSA:
6466 		case EVP_PKEY_RSA2:
6467 			cryptedlen = RSA_public_decrypt((int)data_len,
6468 					(unsigned char *)data,
6469 					crypttemp,
6470 					EVP_PKEY_get0_RSA(pkey),
6471 					(int)padding);
6472 			if (cryptedlen != -1) {
6473 				cryptedbuf = zend_string_alloc(cryptedlen, 0);
6474 				memcpy(ZSTR_VAL(cryptedbuf), crypttemp, cryptedlen);
6475 				successful = 1;
6476 			}
6477 			break;
6478 
6479 		default:
6480 			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
6481 
6482 	}
6483 
6484 	efree(crypttemp);
6485 
6486 	if (successful) {
6487 		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
6488 		ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, cryptedbuf);
6489 		cryptedbuf = NULL;
6490 		RETVAL_TRUE;
6491 	} else {
6492 		php_openssl_store_errors();
6493 	}
6494 
6495 	if (cryptedbuf) {
6496 		zend_string_release_ex(cryptedbuf, 0);
6497 	}
6498 	EVP_PKEY_free(pkey);
6499 }
6500 /* }}} */
6501 
6502 /* {{{ Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */
PHP_FUNCTION(openssl_error_string)6503 PHP_FUNCTION(openssl_error_string)
6504 {
6505 	char buf[256];
6506 	unsigned long val;
6507 
6508 	if (zend_parse_parameters_none() == FAILURE) {
6509 		RETURN_THROWS();
6510 	}
6511 
6512 	php_openssl_store_errors();
6513 
6514 	if (OPENSSL_G(errors) == NULL || OPENSSL_G(errors)->top == OPENSSL_G(errors)->bottom) {
6515 		RETURN_FALSE;
6516 	}
6517 
6518 	OPENSSL_G(errors)->bottom = (OPENSSL_G(errors)->bottom + 1) % ERR_NUM_ERRORS;
6519 	val = OPENSSL_G(errors)->buffer[OPENSSL_G(errors)->bottom];
6520 
6521 	if (val) {
6522 		ERR_error_string_n(val, buf, 256);
6523 		RETURN_STRING(buf);
6524 	} else {
6525 		RETURN_FALSE;
6526 	}
6527 }
6528 /* }}} */
6529 
6530 /* {{{ Signs data */
PHP_FUNCTION(openssl_sign)6531 PHP_FUNCTION(openssl_sign)
6532 {
6533 	zval *key, *signature;
6534 	EVP_PKEY *pkey;
6535 	unsigned int siglen;
6536 	zend_string *sigbuf;
6537 	char * data;
6538 	size_t data_len;
6539 	EVP_MD_CTX *md_ctx;
6540 	zend_string *method_str = NULL;
6541 	zend_long method_long = OPENSSL_ALGO_SHA1;
6542 	const EVP_MD *mdtype;
6543 
6544 	ZEND_PARSE_PARAMETERS_START(3, 4)
6545 		Z_PARAM_STRING(data, data_len)
6546 		Z_PARAM_ZVAL(signature)
6547 		Z_PARAM_ZVAL(key)
6548 		Z_PARAM_OPTIONAL
6549 		Z_PARAM_STR_OR_LONG(method_str, method_long)
6550 	ZEND_PARSE_PARAMETERS_END();
6551 
6552 	pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3);
6553 	if (pkey == NULL) {
6554 		if (!EG(exception)) {
6555 			php_error_docref(NULL, E_WARNING, "Supplied key param cannot be coerced into a private key");
6556 		}
6557 		RETURN_FALSE;
6558 	}
6559 
6560 	if (method_str) {
6561 		mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str));
6562 	} else {
6563 		mdtype = php_openssl_get_evp_md_from_algo(method_long);
6564 	}
6565 	if (!mdtype) {
6566 		php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
6567 		RETURN_FALSE;
6568 	}
6569 
6570 	siglen = EVP_PKEY_size(pkey);
6571 	sigbuf = zend_string_alloc(siglen, 0);
6572 
6573 	md_ctx = EVP_MD_CTX_create();
6574 	if (md_ctx != NULL &&
6575 			EVP_SignInit(md_ctx, mdtype) &&
6576 			EVP_SignUpdate(md_ctx, data, data_len) &&
6577 			EVP_SignFinal(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, pkey)) {
6578 		ZSTR_VAL(sigbuf)[siglen] = '\0';
6579 		ZSTR_LEN(sigbuf) = siglen;
6580 		ZEND_TRY_ASSIGN_REF_NEW_STR(signature, sigbuf);
6581 		RETVAL_TRUE;
6582 	} else {
6583 		php_openssl_store_errors();
6584 		efree(sigbuf);
6585 		RETVAL_FALSE;
6586 	}
6587 	EVP_MD_CTX_destroy(md_ctx);
6588 	EVP_PKEY_free(pkey);
6589 }
6590 /* }}} */
6591 
6592 /* {{{ Verifys data */
PHP_FUNCTION(openssl_verify)6593 PHP_FUNCTION(openssl_verify)
6594 {
6595 	zval *key;
6596 	EVP_PKEY *pkey;
6597 	int err = 0;
6598 	EVP_MD_CTX *md_ctx;
6599 	const EVP_MD *mdtype;
6600 	char * data;
6601 	size_t data_len;
6602 	char * signature;
6603 	size_t signature_len;
6604 	zend_string *method_str = NULL;
6605 	zend_long method_long = OPENSSL_ALGO_SHA1;
6606 
6607 	ZEND_PARSE_PARAMETERS_START(3, 4)
6608 		Z_PARAM_STRING(data, data_len)
6609 		Z_PARAM_STRING(signature, signature_len)
6610 		Z_PARAM_ZVAL(key)
6611 		Z_PARAM_OPTIONAL
6612 		Z_PARAM_STR_OR_LONG(method_str, method_long)
6613 	ZEND_PARSE_PARAMETERS_END();
6614 
6615 	PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature, 2);
6616 
6617 	if (method_str) {
6618 		mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str));
6619 	} else {
6620 		mdtype = php_openssl_get_evp_md_from_algo(method_long);
6621 	}
6622 	if (!mdtype) {
6623 		php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
6624 		RETURN_FALSE;
6625 	}
6626 
6627 	pkey = php_openssl_pkey_from_zval(key, 1, NULL, 0, 3);
6628 	if (pkey == NULL) {
6629 		if (!EG(exception)) {
6630 			php_error_docref(NULL, E_WARNING, "Supplied key param cannot be coerced into a public key");
6631 		}
6632 		RETURN_FALSE;
6633 	}
6634 
6635 	md_ctx = EVP_MD_CTX_create();
6636 	if (md_ctx == NULL ||
6637 			!EVP_VerifyInit (md_ctx, mdtype) ||
6638 			!EVP_VerifyUpdate (md_ctx, data, data_len) ||
6639 			(err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey)) < 0) {
6640 		php_openssl_store_errors();
6641 	}
6642 	EVP_MD_CTX_destroy(md_ctx);
6643 	EVP_PKEY_free(pkey);
6644 	RETURN_LONG(err);
6645 }
6646 /* }}} */
6647 
6648 /* {{{ Seals data */
PHP_FUNCTION(openssl_seal)6649 PHP_FUNCTION(openssl_seal)
6650 {
6651 	zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL;
6652 	HashTable *pubkeysht;
6653 	EVP_PKEY **pkeys;
6654 	int i, len1, len2, *eksl, nkeys, iv_len;
6655 	unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks;
6656 	char * data;
6657 	size_t data_len;
6658 	char *method;
6659 	size_t method_len;
6660 	const EVP_CIPHER *cipher;
6661 	EVP_CIPHER_CTX *ctx;
6662 
6663 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z", &data, &data_len,
6664 				&sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) {
6665 		RETURN_THROWS();
6666 	}
6667 
6668 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
6669 
6670 	pubkeysht = Z_ARRVAL_P(pubkeys);
6671 	nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
6672 	if (!nkeys) {
6673 		zend_argument_value_error(4, "cannot be empty");
6674 		RETURN_THROWS();
6675 	}
6676 
6677 	cipher = EVP_get_cipherbyname(method);
6678 	if (!cipher) {
6679 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
6680 		RETURN_FALSE;
6681 	}
6682 
6683 	iv_len = EVP_CIPHER_iv_length(cipher);
6684 	if (!iv && iv_len > 0) {
6685 		zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm");
6686 		RETURN_THROWS();
6687 	}
6688 
6689 	pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
6690 	eksl = safe_emalloc(nkeys, sizeof(*eksl), 0);
6691 	eks = safe_emalloc(nkeys, sizeof(*eks), 0);
6692 	memset(eks, 0, sizeof(*eks) * nkeys);
6693 	memset(pkeys, 0, sizeof(*pkeys) * nkeys);
6694 
6695 	/* get the public keys we are using to seal this data */
6696 	i = 0;
6697 	ZEND_HASH_FOREACH_VAL(pubkeysht, pubkey) {
6698 		pkeys[i] = php_openssl_pkey_from_zval(pubkey, 1, NULL, 0, 4);
6699 		if (pkeys[i] == NULL) {
6700 			if (!EG(exception)) {
6701 				php_error_docref(NULL, E_WARNING, "Not a public key (%dth member of pubkeys)", i+1);
6702 			}
6703 			RETVAL_FALSE;
6704 			goto clean_exit;
6705 		}
6706 		eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
6707 		i++;
6708 	} ZEND_HASH_FOREACH_END();
6709 
6710 	ctx = EVP_CIPHER_CTX_new();
6711 	if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) {
6712 		EVP_CIPHER_CTX_free(ctx);
6713 		php_openssl_store_errors();
6714 		RETVAL_FALSE;
6715 		goto clean_exit;
6716 	}
6717 
6718 	/* allocate one byte extra to make room for \0 */
6719 	buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(ctx));
6720 	EVP_CIPHER_CTX_reset(ctx);
6721 
6722 	if (EVP_SealInit(ctx, cipher, eks, eksl, &iv_buf[0], pkeys, nkeys) <= 0 ||
6723 			!EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
6724 			!EVP_SealFinal(ctx, buf + len1, &len2)) {
6725 		efree(buf);
6726 		EVP_CIPHER_CTX_free(ctx);
6727 		php_openssl_store_errors();
6728 		RETVAL_FALSE;
6729 		goto clean_exit;
6730 	}
6731 
6732 	if (len1 + len2 > 0) {
6733 		ZEND_TRY_ASSIGN_REF_NEW_STR(sealdata, zend_string_init((char*)buf, len1 + len2, 0));
6734 		efree(buf);
6735 
6736 		ekeys = zend_try_array_init(ekeys);
6737 		if (!ekeys) {
6738 			EVP_CIPHER_CTX_free(ctx);
6739 			goto clean_exit;
6740 		}
6741 
6742 		for (i=0; i<nkeys; i++) {
6743 			eks[i][eksl[i]] = '\0';
6744 			add_next_index_stringl(ekeys, (const char*)eks[i], eksl[i]);
6745 			efree(eks[i]);
6746 			eks[i] = NULL;
6747 		}
6748 
6749 		if (iv) {
6750 			iv_buf[iv_len] = '\0';
6751 			ZEND_TRY_ASSIGN_REF_NEW_STR(iv, zend_string_init((char*)iv_buf, iv_len, 0));
6752 		}
6753 	} else {
6754 		efree(buf);
6755 	}
6756 	RETVAL_LONG(len1 + len2);
6757 	EVP_CIPHER_CTX_free(ctx);
6758 
6759 clean_exit:
6760 	for (i=0; i<nkeys; i++) {
6761 		if (pkeys[i] != NULL) {
6762 			EVP_PKEY_free(pkeys[i]);
6763 		}
6764 		if (eks[i]) {
6765 			efree(eks[i]);
6766 		}
6767 	}
6768 	efree(eks);
6769 	efree(eksl);
6770 	efree(pkeys);
6771 }
6772 /* }}} */
6773 
6774 /* {{{ Opens data */
PHP_FUNCTION(openssl_open)6775 PHP_FUNCTION(openssl_open)
6776 {
6777 	zval *privkey, *opendata;
6778 	EVP_PKEY *pkey;
6779 	int len1, len2, cipher_iv_len;
6780 	unsigned char *buf, *iv_buf;
6781 	EVP_CIPHER_CTX *ctx;
6782 	char * data;
6783 	size_t data_len;
6784 	char * ekey;
6785 	size_t ekey_len;
6786 	char *method, *iv = NULL;
6787 	size_t method_len, iv_len = 0;
6788 	const EVP_CIPHER *cipher;
6789 
6790 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szszs|s!", &data, &data_len, &opendata,
6791 				&ekey, &ekey_len, &privkey, &method, &method_len, &iv, &iv_len) == FAILURE) {
6792 		RETURN_THROWS();
6793 	}
6794 
6795 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
6796 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(ekey_len, ekey, 3);
6797 	pkey = php_openssl_pkey_from_zval(privkey, 0, "", 0, 4);
6798 
6799 	if (pkey == NULL) {
6800 		if (!EG(exception)) {
6801 			php_error_docref(NULL, E_WARNING, "Unable to coerce parameter 4 into a private key");
6802 		}
6803 		RETURN_FALSE;
6804 	}
6805 
6806 	cipher = EVP_get_cipherbyname(method);
6807 	if (!cipher) {
6808 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
6809 		RETURN_FALSE;
6810 	}
6811 
6812 	cipher_iv_len = EVP_CIPHER_iv_length(cipher);
6813 	if (cipher_iv_len > 0) {
6814 		if (!iv) {
6815 			zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm");
6816 			RETURN_THROWS();
6817 		}
6818 		if ((size_t)cipher_iv_len != iv_len) {
6819 			php_error_docref(NULL, E_WARNING, "IV length is invalid");
6820 			RETURN_FALSE;
6821 		}
6822 		iv_buf = (unsigned char *)iv;
6823 	} else {
6824 		iv_buf = NULL;
6825 	}
6826 
6827 	buf = emalloc(data_len + 1);
6828 
6829 	ctx = EVP_CIPHER_CTX_new();
6830 	if (ctx != NULL && EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) &&
6831 			EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) &&
6832 			EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) {
6833 		buf[len1 + len2] = '\0';
6834 		ZEND_TRY_ASSIGN_REF_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
6835 		RETVAL_TRUE;
6836 	} else {
6837 		php_openssl_store_errors();
6838 		RETVAL_FALSE;
6839 	}
6840 
6841 	efree(buf);
6842 	EVP_PKEY_free(pkey);
6843 	EVP_CIPHER_CTX_free(ctx);
6844 }
6845 /* }}} */
6846 
php_openssl_add_method_or_alias(const OBJ_NAME * name,void * arg)6847 static void php_openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */
6848 {
6849 	add_next_index_string((zval*)arg, (char*)name->name);
6850 }
6851 /* }}} */
6852 
php_openssl_add_method(const OBJ_NAME * name,void * arg)6853 static void php_openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */
6854 {
6855 	if (name->alias == 0) {
6856 		add_next_index_string((zval*)arg, (char*)name->name);
6857 	}
6858 }
6859 /* }}} */
6860 
6861 /* {{{ Return array of available digest algorithms */
PHP_FUNCTION(openssl_get_md_methods)6862 PHP_FUNCTION(openssl_get_md_methods)
6863 {
6864 	zend_bool aliases = 0;
6865 
6866 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
6867 		RETURN_THROWS();
6868 	}
6869 	array_init(return_value);
6870 	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
6871 		aliases ? php_openssl_add_method_or_alias: php_openssl_add_method,
6872 		return_value);
6873 }
6874 /* }}} */
6875 
6876 /* {{{ Return array of available cipher algorithms */
PHP_FUNCTION(openssl_get_cipher_methods)6877 PHP_FUNCTION(openssl_get_cipher_methods)
6878 {
6879 	zend_bool aliases = 0;
6880 
6881 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
6882 		RETURN_THROWS();
6883 	}
6884 	array_init(return_value);
6885 	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
6886 		aliases ? php_openssl_add_method_or_alias: php_openssl_add_method,
6887 		return_value);
6888 }
6889 /* }}} */
6890 
6891 /* {{{ Return array of available elliptic curves */
6892 #ifdef HAVE_EVP_PKEY_EC
PHP_FUNCTION(openssl_get_curve_names)6893 PHP_FUNCTION(openssl_get_curve_names)
6894 {
6895 	EC_builtin_curve *curves = NULL;
6896 	const char *sname;
6897 	size_t i;
6898 	size_t len = EC_get_builtin_curves(NULL, 0);
6899 
6900 	if (zend_parse_parameters_none() == FAILURE) {
6901 		RETURN_THROWS();
6902 	}
6903 
6904 	curves = emalloc(sizeof(EC_builtin_curve) * len);
6905 	if (!EC_get_builtin_curves(curves, len)) {
6906 		RETURN_FALSE;
6907 	}
6908 
6909 	array_init(return_value);
6910 	for (i = 0; i < len; i++) {
6911 		sname = OBJ_nid2sn(curves[i].nid);
6912 		if (sname != NULL) {
6913 			add_next_index_string(return_value, sname);
6914 		}
6915 	}
6916 	efree(curves);
6917 }
6918 #endif
6919 /* }}} */
6920 
6921 /* {{{ Computes digest hash value for given data using given method, returns raw or binhex encoded string */
PHP_FUNCTION(openssl_digest)6922 PHP_FUNCTION(openssl_digest)
6923 {
6924 	zend_bool raw_output = 0;
6925 	char *data, *method;
6926 	size_t data_len, method_len;
6927 	const EVP_MD *mdtype;
6928 	EVP_MD_CTX *md_ctx;
6929 	unsigned int siglen;
6930 	zend_string *sigbuf;
6931 
6932 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
6933 		RETURN_THROWS();
6934 	}
6935 	mdtype = EVP_get_digestbyname(method);
6936 	if (!mdtype) {
6937 		php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
6938 		RETURN_FALSE;
6939 	}
6940 
6941 	siglen = EVP_MD_size(mdtype);
6942 	sigbuf = zend_string_alloc(siglen, 0);
6943 
6944 	md_ctx = EVP_MD_CTX_create();
6945 	if (EVP_DigestInit(md_ctx, mdtype) &&
6946 			EVP_DigestUpdate(md_ctx, (unsigned char *)data, data_len) &&
6947 			EVP_DigestFinal (md_ctx, (unsigned char *)ZSTR_VAL(sigbuf), &siglen)) {
6948 		if (raw_output) {
6949 			ZSTR_VAL(sigbuf)[siglen] = '\0';
6950 			ZSTR_LEN(sigbuf) = siglen;
6951 			RETVAL_STR(sigbuf);
6952 		} else {
6953 			int digest_str_len = siglen * 2;
6954 			zend_string *digest_str = zend_string_alloc(digest_str_len, 0);
6955 
6956 			make_digest_ex(ZSTR_VAL(digest_str), (unsigned char*)ZSTR_VAL(sigbuf), siglen);
6957 			ZSTR_VAL(digest_str)[digest_str_len] = '\0';
6958 			zend_string_release_ex(sigbuf, 0);
6959 			RETVAL_NEW_STR(digest_str);
6960 		}
6961 	} else {
6962 		php_openssl_store_errors();
6963 		zend_string_release_ex(sigbuf, 0);
6964 		RETVAL_FALSE;
6965 	}
6966 
6967 	EVP_MD_CTX_destroy(md_ctx);
6968 }
6969 /* }}} */
6970 
6971 /* Cipher mode info */
6972 struct php_openssl_cipher_mode {
6973 	zend_bool is_aead;
6974 	zend_bool is_single_run_aead;
6975 	zend_bool set_tag_length_always;
6976 	zend_bool set_tag_length_when_encrypting;
6977 	int aead_get_tag_flag;
6978 	int aead_set_tag_flag;
6979 	int aead_ivlen_flag;
6980 };
6981 
php_openssl_load_cipher_mode(struct php_openssl_cipher_mode * mode,const EVP_CIPHER * cipher_type)6982 static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type) /* {{{ */
6983 {
6984 	int cipher_mode = EVP_CIPHER_mode(cipher_type);
6985 	memset(mode, 0, sizeof(struct php_openssl_cipher_mode));
6986 	switch (cipher_mode) {
6987 #ifdef EVP_CIPH_OCB_MODE
6988 		/* Since OpenSSL 1.1, all AEAD ciphers use a common framework. We check for
6989 		 * EVP_CIPH_OCB_MODE, because LibreSSL does not support it. */
6990 		case EVP_CIPH_GCM_MODE:
6991 		case EVP_CIPH_OCB_MODE:
6992 		case EVP_CIPH_CCM_MODE:
6993 			mode->is_aead = 1;
6994 			/* For OCB mode, explicitly set the tag length even when decrypting,
6995 			 * see https://github.com/openssl/openssl/issues/8331. */
6996 			mode->set_tag_length_always = cipher_mode == EVP_CIPH_OCB_MODE;
6997 			mode->set_tag_length_when_encrypting = cipher_mode == EVP_CIPH_CCM_MODE;
6998 			mode->is_single_run_aead = cipher_mode == EVP_CIPH_CCM_MODE;
6999 			mode->aead_get_tag_flag = EVP_CTRL_AEAD_GET_TAG;
7000 			mode->aead_set_tag_flag = EVP_CTRL_AEAD_SET_TAG;
7001 			mode->aead_ivlen_flag = EVP_CTRL_AEAD_SET_IVLEN;
7002 			break;
7003 #else
7004 # ifdef EVP_CIPH_GCM_MODE
7005 		case EVP_CIPH_GCM_MODE:
7006 			mode->is_aead = 1;
7007 			mode->aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG;
7008 			mode->aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG;
7009 			mode->aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN;
7010 			break;
7011 # endif
7012 # ifdef EVP_CIPH_CCM_MODE
7013 		case EVP_CIPH_CCM_MODE:
7014 			mode->is_aead = 1;
7015 			mode->is_single_run_aead = 1;
7016 			mode->set_tag_length_when_encrypting = 1;
7017 			mode->aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG;
7018 			mode->aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG;
7019 			mode->aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN;
7020 			break;
7021 # endif
7022 #endif
7023 	}
7024 }
7025 /* }}} */
7026 
php_openssl_validate_iv(const char ** piv,size_t * piv_len,size_t iv_required_len,zend_bool * free_iv,EVP_CIPHER_CTX * cipher_ctx,struct php_openssl_cipher_mode * mode)7027 static int php_openssl_validate_iv(const char **piv, size_t *piv_len, size_t iv_required_len,
7028 		zend_bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */
7029 {
7030 	char *iv_new;
7031 
7032 	if (mode->is_aead) {
7033 		if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_ivlen_flag, *piv_len, NULL) != 1) {
7034 			php_error_docref(NULL, E_WARNING, "Setting of IV length for AEAD mode failed");
7035 			return FAILURE;
7036 		}
7037 		return SUCCESS;
7038 	}
7039 
7040 	/* Best case scenario, user behaved */
7041 	if (*piv_len == iv_required_len) {
7042 		return SUCCESS;
7043 	}
7044 
7045 	iv_new = ecalloc(1, iv_required_len + 1);
7046 
7047 	if (*piv_len == 0) {
7048 		/* BC behavior */
7049 		*piv_len = iv_required_len;
7050 		*piv = iv_new;
7051 		*free_iv = 1;
7052 		return SUCCESS;
7053 
7054 	}
7055 
7056 	if (*piv_len < iv_required_len) {
7057 		php_error_docref(NULL, E_WARNING,
7058 				"IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0",
7059 				*piv_len, iv_required_len);
7060 		memcpy(iv_new, *piv, *piv_len);
7061 		*piv_len = iv_required_len;
7062 		*piv = iv_new;
7063 		*free_iv = 1;
7064 		return SUCCESS;
7065 	}
7066 
7067 	php_error_docref(NULL, E_WARNING,
7068 			"IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating",
7069 			*piv_len, iv_required_len);
7070 	memcpy(iv_new, *piv, iv_required_len);
7071 	*piv_len = iv_required_len;
7072 	*piv = iv_new;
7073 	*free_iv = 1;
7074 	return SUCCESS;
7075 
7076 }
7077 /* }}} */
7078 
php_openssl_cipher_init(const EVP_CIPHER * cipher_type,EVP_CIPHER_CTX * cipher_ctx,struct php_openssl_cipher_mode * mode,const char ** ppassword,size_t * ppassword_len,zend_bool * free_password,const char ** piv,size_t * piv_len,zend_bool * free_iv,const char * tag,int tag_len,zend_long options,int enc)7079 static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
7080 		EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
7081 		const char **ppassword, size_t *ppassword_len, zend_bool *free_password,
7082 		const char **piv, size_t *piv_len, zend_bool *free_iv,
7083 		const char *tag, int tag_len, zend_long options, int enc)  /* {{{ */
7084 {
7085 	unsigned char *key;
7086 	int key_len, password_len;
7087 	size_t max_iv_len;
7088 
7089 	*free_password = 0;
7090 
7091 	max_iv_len = EVP_CIPHER_iv_length(cipher_type);
7092 	if (enc && *piv_len == 0 && max_iv_len > 0 && !mode->is_aead) {
7093 		php_error_docref(NULL, E_WARNING,
7094 				"Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
7095 	}
7096 
7097 	if (!EVP_CipherInit_ex(cipher_ctx, cipher_type, NULL, NULL, NULL, enc)) {
7098 		php_openssl_store_errors();
7099 		return FAILURE;
7100 	}
7101 	if (php_openssl_validate_iv(piv, piv_len, max_iv_len, free_iv, cipher_ctx, mode) == FAILURE) {
7102 		return FAILURE;
7103 	}
7104 	if (mode->set_tag_length_always || (enc && mode->set_tag_length_when_encrypting)) {
7105 		if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL)) {
7106 			php_error_docref(NULL, E_WARNING, "Setting tag length for AEAD cipher failed");
7107 			return FAILURE;
7108 		}
7109 	}
7110 	if (!enc && tag && tag_len > 0) {
7111 		if (!mode->is_aead) {
7112 			php_error_docref(NULL, E_WARNING, "The tag cannot be used because the cipher algorithm does not support AEAD");
7113 		} else if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, (unsigned char *) tag)) {
7114 			php_error_docref(NULL, E_WARNING, "Setting tag for AEAD cipher decryption failed");
7115 			return FAILURE;
7116 		}
7117 	}
7118 	/* check and set key */
7119 	password_len = (int) *ppassword_len;
7120 	key_len = EVP_CIPHER_key_length(cipher_type);
7121 	if (key_len > password_len) {
7122 		if ((OPENSSL_DONT_ZERO_PAD_KEY & options) && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
7123 			php_openssl_store_errors();
7124 			php_error_docref(NULL, E_WARNING, "Key length cannot be set for the cipher algorithm");
7125 			return FAILURE;
7126 		}
7127 		key = emalloc(key_len);
7128 		memset(key, 0, key_len);
7129 		memcpy(key, *ppassword, password_len);
7130 		*ppassword = (char *) key;
7131 		*ppassword_len = key_len;
7132 		*free_password = 1;
7133 	} else {
7134 		if (password_len > key_len && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
7135 			php_openssl_store_errors();
7136 		}
7137 		key = (unsigned char*)*ppassword;
7138 	}
7139 
7140 	if (!EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)*piv, enc)) {
7141 		php_openssl_store_errors();
7142 		return FAILURE;
7143 	}
7144 	if (options & OPENSSL_ZERO_PADDING) {
7145 		EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
7146 	}
7147 
7148 	return SUCCESS;
7149 }
7150 /* }}} */
7151 
php_openssl_cipher_update(const EVP_CIPHER * cipher_type,EVP_CIPHER_CTX * cipher_ctx,struct php_openssl_cipher_mode * mode,zend_string ** poutbuf,int * poutlen,const char * data,size_t data_len,const char * aad,size_t aad_len,int enc)7152 static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
7153 		EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
7154 		zend_string **poutbuf, int *poutlen, const char *data, size_t data_len,
7155 		const char *aad, size_t aad_len, int enc)  /* {{{ */
7156 {
7157 	int i = 0;
7158 
7159 	if (mode->is_single_run_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
7160 		php_openssl_store_errors();
7161 		php_error_docref(NULL, E_WARNING, "Setting of data length failed");
7162 		return FAILURE;
7163 	}
7164 
7165 	if (mode->is_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, (const unsigned char *) aad, (int) aad_len)) {
7166 		php_openssl_store_errors();
7167 		php_error_docref(NULL, E_WARNING, "Setting of additional application data failed");
7168 		return FAILURE;
7169 	}
7170 
7171 	*poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
7172 
7173 	if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
7174 					&i, (const unsigned char *)data, (int)data_len)) {
7175 		/* we don't show warning when we fail but if we ever do, then it should look like this:
7176 		if (mode->is_single_run_aead && !enc) {
7177 			php_error_docref(NULL, E_WARNING, "Tag verifycation failed");
7178 		} else {
7179 			php_error_docref(NULL, E_WARNING, enc ? "Encryption failed" : "Decryption failed");
7180 		}
7181 		*/
7182 		php_openssl_store_errors();
7183 		zend_string_release_ex(*poutbuf, 0);
7184 		return FAILURE;
7185 	}
7186 
7187 	*poutlen = i;
7188 
7189 	return SUCCESS;
7190 }
7191 /* }}} */
7192 
7193 
php_openssl_encrypt(const char * data,size_t data_len,const char * method,size_t method_len,const char * password,size_t password_len,zend_long options,const char * iv,size_t iv_len,zval * tag,zend_long tag_len,const char * aad,size_t aad_len)7194 PHP_OPENSSL_API zend_string* php_openssl_encrypt(
7195 	const char *data, size_t data_len,
7196 	const char *method, size_t method_len,
7197 	const char *password, size_t password_len,
7198 	zend_long options,
7199 	const char *iv, size_t iv_len,
7200 	zval *tag, zend_long tag_len,
7201 	const char *aad, size_t aad_len)
7202 {
7203 	const EVP_CIPHER *cipher_type;
7204 	EVP_CIPHER_CTX *cipher_ctx;
7205 	struct php_openssl_cipher_mode mode;
7206 	int i = 0, outlen;
7207 	zend_bool free_iv = 0, free_password = 0;
7208 	zend_string *outbuf = NULL;
7209 
7210 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(data_len, data);
7211 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(password_len, password);
7212 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(aad_len, aad);
7213 	PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(tag_len, tag_len);
7214 
7215 
7216 	cipher_type = EVP_get_cipherbyname(method);
7217 	if (!cipher_type) {
7218 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7219 		return NULL;
7220 	}
7221 
7222 	cipher_ctx = EVP_CIPHER_CTX_new();
7223 	if (!cipher_ctx) {
7224 		php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
7225 		return NULL;
7226 	}
7227 
7228 	php_openssl_load_cipher_mode(&mode, cipher_type);
7229 
7230 	if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
7231 				&password, &password_len, &free_password,
7232 				&iv, &iv_len, &free_iv, NULL, tag_len, options, 1) == FAILURE ||
7233 			php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
7234 				data, data_len, aad, aad_len, 1) == FAILURE) {
7235 		outbuf = NULL;
7236 	} else if (EVP_EncryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
7237 		outlen += i;
7238 		if (options & OPENSSL_RAW_DATA) {
7239 			ZSTR_VAL(outbuf)[outlen] = '\0';
7240 			ZSTR_LEN(outbuf) = outlen;
7241 		} else {
7242 			zend_string *base64_str;
7243 
7244 			base64_str = php_base64_encode((unsigned char*)ZSTR_VAL(outbuf), outlen);
7245 			zend_string_release_ex(outbuf, 0);
7246 			outbuf = base64_str;
7247 		}
7248 		if (mode.is_aead && tag) {
7249 			zend_string *tag_str = zend_string_alloc(tag_len, 0);
7250 
7251 			if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode.aead_get_tag_flag, tag_len, ZSTR_VAL(tag_str)) == 1) {
7252 				ZSTR_VAL(tag_str)[tag_len] = '\0';
7253 				ZSTR_LEN(tag_str) = tag_len;
7254 				ZEND_TRY_ASSIGN_REF_NEW_STR(tag, tag_str);
7255 			} else {
7256 				php_error_docref(NULL, E_WARNING, "Retrieving verification tag failed");
7257 				zend_string_release_ex(tag_str, 0);
7258 				zend_string_release_ex(outbuf, 0);
7259 				outbuf = NULL;
7260 			}
7261 		} else if (tag) {
7262 			ZEND_TRY_ASSIGN_REF_NULL(tag);
7263 		} else if (mode.is_aead) {
7264 			php_error_docref(NULL, E_WARNING, "A tag should be provided when using AEAD mode");
7265 			zend_string_release_ex(outbuf, 0);
7266 			outbuf = NULL;
7267 		}
7268 	} else {
7269 		php_openssl_store_errors();
7270 		zend_string_release_ex(outbuf, 0);
7271 		outbuf = NULL;
7272 	}
7273 
7274 	if (free_password) {
7275 		efree((void *) password);
7276 	}
7277 	if (free_iv) {
7278 		efree((void *) iv);
7279 	}
7280 	EVP_CIPHER_CTX_reset(cipher_ctx);
7281 	EVP_CIPHER_CTX_free(cipher_ctx);
7282 	return outbuf;
7283 }
7284 
7285 /* {{{ Encrypts given data with given method and key, returns raw or base64 encoded string */
PHP_FUNCTION(openssl_encrypt)7286 PHP_FUNCTION(openssl_encrypt)
7287 {
7288 	zend_long options = 0, tag_len = 16;
7289 	char *data, *method, *password, *iv = "", *aad = "";
7290 	size_t data_len, method_len, password_len, iv_len = 0, aad_len = 0;
7291 	zend_string *ret;
7292 	zval *tag = NULL;
7293 
7294 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lszsl", &data, &data_len, &method, &method_len,
7295 					&password, &password_len, &options, &iv, &iv_len, &tag, &aad, &aad_len, &tag_len) == FAILURE) {
7296 		RETURN_THROWS();
7297 	}
7298 
7299 	if ((ret = php_openssl_encrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
7300 		RETVAL_STR(ret);
7301 	} else {
7302 		RETVAL_FALSE;
7303 	}
7304 }
7305 /* }}} */
7306 
php_openssl_decrypt(const char * data,size_t data_len,const char * method,size_t method_len,const char * password,size_t password_len,zend_long options,const char * iv,size_t iv_len,const char * tag,zend_long tag_len,const char * aad,size_t aad_len)7307 PHP_OPENSSL_API zend_string* php_openssl_decrypt(
7308 	const char *data, size_t data_len,
7309 	const char *method, size_t method_len,
7310 	const char *password, size_t password_len,
7311 	zend_long options,
7312 	const char *iv, size_t iv_len,
7313 	const char *tag, zend_long tag_len,
7314 	const char *aad, size_t aad_len)
7315 {
7316 	const EVP_CIPHER *cipher_type;
7317 	EVP_CIPHER_CTX *cipher_ctx;
7318 	struct php_openssl_cipher_mode mode;
7319 	int i = 0, outlen;
7320 	zend_string *base64_str = NULL;
7321 	zend_bool free_iv = 0, free_password = 0;
7322 	zend_string *outbuf = NULL;
7323 
7324 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(data_len, data);
7325 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(password_len, password);
7326 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(aad_len, aad);
7327 	PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(tag_len, tag);
7328 
7329 
7330 	cipher_type = EVP_get_cipherbyname(method);
7331 	if (!cipher_type) {
7332 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7333 		return NULL;
7334 	}
7335 
7336 	cipher_ctx = EVP_CIPHER_CTX_new();
7337 	if (!cipher_ctx) {
7338 		php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
7339 		return NULL;
7340 	}
7341 
7342 	php_openssl_load_cipher_mode(&mode, cipher_type);
7343 
7344 	if (!(options & OPENSSL_RAW_DATA)) {
7345 		base64_str = php_base64_decode((unsigned char*)data, data_len);
7346 		if (!base64_str) {
7347 			php_error_docref(NULL, E_WARNING, "Failed to base64 decode the input");
7348 			EVP_CIPHER_CTX_free(cipher_ctx);
7349 			return NULL;
7350 		}
7351 		data_len = ZSTR_LEN(base64_str);
7352 		data = ZSTR_VAL(base64_str);
7353 	}
7354 
7355 	if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
7356 				&password, &password_len, &free_password,
7357 				&iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE ||
7358 			php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
7359 				data, data_len, aad, aad_len, 0) == FAILURE) {
7360 		outbuf = NULL;
7361 	} else if (mode.is_single_run_aead ||
7362 			EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
7363 		outlen += i;
7364 		ZSTR_VAL(outbuf)[outlen] = '\0';
7365 		ZSTR_LEN(outbuf) = outlen;
7366 	} else {
7367 		php_openssl_store_errors();
7368 		zend_string_release_ex(outbuf, 0);
7369 		outbuf = NULL;
7370 	}
7371 
7372 	if (free_password) {
7373 		efree((void *) password);
7374 	}
7375 	if (free_iv) {
7376 		efree((void *) iv);
7377 	}
7378 	if (base64_str) {
7379 		zend_string_release_ex(base64_str, 0);
7380 	}
7381 	EVP_CIPHER_CTX_reset(cipher_ctx);
7382 	EVP_CIPHER_CTX_free(cipher_ctx);
7383 	return outbuf;
7384 }
7385 
7386 /* {{{ Takes raw or base64 encoded string and decrypts it using given method and key */
PHP_FUNCTION(openssl_decrypt)7387 PHP_FUNCTION(openssl_decrypt)
7388 {
7389 	zend_long options = 0;
7390 	char *data, *method, *password, *iv = "", *tag = NULL, *aad = "";
7391 	size_t data_len, method_len, password_len, iv_len = 0, tag_len = 0, aad_len = 0;
7392 	zend_string *ret;
7393 
7394 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsss", &data, &data_len, &method, &method_len,
7395 					&password, &password_len, &options, &iv, &iv_len, &tag, &tag_len, &aad, &aad_len) == FAILURE) {
7396 		RETURN_THROWS();
7397 	}
7398 
7399 	if (!method_len) {
7400 		zend_argument_value_error(2, "cannot be empty");
7401 		RETURN_THROWS();
7402 	}
7403 
7404 	if ((ret = php_openssl_decrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
7405 		RETVAL_STR(ret);
7406 	} else {
7407 		RETVAL_FALSE;
7408 	}
7409 }
7410 /* }}} */
7411 
php_openssl_cipher_iv_length(const char * method)7412 PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method)
7413 {
7414 	const EVP_CIPHER *cipher_type;
7415 
7416 	cipher_type = EVP_get_cipherbyname(method);
7417 	if (!cipher_type) {
7418 		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7419 		return -1;
7420 	}
7421 
7422 	return EVP_CIPHER_iv_length(cipher_type);
7423 }
7424 
7425 /* {{{ */
PHP_FUNCTION(openssl_cipher_iv_length)7426 PHP_FUNCTION(openssl_cipher_iv_length)
7427 {
7428 	char *method;
7429 	size_t method_len;
7430 	zend_long ret;
7431 
7432 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &method, &method_len) == FAILURE) {
7433 		RETURN_THROWS();
7434 	}
7435 
7436 	if (!method_len) {
7437 		zend_argument_value_error(1, "cannot be empty");
7438 		RETURN_THROWS();
7439 	}
7440 
7441 	/* Warning is emitted in php_openssl_cipher_iv_length */
7442 	if ((ret = php_openssl_cipher_iv_length(method)) == -1) {
7443 		RETURN_FALSE;
7444 	}
7445 
7446 	RETURN_LONG(ret);
7447 }
7448 /* }}} */
7449 
7450 
php_openssl_random_pseudo_bytes(zend_long buffer_length)7451 PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_length)
7452 {
7453 	zend_string *buffer = NULL;
7454 	if (buffer_length <= 0
7455 #ifndef PHP_WIN32
7456 		|| ZEND_LONG_INT_OVFL(buffer_length)
7457 #endif
7458 			) {
7459 		zend_argument_value_error(1, "must be greater than 0");
7460 		return NULL;
7461 	}
7462 	buffer = zend_string_alloc(buffer_length, 0);
7463 
7464 #ifdef PHP_WIN32
7465 	/* random/urandom equivalent on Windows */
7466 	if (php_win32_get_random_bytes((unsigned char*)(buffer)->val, (size_t) buffer_length) == FAILURE){
7467 		zend_string_release_ex(buffer, 0);
7468 		zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
7469 		return NULL;
7470 	}
7471 #else
7472 
7473 	PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(buffer_length, length);
7474 	PHP_OPENSSL_RAND_ADD_TIME();
7475 	/* FIXME loop if requested size > INT_MAX */
7476 	if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), (int)buffer_length) <= 0) {
7477 		zend_string_release_ex(buffer, 0);
7478 		zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
7479 		return NULL;
7480 	} else {
7481 		php_openssl_store_errors();
7482 	}
7483 #endif
7484 	return buffer;
7485 }
7486 
7487 /* {{{ Returns a string of the length specified filled with random pseudo bytes */
PHP_FUNCTION(openssl_random_pseudo_bytes)7488 PHP_FUNCTION(openssl_random_pseudo_bytes)
7489 {
7490 	zend_string *buffer = NULL;
7491 	zend_long buffer_length;
7492 	zval *zstrong_result_returned = NULL;
7493 
7494 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) {
7495 		RETURN_THROWS();
7496 	}
7497 
7498 	if (zstrong_result_returned) {
7499 		ZEND_TRY_ASSIGN_REF_FALSE(zstrong_result_returned);
7500 	}
7501 
7502 	if ((buffer = php_openssl_random_pseudo_bytes(buffer_length))) {
7503 		ZSTR_VAL(buffer)[buffer_length] = 0;
7504 		RETVAL_NEW_STR(buffer);
7505 	}
7506 
7507 	if (zstrong_result_returned) {
7508 		ZEND_TRY_ASSIGN_REF_TRUE(zstrong_result_returned);
7509 	}
7510 }
7511 /* }}} */
7512