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