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