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