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