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