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