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