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