php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
openssl.c
Go to the documentation of this file.
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"
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
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 */
133
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 */
156
157#include "openssl_arginfo.h"
158
159/* OpenSSLCertificate class */
160
162
163static zend_object_handlers php_openssl_certificate_object_handlers;
164
165static 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
174static 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
179static 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
193
194static zend_class_entry *php_openssl_request_ce;
195
196static 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
202static zend_object_handlers php_openssl_request_object_handlers;
203
204static 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
213static 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
218static 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
233
234static zend_class_entry *php_openssl_pkey_ce;
235
236static 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
242static zend_object_handlers php_openssl_pkey_object_handlers;
243
244static 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
253static void php_openssl_pkey_object_init(zval *zv, EVP_PKEY *pkey, bool is_private) {
254 object_init_ex(zv, php_openssl_pkey_ce);
256 obj->pkey = pkey;
257 obj->is_private = is_private;
258}
259
260static 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
265static 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)
274static const zend_module_dep openssl_deps[] = {
275 ZEND_MOD_REQUIRED("standard")
277};
278#endif
279/* {{{ openssl_module_entry */
281#if defined(HAVE_OPENSSL_ARGON2)
283 openssl_deps,
284#else
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,
300};
301/* }}} */
302
303#ifdef COMPILE_DL_OPENSSL
304ZEND_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
315static 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
324static 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
332static 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
341static 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
348static void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
349{
350 *p = r->p;
351 *q = r->q;
352}
353
354static 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
361static 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
368static 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
377static 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
383static 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
391static 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
398int 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
407static 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
413int 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
421static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1)
422{
423 return M_ASN1_STRING_data(asn1);
424}
425
426static 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
433static 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 */
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 */
506static 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 */
520static 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 */
537static 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) {
546 } else {
548 php_verror(NULL, arg_name, type, format, va);
549 }
550 va_end(va);
551}
552
553/* openssl file path check extended */
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
611static int ssl_stream_data_index;
612
614{
615 return (php_stream*)SSL_get_ex_data(ssl, ssl_stream_data_index);
616}
617
619{
620 return ssl_stream_data_index;
621}
622
623/* openssl -> PHP "bridging" */
624/* true global; readonly after module startup */
625static char default_ssl_conf_filename[MAXPATHLEN];
626
627struct 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;
639
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
652static X509 *php_openssl_x509_from_param(
653 zend_object *cert_obj, zend_string *cert_str, uint32_t arg_num);
654static X509 *php_openssl_x509_from_zval(
655 zval *val, bool *free_cert, uint32_t arg_num, bool is_from_array, const char *option_name);
656static X509_REQ *php_openssl_csr_from_param(
657 zend_object *csr_obj, zend_string *csr_str, uint32_t arg_num);
658static EVP_PKEY *php_openssl_pkey_from_zval(
659 zval *val, int public_key, char *passphrase, size_t passphrase_len, uint32_t arg_num);
660
661static X509_STORE * php_openssl_setup_verify(zval * calist, uint32_t arg_num);
662static STACK_OF(X509) * php_openssl_load_all_certs_from_file(
663 char *cert_file, size_t cert_file_len, uint32_t arg_num);
664static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req);
665
666static 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 {
727 }
728
729 if (to_add_buf != NULL) {
730 OPENSSL_free(to_add_buf);
731 }
732 }
733
734 if (key != NULL) {
736 }
737}
738/* }}} */
739
740static 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
746static 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
841static 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)) {
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
859static 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
868static 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
877static 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) {
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) {
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
932static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo);
933
934/* {{{ strip line endings from spkac */
935static 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
952static 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)) {
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);
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"));
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"));
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 {
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();
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
1066static 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
1088static 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
1099static 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) {
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
1129static 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 }
1143 if (file == NULL || !RAND_write_file(file)) {
1145 php_error_docref(NULL, E_WARNING, "Unable to write random state");
1146 return FAILURE;
1147 }
1148 return SUCCESS;
1149}
1150/* }}} */
1151
1152static 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
1178 mdtype = (EVP_MD *) EVP_sha224();
1179 break;
1181 mdtype = (EVP_MD *) EVP_sha256();
1182 break;
1184 mdtype = (EVP_MD *) EVP_sha384();
1185 break;
1187 mdtype = (EVP_MD *) EVP_sha512();
1188 break;
1189#ifndef OPENSSL_NO_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
1202static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo) { /* {{{ */
1203 switch (algo) {
1204#ifndef OPENSSL_NO_RC2
1206 return EVP_rc2_40_cbc();
1207 break;
1209 return EVP_rc2_64_cbc();
1210 break;
1212 return EVP_rc2_cbc();
1213 break;
1214#endif
1215
1216#ifndef OPENSSL_NO_DES
1218 return EVP_des_cbc();
1219 break;
1221 return EVP_des_ede3_cbc();
1222 break;
1223#endif
1224
1225#ifndef OPENSSL_NO_AES
1227 return EVP_aes_128_cbc();
1228 break;
1230 return EVP_aes_192_cbc();
1231 break;
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 */
1247 PHP_INI_ENTRY("openssl.cafile", NULL, PHP_INI_PERDIR, NULL)
1248 PHP_INI_ENTRY("openssl.capath", NULL, PHP_INI_PERDIR, NULL)
1250/* }}} */
1251
1252/* {{{ PHP_MINIT_FUNCTION */
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
1327#ifndef OPENSSL_NO_SSL3
1329#endif
1335
1336 /* override the default tcp socket provider */
1338
1341
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 */
1356{
1357#if defined(COMPILE_DL_OPENSSL) && defined(ZTS)
1359#endif
1360 openssl_globals->errors = NULL;
1361 openssl_globals->errors_mark = NULL;
1362}
1363/* }}} */
1364
1365/* {{{ PHP_GSHUTDOWN_FUNCTION */
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 */
1379{
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);
1387}
1388/* }}} */
1389
1390/* {{{ PHP_MSHUTDOWN_FUNCTION */
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
1411
1413#ifndef OPENSSL_NO_SSL3
1415#endif
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 */
1424
1426
1427 return SUCCESS;
1428}
1429/* }}} */
1430
1431/* {{{ x509 cert functions */
1432
1433/* {{{ Retrieve an array mapping available certificate locations */
1435{
1437 RETURN_THROWS();
1438 }
1439
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
1455static 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) {
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) {
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)) {
1487 }
1488
1489 if (cert == NULL) {
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*/
1504static 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
1516static 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{
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 */
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
1551 Z_PARAM_PATH(filename, filename_len)
1553 Z_PARAM_BOOL(notext)
1555
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)) {
1572 }
1573 if (!PEM_write_bio_X509(bio_out, cert)) {
1575 }
1576
1578 } else {
1580 php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
1581 }
1582
1583 if (!BIO_free(bio_out)) {
1585 }
1586
1587exit_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 */
1597{
1598 size_t challenge_len;
1599 char * challenge = NULL, *spkstr = NULL;
1600 zend_string * s = NULL;
1601 const char *spkac = "SPKAC=";
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 }
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) {
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)) {
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)) {
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)) {
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){
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
1670cleanup:
1671 EVP_PKEY_free(pkey);
1672 if (spki != NULL) {
1673 NETSCAPE_SPKI_free(spki);
1674 }
1675
1676 if (s && ZSTR_LEN(s) <= 0) {
1678 }
1679}
1680/* }}} */
1681
1682/* {{{ Verifies spki returns boolean */
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 }
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) {
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) {
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
1722cleanup:
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) {
1733 } else {
1735 }
1736}
1737/* }}} */
1738
1739/* {{{ Exports public key from existing spki to var */
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 }
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) {
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) {
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 {
1785 }
1786 goto cleanup;
1787
1788cleanup:
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 */
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 }
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) {
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
1833cleanup:
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 */
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
1855 Z_PARAM_ZVAL(zout)
1857 Z_PARAM_BOOL(notext)
1859
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) {
1871 goto cleanup;
1872 }
1873 if (!notext && !X509_print(bio_out, cert)) {
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
1883 } else {
1885 }
1886
1887 BIO_free(bio_out);
1888
1889cleanup:
1890 if (cert_str) {
1891 X509_free(cert);
1892 }
1893}
1894/* }}} */
1895
1896zend_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;
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)) {
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);
1917 ZSTR_VAL(ret)[n * 2] = '\0';
1918 }
1919
1920 return ret;
1921}
1922
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
1936 Z_PARAM_STRING(method, method_len)
1937 Z_PARAM_BOOL(raw_output)
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");
1944 }
1945
1946 fingerprint = php_openssl_x509_fingerprint(cert, method, raw_output);
1947 if (fingerprint) {
1948 RETVAL_STR(fingerprint);
1949 } else {
1951 }
1952
1953 if (cert_str) {
1954 X509_free(cert);
1955 }
1956}
1957
1958/* {{{ Checks if a private key corresponds to a CERT */
1960{
1961 X509 *cert;
1962 zend_object *cert_obj;
1963 zend_string *cert_str;
1964 zval *zkey;
1965 EVP_PKEY * key = NULL;
1966
1969 Z_PARAM_ZVAL(zkey)
1971
1972 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1973 if (cert == NULL) {
1975 }
1976
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 */
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
2003 Z_PARAM_ZVAL(zkey)
2005
2006 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2007 if (cert == NULL) {
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) {
2016 }
2017
2018 EVP_PKEY_free(key);
2019 }
2020
2021 if (cert_str) {
2022 X509_free(cert);
2023 }
2024
2026}
2027/* }}} */
2028
2029/* Special handling of subjectAltName, see CVE-2013-4073
2030 * Christian Heimes
2031 */
2032
2033static 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) {
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 */
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
2125 Z_PARAM_BOOL(useshortnames)
2127
2128 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2129 if (cert == NULL) {
2130 // TODO Add Warning?
2132 }
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) {
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) {
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) {
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
2262err_subitem:
2263 zval_ptr_dtor(&subitem);
2264err:
2266 if (cert_str) {
2267 X509_free(cert);
2268 }
2270}
2271/* }}} */
2272
2273/* {{{ php_openssl_load_all_certs_from_file */
2274static 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())) {
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)))) {
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))) {
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;
2324end:
2325 BIO_free(in);
2326 sk_X509_INFO_free(sk);
2327
2328 return ret;
2329}
2330/* }}} */
2331
2332/* {{{ check_cert */
2333static 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) {
2341 php_error_docref(NULL, E_ERROR, "Memory allocation failure");
2342 return 0;
2343 }
2344 if (!X509_STORE_CTX_init(csc, ctx, x, untrustedchain)) {
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)) {
2351 }
2352 ret = X509_verify_cert(csc);
2353 if (ret < 0) {
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 */
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
2378 Z_PARAM_LONG(purpose)
2380 Z_PARAM_ARRAY(zcainfo)
2381 Z_PARAM_STRING_OR_NULL(untrusted, untrusted_len)
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) {
2406 } else {
2408 }
2409 if (cert_str) {
2410 X509_free(cert);
2411 }
2412clean_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*/
2426static 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) {
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)) {
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)) {
2473 php_error_docref(NULL, E_WARNING, "Error loading directory %s", file_path);
2474 } else {
2475 ndirs++;
2476 }
2477 dir_lookup = NULL;
2478 }
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)) {
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)) {
2491 }
2492 }
2493 return store;
2494}
2495/* }}} */
2496
2497/* {{{ Reads X.509 certificates */
2499{
2500 X509 *cert;
2501 php_openssl_certificate_object *x509_cert_obj;
2502 zend_object *cert_obj;
2503 zend_string *cert_str;
2504
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");
2513 }
2514
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 */
2530/* }}} */
2531
2532/* }}} */
2533
2534/* Pop all X509 from Stack and free them, free the stack afterwards */
2535static 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
2546static 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) {
2569 goto clean_exit;
2570 }
2571
2572 }
2573 sk_X509_push(sk, cert);
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) {
2588 goto clean_exit;
2589 }
2590 }
2591 sk_X509_push(sk, cert);
2592 }
2593
2594clean_exit:
2595 return sk;
2596}
2597/* }}} */
2598
2599/* {{{ Creates and exports a PKCS 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
2619 Z_PARAM_PATH(filename, filename_len)
2620 Z_PARAM_ZVAL(zpkey)
2621 Z_PARAM_STRING(pass, pass_len)
2625
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)) {
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) {
2676 php_error_docref(NULL, E_WARNING, "Error writing to file %s", file_path);
2677 } else {
2679 }
2680 BIO_free(bio_out);
2681 } else {
2683 php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
2684 }
2685
2686 PKCS12_free(p12);
2687 } else {
2689 }
2690
2691 php_sk_X509_free(ca);
2692
2693cleanup:
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 */
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
2720 Z_PARAM_ZVAL(zout)
2721 Z_PARAM_ZVAL(zpkey)
2722 Z_PARAM_STRING(pass, pass_len)
2726
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
2771 } else {
2773 }
2774
2775 BIO_free(bio_out);
2776 PKCS12_free(p12);
2777 } else {
2779 }
2780 php_sk_X509_free(ca);
2781
2782cleanup:
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 */
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
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)) {
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 {
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 {
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
2879 } else {
2881 }
2882
2883cleanup:
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
2897static 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 {
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
2919static 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) {
2927 return FAILURE;
2928 }
2929 dn_sk = NCONF_get_section(req->req_config, dn_sect);
2930 if (dn_sk == NULL) {
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) {
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 }
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 }
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)) {
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))) {
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 }
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)) {
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 {
3069 }
3070
3071 if (!X509_REQ_set_pubkey(csr, req->priv_key)) {
3073 }
3074 return SUCCESS;
3075}
3076
3077static 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) {
3094 return NULL;
3095 }
3096
3097 csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
3098 if (csr == NULL) {
3100 }
3101
3102 BIO_free(in);
3103
3104 return csr;
3105}
3106
3107static 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 */
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
3132 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3133 Z_PARAM_PATH(filename, filename_len)
3135 Z_PARAM_BOOL(notext)
3137
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)) {
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);
3158 } else {
3160 }
3161 BIO_free(bio_out);
3162 } else {
3164 php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
3165 }
3166
3167exit_cleanup:
3168 if (csr_str) {
3169 X509_REQ_free(csr);
3170 }
3171}
3172/* }}} */
3173
3174/* {{{ Exports a CSR to file or a var */
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
3185 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3186 Z_PARAM_ZVAL(zout)
3188 Z_PARAM_BOOL(notext)
3190
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)) {
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
3213 } else {
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 */
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
3251 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3253 Z_PARAM_ZVAL(zpkey)
3254 Z_PARAM_LONG(num_days)
3257 Z_PARAM_LONG(serial)
3258 Z_PARAM_STR_OR_NULL(serial_hex)
3260
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)) {
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) {
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) {
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) {
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) {
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) {
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))) {
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) {
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)) {
3378 goto cleanup;
3379 }
3380 }
3381
3382 /* Now sign it */
3383 if (!X509_sign(new_cert, priv_key, req.digest)) {
3385 php_error_docref(NULL, E_WARNING, "Failed to sign it");
3386 goto cleanup;
3387 }
3388
3390 cert_object = Z_OPENSSL_CERTIFICATE_P(return_value);
3391 cert_object->x509 = new_cert;
3392 new_cert_used = true;
3393
3394cleanup:
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 */
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 }
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 {
3457 php_error_docref(NULL, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
3458 } else {
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 {
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 {
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 */
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
3504 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3506 Z_PARAM_BOOL(use_shortnames)
3508
3509 csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3510 if (csr == NULL) {
3512 }
3513
3514 subject = X509_REQ_get_subject_name(csr);
3515
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
3525static 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 */
3541{
3542 zend_object *csr_obj;
3543 zend_string *csr_str;
3544 bool use_shortnames = 1;
3545
3547 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3549 Z_PARAM_BOOL(use_shortnames)
3551
3552 X509_REQ *csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3553 if (csr == NULL) {
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) {
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
3581 char *key;
3582 int len;
3583};
3584
3585/* {{{ php_openssl_pem_password_cb */
3586static 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
3601static 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 }
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) {
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) {
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) {
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
3768static int php_openssl_get_evp_pkey_type(int key_type) {
3769 switch (key_type) {
3771 return EVP_PKEY_RSA;
3772#if !defined(OPENSSL_NO_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
3786 return EVP_PKEY_X25519;
3788 return EVP_PKEY_ED25519;
3790 return EVP_PKEY_X448;
3792 return EVP_PKEY_ED448;
3793#endif
3794 default:
3795 return -1;
3796 }
3797}
3798
3799/* {{{ php_openssl_generate_private_key */
3800static 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",
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);
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) {
3824 goto cleanup;
3825 }
3826
3827 if (type != EVP_PKEY_RSA) {
3828 if (EVP_PKEY_paramgen_init(ctx) <= 0) {
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) {
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) {
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) {
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
3875 }
3876
3877 if (EVP_PKEY_paramgen(ctx, &params) <= 0) {
3879 goto cleanup;
3880 }
3881
3882 EVP_PKEY_CTX_free(ctx);
3883 ctx = EVP_PKEY_CTX_new(params, NULL);
3884 if (!ctx) {
3886 goto cleanup;
3887 }
3888 }
3889
3890 if (EVP_PKEY_keygen_init(ctx) <= 0) {
3892 goto cleanup;
3893 }
3894
3895 if (type == EVP_PKEY_RSA && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, req->priv_key_bits) <= 0) {
3897 goto cleanup;
3898 }
3899
3900 if (EVP_PKEY_keygen(ctx, &key) <= 0) {
3902 goto cleanup;
3903 }
3904
3905 req->priv_key = key;
3906
3907cleanup:
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
3915static 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
3940static bool php_openssl_pkey_init_legacy_rsa(RSA *rsa, zval *data)
3941{
3942 BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
3943
3947 if (!n || !d || !RSA_set0_key(rsa, n, e, d)) {
3948 return 0;
3949 }
3950
3953 if ((p || q) && !RSA_set0_factors(rsa, p, q)) {
3954 return 0;
3955 }
3956
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
3968static 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
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
4022cleanup:
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) {
4040 return NULL;
4041 }
4042
4043 RSA *rsa = RSA_new();
4044 if (!rsa) {
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)) {
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
4063static 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
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 */
4084 if (!DSA_generate_key(dsa)) {
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
4101static 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
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, &param_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;
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
4157cleanup:
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) {
4173 return NULL;
4174 }
4175
4176 DSA *dsa = DSA_new();
4177 if (!dsa) {
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)) {
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 */
4196static 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) {
4204 return NULL;
4205 }
4206
4207 priv_key_const_time = BN_new();
4208 if (priv_key_const_time == NULL) {
4209 BN_free(pub_key);
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);
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);
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
4237static 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
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 */
4264 if (!DH_generate_key(dh)) {
4266 return 0;
4267 }
4268 /* all good */
4269 *is_private = true;
4270 return 1;
4271}
4272#endif
4273
4274static 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
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, &param_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;
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
4337cleanup:
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) {
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)) {
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
4376static 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 {
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 {
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 {
4430
4431 if (!g_x || !g_y) {
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
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;
4498 EC_KEY_generate_key(eckey);
4499 }
4500
4501clean_exit:
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
4521static 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 {
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 {
4598
4599 if (!g_x || !g_y) {
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) {
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, &param_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;
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
4695cleanup:
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) {
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)) {
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
4745static 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
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;
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
4796cleanup:
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 */
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 }
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) {
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) {
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) {
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) {
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 */
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 }
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 }
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) {
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 */
4950 } else {
4952 }
4953 }
4954
4955clean_exit:
4956 PHP_SSL_REQ_DISPOSE(&req);
4957 BIO_free(bio_out);
4958clean_exit_key:
4959 EVP_PKEY_free(key);
4960}
4961/* }}} */
4962
4963/* {{{ Gets an exportable representation of a key into a string or file */
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 }
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 }
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;
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 {
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 */
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) {
5039 }
5040
5041 php_openssl_pkey_object_init(return_value, pkey, /* is_private */ false);
5042}
5043/* }}} */
5044
5045/* {{{ Frees a key */
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 */
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) {
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
5083static 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
5093static 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
5107static 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
5119static 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)*/
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);
5148 }
5149 pbio_len = BIO_get_mem_data(out, &pbio);
5150
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: {
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);
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);
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
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 {
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
5405static 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)) {
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) {
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
5433static 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) {
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) {
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 */
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) {
5485 }
5486
5487 zend_string *result = php_openssl_dh_compute_key(pkey, pub_str, pub_len);
5488 if (result) {
5490 } else {
5492 }
5493}
5494/* }}} */
5495
5496/* {{{ Computes shared secret for public value of remote and local DH or ECDH key */
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) {
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);
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) {
5529 } else {
5531 }
5532}
5533/* }}} */
5534
5535
5536/* {{{ Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
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");
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 {
5586 zend_string_release_ex(out_buffer, 0);
5588 }
5589}
5590/* }}} */
5591
5593static 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) {
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 */
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
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
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
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))) {
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);
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");
5727 }
5728 }
5729 }
5730 } else {
5733 }
5734clean_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 */
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;
5759 zend_string * strindex;
5760 char * infilename = NULL;
5761 size_t infilename_len;
5762 char * outfilename = NULL;
5763 size_t outfilename_len;
5764
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) {
5801 goto clean_exit;
5802 }
5803 }
5804 sk_X509_push(recipcerts, cert);
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) {
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) {
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);
5857 }
5858
5859 (void)BIO_reset(infile);
5860
5861 /* write the encrypted data */
5862 if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
5864 goto clean_exit;
5865 }
5866
5868
5869clean_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 */
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
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)) {
5907 goto clean_exit;
5908 }
5909
5910 p7 = PEM_read_bio_PKCS7(bio_in, NULL, NULL, NULL);
5911 if (p7 == NULL) {
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 }
5949 BIO_free(bio_out);
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 }
5964 BIO_free(bio_out);
5965 }
5966 }
5967
5969
5970clean_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
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;
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
6002 Z_PARAM_PATH(infilename, infilename_len)
6003 Z_PARAM_PATH(outfilename, outfilename_len)
6005 Z_PARAM_ZVAL(zprivkey)
6006 Z_PARAM_ARRAY_OR_NULL(zheaders)
6009 Z_PARAM_PATH_OR_NULL(extracertsfilename, extracertsfilename_len)
6011
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) {
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) {
6074 }
6076 }
6077 /* write the signed data */
6078 if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
6080 goto clean_exit;
6081 }
6082
6084
6085clean_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
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
6115 Z_PARAM_PATH(infilename, infilename_len)
6116 Z_PARAM_PATH(outfilename, outfilename_len)
6117 Z_PARAM_ZVAL(recipcert)
6119 Z_PARAM_ZVAL_OR_NULL(recipkey)
6121
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) {
6154 goto clean_exit;
6155 }
6156 if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
6158 } else {
6160 }
6161clean_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 */
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;
6201
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) {
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) {
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)) {
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))) {
6300 php_error_docref(NULL, E_WARNING, "Failed to write signer %d", i);
6301 }
6302 }
6303
6304 sk_X509_free(signers);
6305 } else {
6308 }
6309 } else {
6310 php_error_docref(NULL, E_WARNING, "Signature OK, but cannot open %s for writing", signersfilename);
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");
6319 }
6320 }
6321 }
6322 } else {
6325 }
6326clean_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 */
6351{
6352 zval * zrecipcerts, * zheaders = NULL;
6353 STACK_OF(X509) * recipcerts = NULL;
6354 BIO * infile = NULL, * outfile = NULL;
6355 zend_long flags = 0;
6357 CMS_ContentInfo * cms = NULL;
6358 zval * zcertval;
6359 X509 * cert;
6360 const EVP_CIPHER *cipher = NULL;
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
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) {
6407 goto clean_exit;
6408 }
6409 }
6410 sk_X509_push(recipcerts, cert);
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) {
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) {
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);
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)) {
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) {
6481 goto clean_exit;
6482 }
6483 }
6484 if (i2d_CMS_bio(outfile, cms) != 1) {
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) {
6493 goto clean_exit;
6494 }
6495 }
6496 if (flags & CMS_STREAM) {
6497 if (PEM_write_bio_CMS_stream(outfile, cms, infile, flags) == 0) {
6499 goto clean_exit;
6500 }
6501 } else {
6502 if (PEM_write_bio_CMS(outfile, cms) == 0) {
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
6514
6515clean_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 */
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
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)) {
6555 goto clean_exit;
6556 }
6557
6558 cms = PEM_read_bio_CMS(bio_in, NULL, NULL, NULL);
6559 if (cms == NULL) {
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 }
6590 BIO_free(bio_out);
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 }
6605 BIO_free(bio_out);
6606 }
6607 }
6608
6610
6611clean_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
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;
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
6650 Z_PARAM_PATH(infilename, infilename_len)
6651 Z_PARAM_PATH(outfilename, outfilename_len)
6653 Z_PARAM_ZVAL(zprivkey)
6654 Z_PARAM_ARRAY_OR_NULL(zheaders)
6658 Z_PARAM_PATH_OR_NULL(extracertsfilename, extracertsfilename_len)
6660
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)) {
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) {
6719 php_error_docref(NULL, E_WARNING, "Error creating CMS structure!");
6720 goto clean_exit;
6721 }
6722 if (BIO_reset(infile) != 0) {
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) {
6744 }
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)) {
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) {
6759 goto clean_exit;
6760 }
6761 }
6762 if (i2d_CMS_bio(outfile, cms) != 1) {
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) {
6771 goto clean_exit;
6772 }
6773 }
6774 if (flags & CMS_STREAM) {
6775 if (PEM_write_bio_CMS_stream(outfile, cms, infile, flags) == 0) {
6777 goto clean_exit;
6778 }
6779 } else {
6780 if (PEM_write_bio_CMS(outfile, cms) == 0) {
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 }
6791
6792clean_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
6811{
6812 X509 *cert;
6813 zval *recipcert, *recipkey = NULL;
6814 bool free_recipcert;
6815 EVP_PKEY * key = NULL;
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
6825 Z_PARAM_PATH(infilename, infilename_len)
6826 Z_PARAM_PATH(outfilename, outfilename_len)
6827 Z_PARAM_ZVAL(recipcert)
6829 Z_PARAM_ZVAL_OR_NULL(recipkey)
6832
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) {
6878 goto clean_exit;
6879 }
6880 if (CMS_decrypt(cms, key, cert, NULL, out, 0)) {
6882 } else {
6884 }
6885clean_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 */
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 }
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) {
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);
6939 goto cleanup;
6940 }
6941
6942 ZSTR_VAL(out)[out_len] = '\0';
6945
6946cleanup:
6947 EVP_PKEY_CTX_free(ctx);
6948 EVP_PKEY_free(pkey);
6949}
6950/* }}} */
6951
6952/* {{{ Decrypts data with private key */
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 }
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) {
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);
6988 goto cleanup;
6989 }
6990
6991 out = zend_string_truncate(out, out_len, 0);
6992 ZSTR_VAL(out)[out_len] = '\0';
6995
6996cleanup:
6997 EVP_PKEY_CTX_free(ctx);
6998 EVP_PKEY_free(pkey);
6999}
7000/* }}} */
7001
7002/* {{{ Encrypts data with public key */
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 }
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) {
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);
7038 goto cleanup;
7039 }
7040
7041 ZSTR_VAL(out)[out_len] = '\0';
7044
7045cleanup:
7046 EVP_PKEY_CTX_free(ctx);
7047 EVP_PKEY_free(pkey);
7048}
7049/* }}} */
7050
7051/* {{{ Decrypts data with public key */
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 }
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) {
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);
7087 goto cleanup;
7088 }
7089
7090 out = zend_string_truncate(out, out_len, 0);
7091 ZSTR_VAL(out)[out_len] = '\0';
7094
7095cleanup:
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 */
7103{
7104 char buf[256];
7105 unsigned long val;
7106
7108 RETURN_THROWS();
7109 }
7110
7112
7113 if (OPENSSL_G(errors) == NULL || OPENSSL_G(errors)->top == OPENSSL_G(errors)->bottom) {
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);
7123 } else {
7125 }
7126}
7127/* }}} */
7128
7129/* {{{ Signs data */
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
7144 Z_PARAM_STRING(data, data_len)
7145 Z_PARAM_ZVAL(signature)
7148 Z_PARAM_STR_OR_LONG(method_str, method_long)
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 }
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 EVP_PKEY_free(pkey);
7166 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
7168 }
7169
7170 md_ctx = EVP_MD_CTX_create();
7171 size_t siglen;
7172#if PHP_OPENSSL_API_VERSION >= 0x10100
7173 if (md_ctx != NULL &&
7174 EVP_DigestSignInit(md_ctx, NULL, mdtype, NULL, pkey) &&
7175 EVP_DigestSign(md_ctx, NULL, &siglen, (unsigned char*)data, data_len) &&
7176 (sigbuf = zend_string_alloc(siglen, 0)) != NULL &&
7177 EVP_DigestSign(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, (unsigned char*)data, data_len)) {
7178#else
7179 if (md_ctx != NULL &&
7180 EVP_SignInit(md_ctx, mdtype) &&
7181 EVP_SignUpdate(md_ctx, data, data_len) &&
7182 (siglen = EVP_PKEY_size(pkey)) &&
7183 (sigbuf = zend_string_alloc(siglen, 0)) != NULL &&
7184 EVP_SignFinal(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), (unsigned int*)&siglen, pkey)) {
7185#endif
7186 ZSTR_VAL(sigbuf)[siglen] = '\0';
7187 ZSTR_LEN(sigbuf) = siglen;
7188 ZEND_TRY_ASSIGN_REF_NEW_STR(signature, sigbuf);
7190 } else {
7192 efree(sigbuf);
7194 }
7195 EVP_MD_CTX_destroy(md_ctx);
7196 EVP_PKEY_free(pkey);
7197}
7198/* }}} */
7199
7200/* {{{ Verifys data */
7202{
7203 zval *key;
7204 EVP_PKEY *pkey;
7205 int err = 0;
7206 EVP_MD_CTX *md_ctx;
7207 const EVP_MD *mdtype;
7208 char * data;
7209 size_t data_len;
7210 char * signature;
7211 size_t signature_len;
7212 zend_string *method_str = NULL;
7213 zend_long method_long = OPENSSL_ALGO_SHA1;
7214 bool can_default_digest = ZEND_THREEWAY_COMPARE(PHP_OPENSSL_API_VERSION, 0x30000) >= 0;
7215
7217 Z_PARAM_STRING(data, data_len)
7218 Z_PARAM_STRING(signature, signature_len)
7221 Z_PARAM_STR_OR_LONG(method_str, method_long)
7223
7224 PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature, 2);
7225
7226 if (method_str) {
7227 mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str));
7228 } else {
7229 mdtype = php_openssl_get_evp_md_from_algo(method_long);
7230 }
7231 if (!mdtype && (!can_default_digest || method_long != 0)) {
7232 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
7234 }
7235
7236 pkey = php_openssl_pkey_from_zval(key, 1, NULL, 0, 3);
7237 if (pkey == NULL) {
7238 if (!EG(exception)) {
7239 php_error_docref(NULL, E_WARNING, "Supplied key param cannot be coerced into a public key");
7240 }
7242 }
7243
7244 md_ctx = EVP_MD_CTX_create();
7245 if (md_ctx == NULL ||
7246#if PHP_OPENSSL_API_VERSION >= 0x10100
7247 !EVP_DigestVerifyInit(md_ctx, NULL, mdtype, NULL, pkey) ||
7248 (err = EVP_DigestVerify(md_ctx, (unsigned char *)signature, signature_len, (unsigned char*)data, data_len)) < 0) {
7249#else
7250 !EVP_VerifyInit (md_ctx, mdtype) ||
7251 !EVP_VerifyUpdate (md_ctx, data, data_len) ||
7252 (err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey)) < 0) {
7253#endif
7255 }
7256 EVP_MD_CTX_destroy(md_ctx);
7257 EVP_PKEY_free(pkey);
7259}
7260/* }}} */
7261
7262/* {{{ Seals data */
7264{
7265 zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL;
7266 HashTable *pubkeysht;
7267 EVP_PKEY **pkeys;
7268 int i, len1, len2, *eksl, nkeys, iv_len;
7269 unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks;
7270 char * data;
7271 size_t data_len;
7272 char *method;
7273 size_t method_len;
7274 const EVP_CIPHER *cipher;
7275 EVP_CIPHER_CTX *ctx;
7276
7277 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z", &data, &data_len,
7278 &sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) {
7279 RETURN_THROWS();
7280 }
7281
7283
7284 pubkeysht = Z_ARRVAL_P(pubkeys);
7285 nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
7286 if (!nkeys) {
7288 RETURN_THROWS();
7289 }
7290
7291 cipher = EVP_get_cipherbyname(method);
7292 if (!cipher) {
7293 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7295 }
7296
7297 iv_len = EVP_CIPHER_iv_length(cipher);
7298 if (!iv && iv_len > 0) {
7299 zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm");
7300 RETURN_THROWS();
7301 }
7302
7303 pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
7304 eksl = safe_emalloc(nkeys, sizeof(*eksl), 0);
7305 eks = safe_emalloc(nkeys, sizeof(*eks), 0);
7306 memset(eks, 0, sizeof(*eks) * nkeys);
7307 memset(pkeys, 0, sizeof(*pkeys) * nkeys);
7308
7309 /* get the public keys we are using to seal this data */
7310 i = 0;
7311 ZEND_HASH_FOREACH_VAL(pubkeysht, pubkey) {
7312 pkeys[i] = php_openssl_pkey_from_zval(pubkey, 1, NULL, 0, 4);
7313 if (pkeys[i] == NULL) {
7314 if (!EG(exception)) {
7315 php_error_docref(NULL, E_WARNING, "Not a public key (%dth member of pubkeys)", i+1);
7316 }
7318 goto clean_exit;
7319 }
7320 eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
7321 i++;
7323
7324 ctx = EVP_CIPHER_CTX_new();
7325 if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) {
7326 EVP_CIPHER_CTX_free(ctx);
7329 goto clean_exit;
7330 }
7331
7332 /* allocate one byte extra to make room for \0 */
7333 buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(ctx));
7335
7336 if (EVP_SealInit(ctx, cipher, eks, eksl, &iv_buf[0], pkeys, nkeys) <= 0 ||
7337 !EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
7338 !EVP_SealFinal(ctx, buf + len1, &len2)) {
7339 efree(buf);
7340 EVP_CIPHER_CTX_free(ctx);
7343 goto clean_exit;
7344 }
7345
7346 if (len1 + len2 > 0) {
7347 ZEND_TRY_ASSIGN_REF_NEW_STR(sealdata, zend_string_init((char*)buf, len1 + len2, 0));
7348 efree(buf);
7349
7350 ekeys = zend_try_array_init(ekeys);
7351 if (!ekeys) {
7352 EVP_CIPHER_CTX_free(ctx);
7353 goto clean_exit;
7354 }
7355
7356 for (i=0; i<nkeys; i++) {
7357 eks[i][eksl[i]] = '\0';
7358 add_next_index_stringl(ekeys, (const char*)eks[i], eksl[i]);
7359 efree(eks[i]);
7360 eks[i] = NULL;
7361 }
7362
7363 if (iv) {
7364 iv_buf[iv_len] = '\0';
7365 ZEND_TRY_ASSIGN_REF_NEW_STR(iv, zend_string_init((char*)iv_buf, iv_len, 0));
7366 }
7367 } else {
7368 efree(buf);
7369 }
7370 RETVAL_LONG(len1 + len2);
7371 EVP_CIPHER_CTX_free(ctx);
7372
7373clean_exit:
7374 for (i=0; i<nkeys; i++) {
7375 if (pkeys[i] != NULL) {
7376 EVP_PKEY_free(pkeys[i]);
7377 }
7378 if (eks[i]) {
7379 efree(eks[i]);
7380 }
7381 }
7382 efree(eks);
7383 efree(eksl);
7384 efree(pkeys);
7385}
7386/* }}} */
7387
7388/* {{{ Opens data */
7390{
7391 zval *privkey, *opendata;
7392 EVP_PKEY *pkey;
7393 int len1, len2, cipher_iv_len;
7394 unsigned char *buf, *iv_buf;
7395 EVP_CIPHER_CTX *ctx;
7396 char * data;
7397 size_t data_len;
7398 char * ekey;
7399 size_t ekey_len;
7400 char *method, *iv = NULL;
7401 size_t method_len, iv_len = 0;
7402 const EVP_CIPHER *cipher;
7403
7404 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szszs|s!", &data, &data_len, &opendata,
7405 &ekey, &ekey_len, &privkey, &method, &method_len, &iv, &iv_len) == FAILURE) {
7406 RETURN_THROWS();
7407 }
7408
7410 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(ekey_len, ekey, 3);
7411 pkey = php_openssl_pkey_from_zval(privkey, 0, "", 0, 4);
7412
7413 if (pkey == NULL) {
7414 if (!EG(exception)) {
7415 php_error_docref(NULL, E_WARNING, "Unable to coerce parameter 4 into a private key");
7416 }
7418 }
7419
7420 cipher = EVP_get_cipherbyname(method);
7421 if (!cipher) {
7422 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7424 }
7425
7426 cipher_iv_len = EVP_CIPHER_iv_length(cipher);
7427 if (cipher_iv_len > 0) {
7428 if (!iv) {
7429 zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm");
7430 RETURN_THROWS();
7431 }
7432 if ((size_t)cipher_iv_len != iv_len) {
7433 php_error_docref(NULL, E_WARNING, "IV length is invalid");
7435 }
7436 iv_buf = (unsigned char *)iv;
7437 } else {
7438 iv_buf = NULL;
7439 }
7440
7441 buf = emalloc(data_len + 1);
7442
7443 ctx = EVP_CIPHER_CTX_new();
7444 if (ctx != NULL && EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) &&
7445 EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) &&
7446 EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) {
7447 buf[len1 + len2] = '\0';
7448 ZEND_TRY_ASSIGN_REF_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
7450 } else {
7453 }
7454
7455 efree(buf);
7456 EVP_PKEY_free(pkey);
7457 EVP_CIPHER_CTX_free(ctx);
7458}
7459/* }}} */
7460
7461static void php_openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */
7462{
7463 add_next_index_string((zval*)arg, (char*)name->name);
7464}
7465/* }}} */
7466
7467static void php_openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */
7468{
7469 if (name->alias == 0) {
7470 add_next_index_string((zval*)arg, (char*)name->name);
7471 }
7472}
7473/* }}} */
7474
7475/* {{{ Return array of available digest algorithms */
7477{
7478 bool aliases = 0;
7479
7480 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
7481 RETURN_THROWS();
7482 }
7484 OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
7485 aliases ? php_openssl_add_method_or_alias: php_openssl_add_method,
7486 return_value);
7487}
7488/* }}} */
7489
7490#if PHP_OPENSSL_API_VERSION >= 0x30000
7491static void php_openssl_add_cipher_name(const char *name, void *arg)
7492{
7493 size_t len = strlen(name);
7494 zend_string *str = zend_string_alloc(len, 0);
7496 add_next_index_str((zval*)arg, str);
7497}
7498
7499static void php_openssl_add_cipher_or_alias(EVP_CIPHER *cipher, void *arg)
7500{
7501 EVP_CIPHER_names_do_all(cipher, php_openssl_add_cipher_name, arg);
7502}
7503
7504static void php_openssl_add_cipher(EVP_CIPHER *cipher, void *arg)
7505{
7506 php_openssl_add_cipher_name(EVP_CIPHER_get0_name(cipher), arg);
7507}
7508
7509static int php_openssl_compare_func(Bucket *a, Bucket *b)
7510{
7511 return string_compare_function(&a->val, &b->val);
7512}
7513#endif
7514
7515/* {{{ Return array of available cipher algorithms */
7517{
7518 bool aliases = 0;
7519
7520 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
7521 RETURN_THROWS();
7522 }
7524#if PHP_OPENSSL_API_VERSION >= 0x30000
7525 EVP_CIPHER_do_all_provided(NULL,
7526 aliases ? php_openssl_add_cipher_or_alias : php_openssl_add_cipher,
7527 return_value);
7528 zend_hash_sort(Z_ARRVAL_P(return_value), php_openssl_compare_func, 1);
7529#else
7530 OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
7531 aliases ? php_openssl_add_method_or_alias : php_openssl_add_method,
7532 return_value);
7533#endif
7534}
7535/* }}} */
7536
7537/* {{{ Return array of available elliptic curves */
7538#ifdef HAVE_EVP_PKEY_EC
7540{
7541 EC_builtin_curve *curves = NULL;
7542 const char *sname;
7543 size_t i;
7544 size_t len = EC_get_builtin_curves(NULL, 0);
7545
7547 RETURN_THROWS();
7548 }
7549
7550 curves = emalloc(sizeof(EC_builtin_curve) * len);
7551 if (!EC_get_builtin_curves(curves, len)) {
7553 }
7554
7556 for (i = 0; i < len; i++) {
7557 sname = OBJ_nid2sn(curves[i].nid);
7558 if (sname != NULL) {
7560 }
7561 }
7562 efree(curves);
7563}
7564#endif
7565/* }}} */
7566
7567/* {{{ Computes digest hash value for given data using given method, returns raw or binhex encoded string */
7569{
7570 bool raw_output = 0;
7571 char *data, *method;
7572 size_t data_len, method_len;
7573 const EVP_MD *mdtype;
7574 EVP_MD_CTX *md_ctx;
7575 unsigned int siglen;
7576 zend_string *sigbuf;
7577
7578 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
7579 RETURN_THROWS();
7580 }
7581 mdtype = EVP_get_digestbyname(method);
7582 if (!mdtype) {
7583 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
7585 }
7586
7587 siglen = EVP_MD_size(mdtype);
7588 sigbuf = zend_string_alloc(siglen, 0);
7589
7590 md_ctx = EVP_MD_CTX_create();
7591 if (EVP_DigestInit(md_ctx, mdtype) &&
7592 EVP_DigestUpdate(md_ctx, (unsigned char *)data, data_len) &&
7593 EVP_DigestFinal (md_ctx, (unsigned char *)ZSTR_VAL(sigbuf), &siglen)) {
7594 if (raw_output) {
7595 ZSTR_VAL(sigbuf)[siglen] = '\0';
7596 ZSTR_LEN(sigbuf) = siglen;
7597 RETVAL_STR(sigbuf);
7598 } else {
7599 int digest_str_len = siglen * 2;
7600 zend_string *digest_str = zend_string_alloc(digest_str_len, 0);
7601
7602 make_digest_ex(ZSTR_VAL(digest_str), (unsigned char*)ZSTR_VAL(sigbuf), siglen);
7603 ZSTR_VAL(digest_str)[digest_str_len] = '\0';
7604 zend_string_release_ex(sigbuf, 0);
7605 RETVAL_NEW_STR(digest_str);
7606 }
7607 } else {
7609 zend_string_release_ex(sigbuf, 0);
7611 }
7612
7613 EVP_MD_CTX_destroy(md_ctx);
7614}
7615/* }}} */
7616
7617/* Cipher mode info */
7627
7628#if PHP_OPENSSL_API_VERSION >= 0x10100
7629static inline void php_openssl_set_aead_flags(struct php_openssl_cipher_mode *mode) {
7630 mode->is_aead = true;
7631 mode->aead_get_tag_flag = EVP_CTRL_AEAD_GET_TAG;
7632 mode->aead_set_tag_flag = EVP_CTRL_AEAD_SET_TAG;
7633 mode->aead_ivlen_flag = EVP_CTRL_AEAD_SET_IVLEN;
7634}
7635#endif
7636
7637static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type)
7638{
7639 int cipher_mode = EVP_CIPHER_mode(cipher_type);
7640 memset(mode, 0, sizeof(struct php_openssl_cipher_mode));
7641 switch (cipher_mode) {
7642#if PHP_OPENSSL_API_VERSION >= 0x10100
7643 /* Since OpenSSL 1.1, all AEAD ciphers use a common framework. We check for
7644 * EVP_CIPH_OCB_MODE, because LibreSSL does not support it. */
7645 case EVP_CIPH_GCM_MODE:
7646 case EVP_CIPH_CCM_MODE:
7647# ifdef EVP_CIPH_OCB_MODE
7648 case EVP_CIPH_OCB_MODE:
7649 /* For OCB mode, explicitly set the tag length even when decrypting,
7650 * see https://github.com/openssl/openssl/issues/8331. */
7651 mode->set_tag_length_always = cipher_mode == EVP_CIPH_OCB_MODE;
7652# endif
7653 php_openssl_set_aead_flags(mode);
7654 mode->set_tag_length_when_encrypting = cipher_mode == EVP_CIPH_CCM_MODE;
7655 mode->is_single_run_aead = cipher_mode == EVP_CIPH_CCM_MODE;
7656 break;
7657# ifdef NID_chacha20_poly1305
7658 default:
7659 if (EVP_CIPHER_nid(cipher_type) == NID_chacha20_poly1305) {
7660 php_openssl_set_aead_flags(mode);
7661 }
7662 break;
7663
7664# endif
7665#else
7666# ifdef EVP_CIPH_GCM_MODE
7667 case EVP_CIPH_GCM_MODE:
7668 mode->is_aead = 1;
7669 mode->aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG;
7670 mode->aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG;
7671 mode->aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN;
7672 break;
7673# endif
7674# ifdef EVP_CIPH_CCM_MODE
7675 case EVP_CIPH_CCM_MODE:
7676 mode->is_aead = 1;
7677 mode->is_single_run_aead = 1;
7678 mode->set_tag_length_when_encrypting = 1;
7679 mode->aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG;
7680 mode->aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG;
7681 mode->aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN;
7682 break;
7683# endif
7684#endif
7685 }
7686}
7687
7688static int php_openssl_validate_iv(const char **piv, size_t *piv_len, size_t iv_required_len,
7689 bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */
7690{
7691 char *iv_new;
7692
7693 if (mode->is_aead) {
7694 if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_ivlen_flag, *piv_len, NULL) != 1) {
7695 php_error_docref(NULL, E_WARNING, "Setting of IV length for AEAD mode failed");
7696 return FAILURE;
7697 }
7698 return SUCCESS;
7699 }
7700
7701 /* Best case scenario, user behaved */
7702 if (*piv_len == iv_required_len) {
7703 return SUCCESS;
7704 }
7705
7706 iv_new = ecalloc(1, iv_required_len + 1);
7707
7708 if (*piv_len == 0) {
7709 /* BC behavior */
7710 *piv_len = iv_required_len;
7711 *piv = iv_new;
7712 *free_iv = 1;
7713 return SUCCESS;
7714
7715 }
7716
7717 if (*piv_len < iv_required_len) {
7719 "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0",
7720 *piv_len, iv_required_len);
7721 memcpy(iv_new, *piv, *piv_len);
7722 *piv_len = iv_required_len;
7723 *piv = iv_new;
7724 *free_iv = 1;
7725 return SUCCESS;
7726 }
7727
7729 "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating",
7730 *piv_len, iv_required_len);
7731 memcpy(iv_new, *piv, iv_required_len);
7732 *piv_len = iv_required_len;
7733 *piv = iv_new;
7734 *free_iv = 1;
7735 return SUCCESS;
7736
7737}
7738/* }}} */
7739
7740static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
7741 EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
7742 const char **ppassword, size_t *ppassword_len, bool *free_password,
7743 const char **piv, size_t *piv_len, bool *free_iv,
7744 const char *tag, int tag_len, zend_long options, int enc) /* {{{ */
7745{
7746 unsigned char *key;
7747 int key_len, password_len;
7748 size_t max_iv_len;
7749
7750 *free_password = 0;
7751
7752 max_iv_len = EVP_CIPHER_iv_length(cipher_type);
7753 if (enc && *piv_len == 0 && max_iv_len > 0 && !mode->is_aead) {
7755 "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
7756 }
7757
7758 if (!EVP_CipherInit_ex(cipher_ctx, cipher_type, NULL, NULL, NULL, enc)) {
7760 return FAILURE;
7761 }
7762 if (php_openssl_validate_iv(piv, piv_len, max_iv_len, free_iv, cipher_ctx, mode) == FAILURE) {
7763 return FAILURE;
7764 }
7765 if (mode->set_tag_length_always || (enc && mode->set_tag_length_when_encrypting)) {
7766 if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL)) {
7767 php_error_docref(NULL, E_WARNING, "Setting tag length for AEAD cipher failed");
7768 return FAILURE;
7769 }
7770 }
7771 if (!enc && tag && tag_len > 0) {
7772 if (!mode->is_aead) {
7773 php_error_docref(NULL, E_WARNING, "The tag cannot be used because the cipher algorithm does not support AEAD");
7774 } else if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, (unsigned char *) tag)) {
7775 php_error_docref(NULL, E_WARNING, "Setting tag for AEAD cipher decryption failed");
7776 return FAILURE;
7777 }
7778 }
7779 /* check and set key */
7780 password_len = (int) *ppassword_len;
7781 key_len = EVP_CIPHER_key_length(cipher_type);
7782 if (key_len > password_len) {
7783 if ((OPENSSL_DONT_ZERO_PAD_KEY & options) && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
7785 php_error_docref(NULL, E_WARNING, "Key length cannot be set for the cipher algorithm");
7786 return FAILURE;
7787 }
7788 key = emalloc(key_len);
7789 memset(key, 0, key_len);
7790 memcpy(key, *ppassword, password_len);
7791 *ppassword = (char *) key;
7792 *ppassword_len = key_len;
7793 *free_password = 1;
7794 } else {
7795 if (password_len > key_len && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
7797 }
7798 key = (unsigned char*)*ppassword;
7799 }
7800
7801 if (!EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)*piv, enc)) {
7803 return FAILURE;
7804 }
7806 EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
7807 }
7808
7809 return SUCCESS;
7810}
7811/* }}} */
7812
7813static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
7814 EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
7815 zend_string **poutbuf, int *poutlen, const char *data, size_t data_len,
7816 const char *aad, size_t aad_len, int enc) /* {{{ */
7817{
7818 int i = 0;
7819
7820 if (mode->is_single_run_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
7822 php_error_docref(NULL, E_WARNING, "Setting of data length failed");
7823 return FAILURE;
7824 }
7825
7826 if (mode->is_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, (const unsigned char *) aad, (int) aad_len)) {
7828 php_error_docref(NULL, E_WARNING, "Setting of additional application data failed");
7829 return FAILURE;
7830 }
7831
7832 *poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
7833
7834 if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
7835 &i, (const unsigned char *)data, (int)data_len)) {
7836 /* we don't show warning when we fail but if we ever do, then it should look like this:
7837 if (mode->is_single_run_aead && !enc) {
7838 php_error_docref(NULL, E_WARNING, "Tag verifycation failed");
7839 } else {
7840 php_error_docref(NULL, E_WARNING, enc ? "Encryption failed" : "Decryption failed");
7841 }
7842 */
7844 zend_string_release_ex(*poutbuf, 0);
7845 return FAILURE;
7846 }
7847
7848 *poutlen = i;
7849
7850 return SUCCESS;
7851}
7852/* }}} */
7853
7854
7856 const char *data, size_t data_len,
7857 const char *method, size_t method_len,
7858 const char *password, size_t password_len,
7860 const char *iv, size_t iv_len,
7861 zval *tag, zend_long tag_len,
7862 const char *aad, size_t aad_len)
7863{
7864 const EVP_CIPHER *cipher_type;
7865 EVP_CIPHER_CTX *cipher_ctx;
7867 int i = 0, outlen;
7868 bool free_iv = 0, free_password = 0;
7869 zend_string *outbuf = NULL;
7870
7872 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(password_len, password);
7875
7876
7877 cipher_type = EVP_get_cipherbyname(method);
7878 if (!cipher_type) {
7879 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7880 return NULL;
7881 }
7882
7883 cipher_ctx = EVP_CIPHER_CTX_new();
7884 if (!cipher_ctx) {
7885 php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
7886 return NULL;
7887 }
7888
7889 php_openssl_load_cipher_mode(&mode, cipher_type);
7890
7891 if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
7892 &password, &password_len, &free_password,
7893 &iv, &iv_len, &free_iv, NULL, tag_len, options, 1) == FAILURE ||
7894 php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
7895 data, data_len, aad, aad_len, 1) == FAILURE) {
7896 outbuf = NULL;
7897 } else if (EVP_EncryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
7898 outlen += i;
7899 if (options & OPENSSL_RAW_DATA) {
7900 ZSTR_VAL(outbuf)[outlen] = '\0';
7901 ZSTR_LEN(outbuf) = outlen;
7902 } else {
7903 zend_string *base64_str;
7904
7905 base64_str = php_base64_encode((unsigned char*)ZSTR_VAL(outbuf), outlen);
7906 zend_string_release_ex(outbuf, 0);
7907 outbuf = base64_str;
7908 }
7909 if (mode.is_aead && tag) {
7910 zend_string *tag_str = zend_string_alloc(tag_len, 0);
7911
7912 if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode.aead_get_tag_flag, tag_len, ZSTR_VAL(tag_str)) == 1) {
7913 ZSTR_VAL(tag_str)[tag_len] = '\0';
7914 ZSTR_LEN(tag_str) = tag_len;
7915 ZEND_TRY_ASSIGN_REF_NEW_STR(tag, tag_str);
7916 } else {
7917 php_error_docref(NULL, E_WARNING, "Retrieving verification tag failed");
7918 zend_string_release_ex(tag_str, 0);
7919 zend_string_release_ex(outbuf, 0);
7920 outbuf = NULL;
7921 }
7922 } else if (tag) {
7924 } else if (mode.is_aead) {
7925 php_error_docref(NULL, E_WARNING, "A tag should be provided when using AEAD mode");
7926 zend_string_release_ex(outbuf, 0);
7927 outbuf = NULL;
7928 }
7929 } else {
7931 zend_string_release_ex(outbuf, 0);
7932 outbuf = NULL;
7933 }
7934
7935 if (free_password) {
7936 efree((void *) password);
7937 }
7938 if (free_iv) {
7939 efree((void *) iv);
7940 }
7941 EVP_CIPHER_CTX_reset(cipher_ctx);
7942 EVP_CIPHER_CTX_free(cipher_ctx);
7943 return outbuf;
7944}
7945
7946/* {{{ Encrypts given data with given method and key, returns raw or base64 encoded string */
7948{
7949 zend_long options = 0, tag_len = 16;
7950 char *data, *method, *password, *iv = "", *aad = "";
7951 size_t data_len, method_len, password_len, iv_len = 0, aad_len = 0;
7953 zval *tag = NULL;
7954
7955 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lszsl", &data, &data_len, &method, &method_len,
7956 &password, &password_len, &options, &iv, &iv_len, &tag, &aad, &aad_len, &tag_len) == FAILURE) {
7957 RETURN_THROWS();
7958 }
7959
7960 if ((ret = php_openssl_encrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
7961 RETVAL_STR(ret);
7962 } else {
7964 }
7965}
7966/* }}} */
7967
7969 const char *data, size_t data_len,
7970 const char *method, size_t method_len,
7971 const char *password, size_t password_len,
7973 const char *iv, size_t iv_len,
7974 const char *tag, zend_long tag_len,
7975 const char *aad, size_t aad_len)
7976{
7977 const EVP_CIPHER *cipher_type;
7978 EVP_CIPHER_CTX *cipher_ctx;
7980 int i = 0, outlen;
7981 zend_string *base64_str = NULL;
7982 bool free_iv = 0, free_password = 0;
7983 zend_string *outbuf = NULL;
7984
7986 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(password_len, password);
7989
7990
7991 cipher_type = EVP_get_cipherbyname(method);
7992 if (!cipher_type) {
7993 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7994 return NULL;
7995 }
7996
7997 cipher_ctx = EVP_CIPHER_CTX_new();
7998 if (!cipher_ctx) {
7999 php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
8000 return NULL;
8001 }
8002
8003 php_openssl_load_cipher_mode(&mode, cipher_type);
8004
8005 if (!(options & OPENSSL_RAW_DATA)) {
8006 base64_str = php_base64_decode((unsigned char*)data, data_len);
8007 if (!base64_str) {
8008 php_error_docref(NULL, E_WARNING, "Failed to base64 decode the input");
8009 EVP_CIPHER_CTX_free(cipher_ctx);
8010 return NULL;
8011 }
8012 data_len = ZSTR_LEN(base64_str);
8013 data = ZSTR_VAL(base64_str);
8014 }
8015
8016 if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
8017 &password, &password_len, &free_password,
8018 &iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE ||
8019 php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
8020 data, data_len, aad, aad_len, 0) == FAILURE) {
8021 outbuf = NULL;
8022 } else if (mode.is_single_run_aead ||
8023 EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
8024 outlen += i;
8025 ZSTR_VAL(outbuf)[outlen] = '\0';
8026 ZSTR_LEN(outbuf) = outlen;
8027 } else {
8029 zend_string_release_ex(outbuf, 0);
8030 outbuf = NULL;
8031 }
8032
8033 if (free_password) {
8034 efree((void *) password);
8035 }
8036 if (free_iv) {
8037 efree((void *) iv);
8038 }
8039 if (base64_str) {
8040 zend_string_release_ex(base64_str, 0);
8041 }
8042 EVP_CIPHER_CTX_reset(cipher_ctx);
8043 EVP_CIPHER_CTX_free(cipher_ctx);
8044 return outbuf;
8045}
8046
8047/* {{{ Takes raw or base64 encoded string and decrypts it using given method and key */
8049{
8050 zend_long options = 0;
8051 char *data, *method, *password, *iv = "", *tag = NULL, *aad = "";
8052 size_t data_len, method_len, password_len, iv_len = 0, tag_len = 0, aad_len = 0;
8054
8055 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lss!s", &data, &data_len, &method, &method_len,
8056 &password, &password_len, &options, &iv, &iv_len, &tag, &tag_len, &aad, &aad_len) == FAILURE) {
8057 RETURN_THROWS();
8058 }
8059
8060 if (!method_len) {
8062 RETURN_THROWS();
8063 }
8064
8065 if ((ret = php_openssl_decrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
8066 RETVAL_STR(ret);
8067 } else {
8069 }
8070}
8071/* }}} */
8072
8073static inline const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method)
8074{
8075 const EVP_CIPHER *cipher_type;
8076
8077 cipher_type = EVP_get_cipherbyname(method);
8078 if (!cipher_type) {
8079 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
8080 return NULL;
8081 }
8082
8083 return cipher_type;
8084}
8085
8086PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method)
8087{
8088 const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method);
8089
8090 return cipher_type == NULL ? -1 : EVP_CIPHER_iv_length(cipher_type);
8091}
8092
8094{
8095 zend_string *method;
8096 zend_long ret;
8097
8098 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &method) == FAILURE) {
8099 RETURN_THROWS();
8100 }
8101
8102 if (ZSTR_LEN(method) == 0) {
8104 RETURN_THROWS();
8105 }
8106
8107 /* Warning is emitted in php_openssl_cipher_iv_length */
8108 if ((ret = php_openssl_cipher_iv_length(ZSTR_VAL(method))) == -1) {
8110 }
8111
8113}
8114
8115PHP_OPENSSL_API zend_long php_openssl_cipher_key_length(const char *method)
8116{
8117 const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method);
8118
8119 return cipher_type == NULL ? -1 : EVP_CIPHER_key_length(cipher_type);
8120}
8121
8123{
8124 zend_string *method;
8125 zend_long ret;
8126
8127 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &method) == FAILURE) {
8128 RETURN_THROWS();
8129 }
8130
8131 if (ZSTR_LEN(method) == 0) {
8133 RETURN_THROWS();
8134 }
8135
8136 /* Warning is emitted in php_openssl_cipher_key_length */
8137 if ((ret = php_openssl_cipher_key_length(ZSTR_VAL(method))) == -1) {
8139 }
8140
8142}
8143
8145{
8147 if (buffer_length <= 0) {
8148 zend_argument_value_error(1, "must be greater than 0");
8149 return NULL;
8150 }
8151 if (ZEND_LONG_INT_OVFL(buffer_length)) {
8152 zend_argument_value_error(1, "must be less than or equal to %d", INT_MAX);
8153 return NULL;
8154 }
8155 buffer = zend_string_alloc(buffer_length, 0);
8156
8157 PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(buffer_length, length);
8159 if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), (int)buffer_length) <= 0) {
8162 zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
8163 return NULL;
8164 }
8165
8166 return buffer;
8167}
8168
8169/* {{{ Returns a string of the length specified filled with random pseudo bytes */
8171{
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 ((buffer = php_openssl_random_pseudo_bytes(buffer_length))) {
8181 ZSTR_VAL(buffer)[buffer_length] = 0;
8183
8184 if (zstrong_result_returned) {
8185 ZEND_TRY_ASSIGN_REF_TRUE(zstrong_result_returned);
8186 }
8187 } else if (zstrong_result_returned) {
8188 ZEND_TRY_ASSIGN_REF_FALSE(zstrong_result_returned);
8189 }
8190}
8191/* }}} */
ary($n)
Definition bench.php:112
size_t len
Definition apprentice.c:174
bool exception
Definition assert.c:30
getenv(?string $name=null, bool $local_only=false)
file(string $filename, int $flags=0, $context=null)
is_file(string $filename)
gettimeofday(bool $as_float=false)
char s[4]
Definition cdf.c:77
uint32_t v
Definition cdf.c:1237
zend_ffi_type * type
Definition ffi.c:3812
zval * zv
Definition ffi.c:3975
zend_long n
Definition ffi.c:4979
new_type size
Definition ffi.c:4365
zend_string * res
Definition ffi.c:4692
memcpy(ptr1, ptr2, size)
char * err
Definition ffi.c:3029
zval * arg
Definition ffi.c:3975
memset(ptr, 0, type->size)
zval * val
Definition ffi.c:4262
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
PHPAPI int php_check_open_basedir(const char *path)
PHPAPI char * expand_filepath(const char *filepath, char *real_path)
PHPAPI const php_stream_wrapper php_stream_ftp_wrapper
char * mode
size_t filename_len
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
#define pass(a, b, c, mul)
Definition hash_tiger.c:50
PHPAPI const php_stream_wrapper php_stream_http_wrapper
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int type, const char *format, va_list args)
Definition main.c:992
PHPAPI void make_digest_ex(char *md5str, const unsigned char *digest, int len)
Definition md5.c:28
#define EVP_PKEY_get0_DSA(_pkey)
Definition openssl.c:312
php_openssl_key_type
Definition openssl.c:118
@ OPENSSL_KEYTYPE_DEFAULT
Definition openssl.c:122
@ OPENSSL_KEYTYPE_DSA
Definition openssl.c:120
@ OPENSSL_KEYTYPE_RSA
Definition openssl.c:119
@ OPENSSL_KEYTYPE_DH
Definition openssl.c:121
int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
Definition openssl.c:413
PHP_OPENSSL_API zend_string * php_openssl_encrypt(const char *data, size_t data_len, const char *method, size_t method_len, const char *password, size_t password_len, zend_long options, const char *iv, size_t iv_len, zval *tag, zend_long tag_len, const char *aad, size_t aad_len)
Definition openssl.c:7855
#define OPENSSL_ALGO_MD5
Definition openssl.c:80
#define PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(_var, _name)
Definition openssl.c:467
#define OPENSSL_ALGO_RMD160
Definition openssl.c:95
#define EVP_PKEY_get0_RSA(_pkey)
Definition openssl.c:310
#define EVP_CIPHER_CTX_reset
Definition openssl.c:444
#define PHP_OPENSSL_CHECK_LONG_TO_INT(_var, _name, _arg_num)
Definition openssl.c:473
PHP_OPENSSL_API zend_string * php_openssl_random_pseudo_bytes(zend_long buffer_length)
Definition openssl.c:8144
#define PHP_OPENSSL_ASN1_INTEGER_set
Definition openssl.c:3227
#define SET_OPTIONAL_STRING_ARG(key, varname, defval)
Definition openssl.c:914
#define PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(_var, _name, _arg_num)
Definition openssl.c:470
#define X509_getm_notAfter
Definition openssl.c:443
#define SET_OPTIONAL_LONG_ARG(key, varname, defval)
Definition openssl.c:926
#define OPENSSL_ALGO_SHA256
Definition openssl.c:91
#define OPENSSL_ALGO_MD4
Definition openssl.c:82
zend_string * php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw)
Definition openssl.c:1896
PHP_OPENSSL_API zend_long php_openssl_cipher_key_length(const char *method)
Definition openssl.c:8115
#define OPENSSL_VERSION
Definition openssl.c:441
#define OPENSSL_ALGO_DSS1
Definition openssl.c:88
struct _php_openssl_x509_request_object php_openssl_request_object
#define X509_getm_notBefore
Definition openssl.c:442
#define Z_OPENSSL_REQUEST_P(zv)
Definition openssl.c:200
void php_openssl_store_errors(void)
Definition openssl.c:479
#define OPENSSL_ALGO_SHA384
Definition openssl.c:92
#define PHP_SSL_REQ_INIT(req)
Definition openssl.c:907
php_openssl_cipher_type
Definition openssl.c:134
@ PHP_OPENSSL_CIPHER_RC2_40
Definition openssl.c:135
@ PHP_OPENSSL_CIPHER_DEFAULT
Definition openssl.c:144
@ PHP_OPENSSL_CIPHER_AES_128_CBC
Definition openssl.c:140
@ PHP_OPENSSL_CIPHER_DES
Definition openssl.c:138
@ PHP_OPENSSL_CIPHER_AES_192_CBC
Definition openssl.c:141
@ PHP_OPENSSL_CIPHER_AES_256_CBC
Definition openssl.c:142
@ PHP_OPENSSL_CIPHER_RC2_128
Definition openssl.c:136
@ PHP_OPENSSL_CIPHER_3DES
Definition openssl.c:139
@ PHP_OPENSSL_CIPHER_RC2_64
Definition openssl.c:137
php_stream * php_openssl_get_stream_from_ssl_handle(const SSL *ssl)
Definition openssl.c:613
zend_module_entry openssl_module_entry
Definition openssl.c:280
#define OPENSSL_PKEY_GET_BN(_type, _name)
Definition openssl.c:3925
#define EVP_PKEY_get0_DH(_pkey)
Definition openssl.c:311
#define OPENSSL_ALGO_SHA224
Definition openssl.c:90
int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
Definition openssl.c:398
PHP_OPENSSL_API zend_string * php_openssl_decrypt(const char *data, size_t data_len, const char *method, size_t method_len, const char *password, size_t password_len, zend_long options, const char *iv, size_t iv_len, const char *tag, zend_long tag_len, const char *aad, size_t aad_len)
Definition openssl.c:7968
#define PHP_SSL_REQ_PARSE(req, zval)
Definition openssl.c:909
struct _php_openssl_pkey_object php_openssl_pkey_object
#define OPENSSL_ALGO_MD2
Definition openssl.c:85
#define Z_OPENSSL_PKEY_P(zv)
Definition openssl.c:240
#define EVP_PKEY_get0_EC_KEY(_pkey)
Definition openssl.c:313
#define OpenSSL_version
Definition openssl.c:440
#define OPENSSL_PKEY_SET_BN(_data, _name)
Definition openssl.c:3927
#define OPENSSL_ALGO_SHA1
Definition openssl.c:79
#define PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(_var, _name)
Definition openssl.c:475
#define OPENSSL_ALGO_SHA512
Definition openssl.c:93
#define MIN_KEY_LENGTH
Definition openssl.c:76
zend_class_entry * php_openssl_certificate_ce
Definition openssl.c:161
#define PHP_SSL_CONFIG_SYNTAX_CHECK(var)
Definition openssl.c:911
#define PHP_SSL_REQ_DISPOSE(req)
Definition openssl.c:908
PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method)
Definition openssl.c:8086
#define PHP_OPENSSL_RAND_ADD_TIME()
Definition openssl.c:1086
#define TMP_CLEAN
#define PHP_OPENSSL_CHECK_SIZE_T_TO_INT(_var, _name, _arg_num)
Definition openssl.c:465
php_openssl_encoding
Definition openssl.c:151
@ ENCODING_SMIME
Definition openssl.c:153
@ ENCODING_DER
Definition openssl.c:152
@ ENCODING_PEM
Definition openssl.c:154
int php_openssl_get_ssl_stream_data_index(void)
Definition openssl.c:618
bool 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)
Definition openssl.c:554
const OPENSSL_DONT_ZERO_PAD_KEY
openssl_sign(string $data, &$signature, #[\SensitiveParameter] $private_key, string|int $algorithm=OPENSSL_ALGO_SHA1)
openssl_pkey_free(OpenSSLAsymmetricKey $key)
openssl_x509_export(OpenSSLCertificate|string $certificate, &$output, bool $no_text=true)
openssl_csr_export_to_file(OpenSSLCertificateSigningRequest|string $csr, string $output_filename, bool $no_text=true)
openssl_pkcs12_read(string $pkcs12, &$certificates, #[\SensitiveParameter] string $passphrase)
openssl_dh_compute_key(string $public_key, #[\SensitiveParameter] OpenSSLAsymmetricKey $private_key)
const OPENSSL_ZERO_PADDING
openssl_x509_check_private_key(OpenSSLCertificate|string $certificate, #[\SensitiveParameter] $private_key)
openssl_x509_free(OpenSSLCertificate $certificate)
openssl_get_curve_names()
openssl_seal(#[\SensitiveParameter] string $data, &$sealed_data, &$encrypted_keys, array $public_key, string $cipher_algo, &$iv=null)
openssl_pkcs7_decrypt(string $input_filename, string $output_filename, #[\SensitiveParameter] $certificate, #[\SensitiveParameter] $private_key=null)
openssl_x509_parse(OpenSSLCertificate|string $certificate, bool $short_names=true)
openssl_pkey_derive($public_key, #[\SensitiveParameter] $private_key, int $key_length=0)
openssl_spki_new(#[\SensitiveParameter] OpenSSLAsymmetricKey $private_key, string $challenge, int $digest_algo=OPENSSL_ALGO_MD5)
openssl_pkcs12_export_to_file(OpenSSLCertificate|string $certificate, string $output_filename, #[\SensitiveParameter] $private_key, #[\SensitiveParameter] string $passphrase, array $options=[])
openssl_pkey_get_details(OpenSSLAsymmetricKey $key)
openssl_cms_read(string $input_filename, &$certificates)
openssl_pkey_export_to_file(#[\SensitiveParameter] $key, string $output_filename, #[\SensitiveParameter] ?string $passphrase=null, ?array $options=null)
openssl_pkcs12_export(OpenSSLCertificate|string $certificate, &$output, #[\SensitiveParameter] $private_key, #[\SensitiveParameter] string $passphrase, array $options=[])
openssl_error_string()
openssl_csr_export(OpenSSLCertificateSigningRequest|string $csr, &$output, bool $no_text=true)
openssl_csr_get_public_key(OpenSSLCertificateSigningRequest|string $csr, bool $short_names=true)
openssl_spki_export(string $spki)
openssl_digest(string $data, string $digest_algo, bool $binary=false)
const OPENSSL_KEYTYPE_EC
const PKCS7_DETACHED
const OPENSSL_RAW_DATA
openssl_x509_verify(OpenSSLCertificate|string $certificate, $public_key)
openssl_pkcs7_encrypt(string $input_filename, string $output_filename, $certificate, ?array $headers, int $flags=0, int $cipher_algo=OPENSSL_CIPHER_AES_128_CBC)
openssl_get_cert_locations()
openssl_x509_checkpurpose(OpenSSLCertificate|string $certificate, int $purpose, array $ca_info=[], ?string $untrusted_certificates_file=null)
openssl_pkey_get_public($public_key)
openssl_cms_sign(string $input_filename, string $output_filename, OpenSSLCertificate|string $certificate, #[\SensitiveParameter] $private_key, ?array $headers, int $flags=0, int $encoding=OPENSSL_ENCODING_SMIME, ?string $untrusted_certificates_filename=null)
openssl_get_md_methods(bool $aliases=false)
openssl_private_encrypt(#[\SensitiveParameter] string $data, &$encrypted_data, #[\SensitiveParameter] $private_key, int $padding=OPENSSL_PKCS1_PADDING)
const OPENSSL_VERSION_TEXT
openssl_x509_fingerprint(OpenSSLCertificate|string $certificate, string $digest_algo="sha1", bool $binary=false)
openssl_spki_verify(string $spki)
openssl_csr_get_subject(OpenSSLCertificateSigningRequest|string $csr, bool $short_names=true)
openssl_random_pseudo_bytes(int $length, &$strong_result=null)
openssl_public_encrypt(#[\SensitiveParameter] string $data, &$encrypted_data, $public_key, int $padding=OPENSSL_PKCS1_PADDING)
openssl_pbkdf2(#[\SensitiveParameter] string $password, string $salt, int $key_length, int $iterations, string $digest_algo="sha1")
const OPENSSL_KEYTYPE_X448
openssl_spki_export_challenge(string $spki)
openssl_verify(string $data, string $signature, $public_key, string|int $algorithm=OPENSSL_ALGO_SHA1)
openssl_cipher_key_length(string $cipher_algo)
openssl_private_decrypt(string $data, #[\SensitiveParameter] &$decrypted_data, #[\SensitiveParameter] $private_key, int $padding=OPENSSL_PKCS1_PADDING)
openssl_encrypt(#[\SensitiveParameter] string $data, string $cipher_algo, #[\SensitiveParameter] string $passphrase, int $options=0, string $iv="", &$tag=null, string $aad="", int $tag_length=16)
openssl_decrypt(string $data, string $cipher_algo, #[\SensitiveParameter] string $passphrase, int $options=0, string $iv="", ?string $tag=null, string $aad="")
const OPENSSL_KEYTYPE_ED448
openssl_open(string $data, #[\SensitiveParameter] &$output, string $encrypted_key, #[\SensitiveParameter] $private_key, string $cipher_algo, ?string $iv=null)
openssl_csr_sign(OpenSSLCertificateSigningRequest|string $csr, OpenSSLCertificate|string|null $ca_certificate, #[\SensitiveParameter] $private_key, int $days, ?array $options=null, int $serial=0, ?string $serial_hex=null)
openssl_cms_decrypt(string $input_filename, string $output_filename, #[\SensitiveParameter] $certificate, #[\SensitiveParameter] $private_key=null, int $encoding=OPENSSL_ENCODING_SMIME)
openssl_cms_verify(string $input_filename, int $flags=0, ?string $certificates=null, array $ca_info=[], ?string $untrusted_certificates_filename=null, ?string $content=null, ?string $pk7=null, ?string $sigfile=null, int $encoding=OPENSSL_ENCODING_SMIME)
openssl_public_decrypt(string $data, #[\SensitiveParameter] &$decrypted_data, $public_key, int $padding=OPENSSL_PKCS1_PADDING)
openssl_cipher_iv_length(string $cipher_algo)
const OPENSSL_KEYTYPE_ED25519
openssl_pkey_new(?array $options=null)
openssl_pkey_get_private(#[\SensitiveParameter] $private_key, #[\SensitiveParameter] ?string $passphrase=null)
openssl_get_cipher_methods(bool $aliases=false)
openssl_x509_export_to_file(OpenSSLCertificate|string $certificate, string $output_filename, bool $no_text=true)
const OPENSSL_KEYTYPE_X25519
openssl_pkey_export(#[\SensitiveParameter] $key, &$output, #[\SensitiveParameter] ?string $passphrase=null, ?array $options=null)
const PKCS7_BINARY
openssl_x509_read(OpenSSLCertificate|string $certificate)
openssl_csr_new(array $distinguished_names, #[\SensitiveParameter] &$private_key, ?array $options=null, ?array $extra_attributes=null)
openssl_cms_encrypt(string $input_filename, string $output_filename, $certificate, ?array $headers, int $flags=0, int $encoding=OPENSSL_ENCODING_SMIME, int $cipher_algo=OPENSSL_CIPHER_AES_128_CBC)
openssl_pkcs7_verify(string $input_filename, int $flags, ?string $signers_certificates_filename=null, array $ca_info=[], ?string $untrusted_certificates_filename=null, ?string $content=null, ?string $output_filename=null)
openssl_pkcs7_sign(string $input_filename, string $output_filename, OpenSSLCertificate|string $certificate, #[\SensitiveParameter] $private_key, ?array $headers, int $flags=PKCS7_DETACHED, ?string $untrusted_certificates_filename=null)
openssl_pkcs7_read(string $data, &$certificates)
php_info_print_table_start()
Definition info.c:1064
php_info_print_table_row(2, "PDO Driver for Firebird", "enabled")
php_info_print_table_end()
Definition info.c:1074
#define PHP_GINIT
Definition php.h:397
#define PHP_FUNCTION
Definition php.h:364
#define PHP_MSHUTDOWN_FUNCTION
Definition php.h:401
#define PHP_MINFO
Definition php.h:396
#define PHP_MINIT_FUNCTION
Definition php.h:400
#define strlcpy
Definition php.h:159
#define PHP_MSHUTDOWN
Definition php.h:393
#define PHP_MINFO_FUNCTION
Definition php.h:404
#define PHP_GINIT_FUNCTION
Definition php.h:405
#define INT_MAX
Definition php.h:237
#define PHP_GSHUTDOWN_FUNCTION
Definition php.h:406
#define PHP_MINIT
Definition php.h:392
#define PHP_MODULE_GLOBALS
Definition php.h:408
#define PHP_GSHUTDOWN
Definition php.h:398
mktime(int $hour, ?int $minute=null, ?int $second=null, ?int $month=null, ?int $day=null, ?int $year=null)
unsigned const char * end
Definition php_ffi.h:51
#define PHP_INI_PERDIR
Definition php_ini.h:42
#define PHP_INI_BEGIN
Definition php_ini.h:52
#define PHP_INI_ENTRY
Definition php_ini.h:62
#define PHP_INI_END
Definition php_ini.h:53
PHP_JSON_API size_t int options
Definition php_json.h:102
php_json_error_code error_code
Definition php_json.h:92
char * error_msg
unsigned char key[REFLECTION_KEY_LEN]
xmlCharEncodingHandlerPtr encoding
Definition php_soap.h:170
PHPAPI php_stream_transport_factory_func php_stream_generic_socket_factory
PHPAPI int php_stream_xport_register(const char *protocol, php_stream_transport_factory factory)
Definition transports.c:28
PHPAPI int php_stream_xport_unregister(const char *protocol)
Definition transports.c:37
struct _php_stream php_stream
Definition php_streams.h:96
PHPAPI zend_result php_register_url_stream_wrapper(const char *protocol, const php_stream_wrapper *wrapper)
Definition streams.c:1911
PHPAPI zend_result php_unregister_url_stream_wrapper(const char *protocol)
Definition streams.c:1927
zend_constant * data
original_stack top
struct timeval tv
Definition session.c:1280
p
Definition session.c:1105
zval val
Definition zend_types.h:381
Definition file.h:177
bool set_tag_length_when_encrypting
Definition openssl.c:7622
CONF * req_config
Definition openssl.c:629
char * section_name
Definition openssl.c:632
const EVP_CIPHER * priv_key_encrypt_cipher
Definition openssl.c:648
CONF * global_config
Definition openssl.c:628
EVP_PKEY * priv_key
Definition openssl.c:646
char * request_extensions_section
Definition openssl.c:636
const EVP_MD * digest
Definition openssl.c:631
char * digest_name
Definition openssl.c:634
char * extensions_section
Definition openssl.c:635
const EVP_MD * md_alg
Definition openssl.c:630
char * config_filename
Definition openssl.c:633
$obj a
Definition test.php:84
php_stream * php_openssl_ssl_socket_factory(const char *proto, size_t protolen, const char *resourcename, size_t resourcenamelen, const char *persistent_id, int options, int flags, struct timeval *timeout, php_stream_context *context STREAMS_DC)
Definition xp_ssl.c:2770
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
Definition zend.c:1772
ZEND_API ZEND_COLD void zend_value_error(const char *format,...)
Definition zend.c:1849
ZEND_API size_t(* zend_printf)(const char *format,...)
Definition zend.c:84
#define ZEND_TSRMLS_CACHE_UPDATE()
Definition zend.h:69
ZEND_API zend_result add_next_index_stringl(zval *arg, const char *str, size_t length)
Definition zend_API.c:2195
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type)
Definition zend_API.c:1849
ZEND_API void add_index_string(zval *arg, zend_ulong index, const char *str)
Definition zend_API.c:2087
ZEND_API zend_result zend_parse_parameters(uint32_t num_args, const char *type_spec,...)
Definition zend_API.c:1300
ZEND_API void add_index_bool(zval *arg, zend_ulong index, bool b)
Definition zend_API.c:2051
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, uint32_t arg_num, const char *format, va_list va)
Definition zend_API.c:391
ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type)
Definition zend_API.c:1688
ZEND_API ZEND_COLD void zend_argument_must_not_be_empty_error(uint32_t arg_num)
Definition zend_API.c:443
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
ZEND_API zend_result add_next_index_string(zval *arg, const char *str)
Definition zend_API.c:2186
ZEND_API zend_result add_next_index_str(zval *arg, zend_string *str)
Definition zend_API.c:2177
#define ZEND_TRY_ASSIGN_REF_FALSE(zv)
Definition zend_API.h:1139
#define CHECK_NULL_PATH(p, l)
Definition zend_API.h:950
#define ZEND_NUM_ARGS()
Definition zend_API.h:530
#define Z_PARAM_OBJ_OF_CLASS_OR_STR(destination_object, base_ce, destination_string)
Definition zend_API.h:1788
#define Z_PARAM_PATH_OR_NULL(dest, dest_len)
Definition zend_API.h:2029
#define Z_PARAM_ARRAY_OR_NULL(dest)
Definition zend_API.h:1685
#define ZEND_TRY_ASSIGN_REF_STRINGL(zv, string, len)
Definition zend_API.h:1337
#define RETURN_STRING(s)
Definition zend_API.h:1043
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define RETVAL_STRING(s)
Definition zend_API.h:1017
#define Z_PARAM_STR_OR_NULL(dest)
Definition zend_API.h:2089
#define ZEND_DECLARE_MODULE_GLOBALS(module_name)
Definition zend_API.h:268
#define ZEND_TRY_ASSIGN_REF_TMP(zv, other_zv)
Definition zend_API.h:1403
#define RETVAL_NEW_STR(s)
Definition zend_API.h:1015
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define ZEND_GET_MODULE(name)
Definition zend_API.h:241
#define zend_parse_parameters_none()
Definition zend_API.h:353
#define Z_PARAM_STRING(dest, dest_len)
Definition zend_API.h:2071
#define Z_PARAM_STRING_OR_NULL(dest, dest_len)
Definition zend_API.h:2074
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define ZEND_TRY_ASSIGN_REF_NULL(zv)
Definition zend_API.h:1117
#define Z_PARAM_LONG(dest)
Definition zend_API.h:1896
#define RETURN_LONG(l)
Definition zend_API.h:1037
#define RETURN_NEW_STR(s)
Definition zend_API.h:1041
#define RETURN_THROWS()
Definition zend_API.h:1060
#define Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(destination_object, base_ce, destination_string)
Definition zend_API.h:1791
#define RETVAL_TRUE
Definition zend_API.h:1033
#define ZEND_TRY_ASSIGN_REF_TRUE(zv)
Definition zend_API.h:1161
#define RETVAL_BOOL(b)
Definition zend_API.h:1009
#define ZEND_TRY_ASSIGN_REF_NEW_STR(zv, str)
Definition zend_API.h:1293
#define Z_PARAM_STR_OR_LONG(dest_str, dest_long)
Definition zend_API.h:2165
#define RETVAL_LONG(l)
Definition zend_API.h:1011
#define Z_PARAM_BOOL(dest)
Definition zend_API.h:1726
#define Z_PARAM_OBJECT_OF_CLASS(dest, _ce)
Definition zend_API.h:1976
#define Z_PARAM_PATH(dest, dest_len)
Definition zend_API.h:2026
#define Z_PARAM_ARRAY(dest)
Definition zend_API.h:1682
#define Z_PARAM_ZVAL_OR_NULL(dest)
Definition zend_API.h:2103
#define Z_PARAM_ZVAL(dest)
Definition zend_API.h:2100
#define RETVAL_STR(s)
Definition zend_API.h:1013
#define ZVAL_STRINGL(z, s, l)
Definition zend_API.h:952
#define RETVAL_FALSE
Definition zend_API.h:1032
#define RETVAL_STRINGL(s, l)
Definition zend_API.h:1018
#define array_init(arg)
Definition zend_API.h:537
#define ecalloc(nmemb, size)
Definition zend_alloc.h:158
#define efree(ptr)
Definition zend_alloc.h:155
#define estrdup(s)
Definition zend_alloc.h:164
#define pefree(ptr, persistent)
Definition zend_alloc.h:191
#define safe_emalloc(nmemb, size, offset)
Definition zend_alloc.h:154
#define pecalloc(nmemb, size, persistent)
Definition zend_alloc.h:200
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
strcmp(string $string1, string $string2)
zend_string_release_ex(func->internal_function.function_name, 0)
zval * args
#define snprintf
#define E_ERROR
Definition zend_errors.h:23
#define E_WARNING
Definition zend_errors.h:24
ZEND_API zend_class_entry * zend_ce_exception
ZEND_API zend_class_entry * zend_ce_value_error
ZEND_API ZEND_COLD zend_object * zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code)
ZEND_API void(ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data)
ZEND_API const char * get_active_function_arg_name(uint32_t arg_num)
union _zend_function zend_function
#define EG(v)
ZEND_API zval *ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
Definition zend_hash.c:2689
ZEND_API zval *ZEND_FASTCALL zend_hash_str_update(HashTable *ht, const char *str, size_t len, zval *pData)
Definition zend_hash.c:1031
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht)
Definition zend_hash.c:1808
ZEND_API zval *ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h)
Definition zend_hash.c:2701
#define ZEND_HASH_FOREACH_NUM_KEY_VAL(ht, _h, _val)
Definition zend_hash.h:1156
#define ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _key, _val)
Definition zend_hash.h:1166
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
#define ZEND_HASH_FOREACH_VAL(ht, _val)
Definition zend_hash.h:1102
ZEND_API char * zend_ini_string(const char *name, size_t name_length, int orig)
Definition zend_ini.c:505
#define UNREGISTER_INI_ENTRIES()
Definition zend_ini.h:204
#define REGISTER_INI_ENTRIES()
Definition zend_ini.h:203
#define DISPLAY_INI_ENTRIES()
Definition zend_ini.h:205
int32_t zend_long
Definition zend_long.h:42
#define Z_L(i)
Definition zend_long.h:48
struct _zend_string zend_string
#define STANDARD_MODULE_HEADER
#define INIT_FUNC_ARGS_PASSTHRU
#define ZEND_MOD_END
struct _zend_module_dep zend_module_dep
struct _zend_module_entry zend_module_entry
#define ZEND_MOD_REQUIRED(name)
#define STANDARD_MODULE_PROPERTIES_EX
#define STANDARD_MODULE_HEADER_EX
ZEND_API int zend_objects_not_comparable(zval *o1, zval *o2)
ZEND_API const zend_object_handlers std_object_handlers
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
ZEND_API void zend_object_std_dtor(zend_object *object)
ZEND_API char *ZEND_FASTCALL zend_str_tolower_copy(char *dest, const char *source, size_t length)
ZEND_API int ZEND_FASTCALL string_compare_function(zval *op1, zval *op2)
#define LONG_MAX
#define ZEND_THREEWAY_COMPARE(a, b)
#define XtOffsetOf(s_type, field)
#define ZEND_ASSERT(c)
#define EMPTY_SWITCH_DEFAULT_CASE()
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
#define ZEND_LONG_INT_OVFL(zlong)
struct stat zend_stat_t
Definition zend_stream.h:94
ZEND_API zend_string * zend_string_concat2(const char *str1, size_t str1_len, const char *str2, size_t str2_len)
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define IS_TRUE
Definition zend_types.h:603
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_NULL(z)
#define ZVAL_DEREF(z)
#define IS_STRING
Definition zend_types.h:606
struct _zend_array HashTable
Definition zend_types.h:386
#define Z_OBJ_P(zval_p)
Definition zend_types.h:990
#define IS_ARRAY
Definition zend_types.h:607
#define Z_STR_P(zval_p)
Definition zend_types.h:972
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
#define IS_NULL
Definition zend_types.h:601
#define Z_OBJCE_P(zval_p)
#define Z_STRVAL(zval)
Definition zend_types.h:974
@ FAILURE
Definition zend_types.h:61
#define Z_STRLEN(zval)
Definition zend_types.h:977
#define IS_OBJECT
Definition zend_types.h:608
#define IS_LONG
Definition zend_types.h:604
#define ZVAL_COPY(z, v)
struct _Bucket Bucket
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
struct _zend_object_handlers zend_object_handlers
Definition zend_types.h:88
#define Z_TYPE(zval)
Definition zend_types.h:659
#define Z_ARRVAL(zval)
Definition zend_types.h:986
#define Z_ARR_P(zval_p)
Definition zend_types.h:984
#define Z_LVAL_P(zval_p)
Definition zend_types.h:966
#define ZVAL_COPY_VALUE(z, v)
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
#define MAXPATHLEN
#define VCWD_STAT(path, buff)
zval * return_value
uint32_t arg_num
zend_string * name
bool result
zval * ret
out($f, $s)