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