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