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