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