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