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