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