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