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