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