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 zend_string *str = zval_try_get_string(val);
1461 if (str == NULL) {
1462 return NULL;
1463 }
1464 X509 *cert = php_openssl_x509_from_str(str, arg_num, is_from_array, option_name);
1465 zend_string_release(str);
1466 return cert;
1467 }
1468 /* }}} */
1469
1470 /* {{{ Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export_to_file)1471 PHP_FUNCTION(openssl_x509_export_to_file)
1472 {
1473 X509 *cert;
1474 zend_object *cert_obj;
1475 zend_string *cert_str;
1476
1477 bool notext = 1;
1478 BIO * bio_out;
1479 char * filename, file_path[MAXPATHLEN];
1480 size_t filename_len;
1481
1482 ZEND_PARSE_PARAMETERS_START(2, 3)
1483 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
1484 Z_PARAM_PATH(filename, filename_len)
1485 Z_PARAM_OPTIONAL
1486 Z_PARAM_BOOL(notext)
1487 ZEND_PARSE_PARAMETERS_END();
1488
1489 RETVAL_FALSE;
1490
1491 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1492 if (cert == NULL) {
1493 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
1494 return;
1495 }
1496
1497 if (!php_openssl_check_path(filename, filename_len, file_path, 2)) {
1498 goto exit_cleanup_cert;
1499 }
1500
1501 bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
1502 if (bio_out) {
1503 if (!notext && !X509_print(bio_out, cert)) {
1504 php_openssl_store_errors();
1505 }
1506 if (!PEM_write_bio_X509(bio_out, cert)) {
1507 php_openssl_store_errors();
1508 }
1509
1510 RETVAL_TRUE;
1511 } else {
1512 php_openssl_store_errors();
1513 php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
1514 }
1515
1516 if (!BIO_free(bio_out)) {
1517 php_openssl_store_errors();
1518 }
1519
1520 exit_cleanup_cert:
1521 if (cert_str) {
1522 X509_free(cert);
1523 }
1524 }
1525 /* }}} */
1526
1527 /* {{{ Creates new private key (or uses existing) and creates a new spki cert
1528 outputting results to var */
PHP_FUNCTION(openssl_spki_new)1529 PHP_FUNCTION(openssl_spki_new)
1530 {
1531 size_t challenge_len;
1532 char * challenge = NULL, *spkstr = NULL;
1533 zend_string * s = NULL;
1534 const char *spkac = "SPKAC=";
1535 zend_long algo = OPENSSL_ALGO_MD5;
1536
1537 zval *zpkey = NULL;
1538 EVP_PKEY *pkey = NULL;
1539 NETSCAPE_SPKI *spki=NULL;
1540 const EVP_MD *mdtype;
1541
1542 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|l", &zpkey, php_openssl_pkey_ce, &challenge, &challenge_len, &algo) == FAILURE) {
1543 RETURN_THROWS();
1544 }
1545 RETVAL_FALSE;
1546
1547 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(challenge_len, challenge, 2);
1548
1549 pkey = php_openssl_pkey_from_zval(zpkey, 0, challenge, challenge_len, 1);
1550 if (pkey == NULL) {
1551 if (!EG(exception)) {
1552 php_error_docref(NULL, E_WARNING, "Unable to use supplied private key");
1553 }
1554 goto cleanup;
1555 }
1556
1557 mdtype = php_openssl_get_evp_md_from_algo(algo);
1558
1559 if (!mdtype) {
1560 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
1561 goto cleanup;
1562 }
1563
1564 if ((spki = NETSCAPE_SPKI_new()) == NULL) {
1565 php_openssl_store_errors();
1566 php_error_docref(NULL, E_WARNING, "Unable to create new SPKAC");
1567 goto cleanup;
1568 }
1569
1570 if (challenge) {
1571 if (!ASN1_STRING_set(spki->spkac->challenge, challenge, (int)challenge_len)) {
1572 php_openssl_store_errors();
1573 php_error_docref(NULL, E_WARNING, "Unable to set challenge data");
1574 goto cleanup;
1575 }
1576 }
1577
1578 if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) {
1579 php_openssl_store_errors();
1580 php_error_docref(NULL, E_WARNING, "Unable to embed public key");
1581 goto cleanup;
1582 }
1583
1584 if (!NETSCAPE_SPKI_sign(spki, pkey, mdtype)) {
1585 php_openssl_store_errors();
1586 php_error_docref(NULL, E_WARNING, "Unable to sign with specified digest algorithm");
1587 goto cleanup;
1588 }
1589
1590 spkstr = NETSCAPE_SPKI_b64_encode(spki);
1591 if (!spkstr){
1592 php_openssl_store_errors();
1593 php_error_docref(NULL, E_WARNING, "Unable to encode SPKAC");
1594 goto cleanup;
1595 }
1596
1597 s = zend_string_alloc(strlen(spkac) + strlen(spkstr), 0);
1598 sprintf(ZSTR_VAL(s), "%s%s", spkac, spkstr);
1599 ZSTR_LEN(s) = strlen(ZSTR_VAL(s));
1600 OPENSSL_free(spkstr);
1601
1602 RETVAL_STR(s);
1603 goto cleanup;
1604
1605 cleanup:
1606 EVP_PKEY_free(pkey);
1607 if (spki != NULL) {
1608 NETSCAPE_SPKI_free(spki);
1609 }
1610
1611 if (s && ZSTR_LEN(s) <= 0) {
1612 RETVAL_FALSE;
1613 }
1614 }
1615 /* }}} */
1616
1617 /* {{{ Verifies spki returns boolean */
PHP_FUNCTION(openssl_spki_verify)1618 PHP_FUNCTION(openssl_spki_verify)
1619 {
1620 size_t spkstr_len;
1621 int i = 0, spkstr_cleaned_len = 0;
1622 char *spkstr, * spkstr_cleaned = NULL;
1623
1624 EVP_PKEY *pkey = NULL;
1625 NETSCAPE_SPKI *spki = NULL;
1626
1627 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
1628 RETURN_THROWS();
1629 }
1630 RETVAL_FALSE;
1631
1632 spkstr_cleaned = emalloc(spkstr_len + 1);
1633 spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
1634
1635 if (spkstr_cleaned_len == 0) {
1636 php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
1637 goto cleanup;
1638 }
1639
1640 spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
1641 if (spki == NULL) {
1642 php_openssl_store_errors();
1643 php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
1644 goto cleanup;
1645 }
1646
1647 pkey = X509_PUBKEY_get(spki->spkac->pubkey);
1648 if (pkey == NULL) {
1649 php_openssl_store_errors();
1650 php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
1651 goto cleanup;
1652 }
1653
1654 i = NETSCAPE_SPKI_verify(spki, pkey);
1655 goto cleanup;
1656
1657 cleanup:
1658 if (spki != NULL) {
1659 NETSCAPE_SPKI_free(spki);
1660 }
1661 EVP_PKEY_free(pkey);
1662 if (spkstr_cleaned != NULL) {
1663 efree(spkstr_cleaned);
1664 }
1665
1666 if (i > 0) {
1667 RETVAL_TRUE;
1668 } else {
1669 php_openssl_store_errors();
1670 }
1671 }
1672 /* }}} */
1673
1674 /* {{{ Exports public key from existing spki to var */
PHP_FUNCTION(openssl_spki_export)1675 PHP_FUNCTION(openssl_spki_export)
1676 {
1677 size_t spkstr_len;
1678 char *spkstr, * spkstr_cleaned = NULL, * s = NULL;
1679 int spkstr_cleaned_len;
1680
1681 EVP_PKEY *pkey = NULL;
1682 NETSCAPE_SPKI *spki = NULL;
1683 BIO *out = NULL;
1684
1685 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
1686 RETURN_THROWS();
1687 }
1688 RETVAL_FALSE;
1689
1690 spkstr_cleaned = emalloc(spkstr_len + 1);
1691 spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
1692
1693 if (spkstr_cleaned_len == 0) {
1694 php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
1695 goto cleanup;
1696 }
1697
1698 spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
1699 if (spki == NULL) {
1700 php_openssl_store_errors();
1701 php_error_docref(NULL, E_WARNING, "Unable to decode supplied SPKAC");
1702 goto cleanup;
1703 }
1704
1705 pkey = X509_PUBKEY_get(spki->spkac->pubkey);
1706 if (pkey == NULL) {
1707 php_openssl_store_errors();
1708 php_error_docref(NULL, E_WARNING, "Unable to acquire signed public key");
1709 goto cleanup;
1710 }
1711
1712 out = BIO_new(BIO_s_mem());
1713 if (out && PEM_write_bio_PUBKEY(out, pkey)) {
1714 BUF_MEM *bio_buf;
1715
1716 BIO_get_mem_ptr(out, &bio_buf);
1717 RETVAL_STRINGL((char *)bio_buf->data, bio_buf->length);
1718 } else {
1719 php_openssl_store_errors();
1720 }
1721 goto cleanup;
1722
1723 cleanup:
1724
1725 if (spki != NULL) {
1726 NETSCAPE_SPKI_free(spki);
1727 }
1728 BIO_free_all(out);
1729 EVP_PKEY_free(pkey);
1730 if (spkstr_cleaned != NULL) {
1731 efree(spkstr_cleaned);
1732 }
1733 if (s != NULL) {
1734 efree(s);
1735 }
1736 }
1737 /* }}} */
1738
1739 /* {{{ Exports spkac challenge from existing spki to var */
PHP_FUNCTION(openssl_spki_export_challenge)1740 PHP_FUNCTION(openssl_spki_export_challenge)
1741 {
1742 size_t spkstr_len;
1743 char *spkstr, * spkstr_cleaned = NULL;
1744 int spkstr_cleaned_len;
1745
1746 NETSCAPE_SPKI *spki = NULL;
1747
1748 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
1749 RETURN_THROWS();
1750 }
1751 RETVAL_FALSE;
1752
1753 spkstr_cleaned = emalloc(spkstr_len + 1);
1754 spkstr_cleaned_len = (int)(spkstr_len - php_openssl_spki_cleanup(spkstr, spkstr_cleaned));
1755
1756 if (spkstr_cleaned_len == 0) {
1757 php_error_docref(NULL, E_WARNING, "Invalid SPKAC");
1758 goto cleanup;
1759 }
1760
1761 spki = NETSCAPE_SPKI_b64_decode(spkstr_cleaned, spkstr_cleaned_len);
1762 if (spki == NULL) {
1763 php_openssl_store_errors();
1764 php_error_docref(NULL, E_WARNING, "Unable to decode SPKAC");
1765 goto cleanup;
1766 }
1767
1768 RETVAL_STRING((const char *)ASN1_STRING_get0_data(spki->spkac->challenge));
1769 goto cleanup;
1770
1771 cleanup:
1772 if (spkstr_cleaned != NULL) {
1773 efree(spkstr_cleaned);
1774 }
1775 if (spki) {
1776 NETSCAPE_SPKI_free(spki);
1777 }
1778 }
1779 /* }}} */
1780
1781 /* {{{ Exports a CERT to file or a var */
PHP_FUNCTION(openssl_x509_export)1782 PHP_FUNCTION(openssl_x509_export)
1783 {
1784 X509 *cert;
1785 zend_object *cert_obj;
1786 zend_string *cert_str;
1787 zval *zout;
1788 bool notext = 1;
1789 BIO * bio_out;
1790
1791 ZEND_PARSE_PARAMETERS_START(2, 3)
1792 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
1793 Z_PARAM_ZVAL(zout)
1794 Z_PARAM_OPTIONAL
1795 Z_PARAM_BOOL(notext)
1796 ZEND_PARSE_PARAMETERS_END();
1797
1798 RETVAL_FALSE;
1799
1800 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1801 if (cert == NULL) {
1802 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
1803 return;
1804 }
1805
1806 bio_out = BIO_new(BIO_s_mem());
1807 if (!bio_out) {
1808 php_openssl_store_errors();
1809 goto cleanup;
1810 }
1811 if (!notext && !X509_print(bio_out, cert)) {
1812 php_openssl_store_errors();
1813 }
1814 if (PEM_write_bio_X509(bio_out, cert)) {
1815 BUF_MEM *bio_buf;
1816
1817 BIO_get_mem_ptr(bio_out, &bio_buf);
1818 ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
1819
1820 RETVAL_TRUE;
1821 } else {
1822 php_openssl_store_errors();
1823 }
1824
1825 BIO_free(bio_out);
1826
1827 cleanup:
1828 if (cert_str) {
1829 X509_free(cert);
1830 }
1831 }
1832 /* }}} */
1833
php_openssl_x509_fingerprint(X509 * peer,const char * method,bool raw)1834 zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw)
1835 {
1836 unsigned char md[EVP_MAX_MD_SIZE];
1837 const EVP_MD *mdtype;
1838 unsigned int n;
1839 zend_string *ret;
1840
1841 if (!(mdtype = EVP_get_digestbyname(method))) {
1842 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
1843 return NULL;
1844 } else if (!X509_digest(peer, mdtype, md, &n)) {
1845 php_openssl_store_errors();
1846 php_error_docref(NULL, E_ERROR, "Could not generate signature");
1847 return NULL;
1848 }
1849
1850 if (raw) {
1851 ret = zend_string_init((char*)md, n, 0);
1852 } else {
1853 ret = zend_string_alloc(n * 2, 0);
1854 make_digest_ex(ZSTR_VAL(ret), md, n);
1855 ZSTR_VAL(ret)[n * 2] = '\0';
1856 }
1857
1858 return ret;
1859 }
1860
PHP_FUNCTION(openssl_x509_fingerprint)1861 PHP_FUNCTION(openssl_x509_fingerprint)
1862 {
1863 X509 *cert;
1864 zend_object *cert_obj;
1865 zend_string *cert_str;
1866 bool raw_output = 0;
1867 char *method = "sha1";
1868 size_t method_len;
1869 zend_string *fingerprint;
1870
1871 ZEND_PARSE_PARAMETERS_START(1, 3)
1872 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
1873 Z_PARAM_OPTIONAL
1874 Z_PARAM_STRING(method, method_len)
1875 Z_PARAM_BOOL(raw_output)
1876 ZEND_PARSE_PARAMETERS_END();
1877
1878 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1879 if (cert == NULL) {
1880 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
1881 RETURN_FALSE;
1882 }
1883
1884 fingerprint = php_openssl_x509_fingerprint(cert, method, raw_output);
1885 if (fingerprint) {
1886 RETVAL_STR(fingerprint);
1887 } else {
1888 RETVAL_FALSE;
1889 }
1890
1891 if (cert_str) {
1892 X509_free(cert);
1893 }
1894 }
1895
1896 /* {{{ Checks if a private key corresponds to a CERT */
PHP_FUNCTION(openssl_x509_check_private_key)1897 PHP_FUNCTION(openssl_x509_check_private_key)
1898 {
1899 X509 *cert;
1900 zend_object *cert_obj;
1901 zend_string *cert_str;
1902 zval *zkey;
1903 EVP_PKEY * key = NULL;
1904
1905 ZEND_PARSE_PARAMETERS_START(2, 2)
1906 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
1907 Z_PARAM_ZVAL(zkey)
1908 ZEND_PARSE_PARAMETERS_END();
1909
1910 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1911 if (cert == NULL) {
1912 RETURN_FALSE;
1913 }
1914
1915 RETVAL_FALSE;
1916
1917 key = php_openssl_pkey_from_zval(zkey, 0, "", 0, 2);
1918 if (key) {
1919 RETVAL_BOOL(X509_check_private_key(cert, key));
1920 EVP_PKEY_free(key);
1921 }
1922
1923 if (cert_str) {
1924 X509_free(cert);
1925 }
1926 }
1927 /* }}} */
1928
1929 /* {{{ Verifies the signature of certificate cert using public key key */
PHP_FUNCTION(openssl_x509_verify)1930 PHP_FUNCTION(openssl_x509_verify)
1931 {
1932 X509 *cert;
1933 zend_object *cert_obj;
1934 zend_string *cert_str;
1935 zval *zkey;
1936 EVP_PKEY * key = NULL;
1937 int err = -1;
1938
1939 ZEND_PARSE_PARAMETERS_START(2, 2)
1940 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
1941 Z_PARAM_ZVAL(zkey)
1942 ZEND_PARSE_PARAMETERS_END();
1943
1944 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
1945 if (cert == NULL) {
1946 RETURN_LONG(err);
1947 }
1948
1949 key = php_openssl_pkey_from_zval(zkey, 1, NULL, 0, 2);
1950 if (key != NULL) {
1951 err = X509_verify(cert, key);
1952 if (err < 0) {
1953 php_openssl_store_errors();
1954 }
1955
1956 EVP_PKEY_free(key);
1957 }
1958
1959 if (cert_str) {
1960 X509_free(cert);
1961 }
1962
1963 RETURN_LONG(err);
1964 }
1965 /* }}} */
1966
1967 /* Special handling of subjectAltName, see CVE-2013-4073
1968 * Christian Heimes
1969 */
1970
openssl_x509v3_subjectAltName(BIO * bio,X509_EXTENSION * extension)1971 static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
1972 {
1973 GENERAL_NAMES *names;
1974 const X509V3_EXT_METHOD *method = NULL;
1975 ASN1_OCTET_STRING *extension_data;
1976 long i, length, num;
1977 const unsigned char *p;
1978
1979 method = X509V3_EXT_get(extension);
1980 if (method == NULL) {
1981 return -1;
1982 }
1983
1984 extension_data = X509_EXTENSION_get_data(extension);
1985 p = extension_data->data;
1986 length = extension_data->length;
1987 if (method->it) {
1988 names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, &p, length,
1989 ASN1_ITEM_ptr(method->it)));
1990 } else {
1991 names = (GENERAL_NAMES*) (method->d2i(NULL, &p, length));
1992 }
1993 if (names == NULL) {
1994 php_openssl_store_errors();
1995 return -1;
1996 }
1997
1998 num = sk_GENERAL_NAME_num(names);
1999 for (i = 0; i < num; i++) {
2000 GENERAL_NAME *name;
2001 ASN1_STRING *as;
2002 name = sk_GENERAL_NAME_value(names, i);
2003 switch (name->type) {
2004 case GEN_EMAIL:
2005 BIO_puts(bio, "email:");
2006 as = name->d.rfc822Name;
2007 BIO_write(bio, ASN1_STRING_get0_data(as),
2008 ASN1_STRING_length(as));
2009 break;
2010 case GEN_DNS:
2011 BIO_puts(bio, "DNS:");
2012 as = name->d.dNSName;
2013 BIO_write(bio, ASN1_STRING_get0_data(as),
2014 ASN1_STRING_length(as));
2015 break;
2016 case GEN_URI:
2017 BIO_puts(bio, "URI:");
2018 as = name->d.uniformResourceIdentifier;
2019 BIO_write(bio, ASN1_STRING_get0_data(as),
2020 ASN1_STRING_length(as));
2021 break;
2022 default:
2023 /* use builtin print for GEN_OTHERNAME, GEN_X400,
2024 * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID
2025 */
2026 GENERAL_NAME_print(bio, name);
2027 }
2028 /* trailing ', ' except for last element */
2029 if (i < (num - 1)) {
2030 BIO_puts(bio, ", ");
2031 }
2032 }
2033 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
2034
2035 return 0;
2036 }
2037
2038 /* {{{ Returns an array of the fields/values of the CERT */
PHP_FUNCTION(openssl_x509_parse)2039 PHP_FUNCTION(openssl_x509_parse)
2040 {
2041 X509 *cert;
2042 zend_object *cert_obj;
2043 zend_string *cert_str;
2044 int i, sig_nid;
2045 bool useshortnames = 1;
2046 char * tmpstr;
2047 zval subitem;
2048 X509_EXTENSION *extension;
2049 X509_NAME *subject_name;
2050 char *cert_name;
2051 char *extname;
2052 BIO *bio_out;
2053 BUF_MEM *bio_buf;
2054 ASN1_INTEGER *asn1_serial;
2055 BIGNUM *bn_serial;
2056 char *str_serial;
2057 char *hex_serial;
2058 char buf[256];
2059
2060 ZEND_PARSE_PARAMETERS_START(1, 2)
2061 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2062 Z_PARAM_OPTIONAL
2063 Z_PARAM_BOOL(useshortnames)
2064 ZEND_PARSE_PARAMETERS_END();
2065
2066 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2067 if (cert == NULL) {
2068 // TODO Add Warning?
2069 RETURN_FALSE;
2070 }
2071 array_init(return_value);
2072
2073 subject_name = X509_get_subject_name(cert);
2074 cert_name = X509_NAME_oneline(subject_name, NULL, 0);
2075 add_assoc_string(return_value, "name", cert_name);
2076 OPENSSL_free(cert_name);
2077
2078 php_openssl_add_assoc_name_entry(return_value, "subject", subject_name, useshortnames);
2079 /* hash as used in CA directories to lookup cert by subject name */
2080 {
2081 char buf[32];
2082 snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert));
2083 add_assoc_string(return_value, "hash", buf);
2084 }
2085
2086 php_openssl_add_assoc_name_entry(return_value, "issuer", X509_get_issuer_name(cert), useshortnames);
2087 add_assoc_long(return_value, "version", X509_get_version(cert));
2088
2089 asn1_serial = X509_get_serialNumber(cert);
2090
2091 bn_serial = ASN1_INTEGER_to_BN(asn1_serial, NULL);
2092 /* Can return NULL on error or memory allocation failure */
2093 if (!bn_serial) {
2094 php_openssl_store_errors();
2095 goto err;
2096 }
2097
2098 hex_serial = BN_bn2hex(bn_serial);
2099 BN_free(bn_serial);
2100 /* Can return NULL on error or memory allocation failure */
2101 if (!hex_serial) {
2102 php_openssl_store_errors();
2103 goto err;
2104 }
2105
2106 str_serial = i2s_ASN1_INTEGER(NULL, asn1_serial);
2107 add_assoc_string(return_value, "serialNumber", str_serial);
2108 OPENSSL_free(str_serial);
2109
2110 /* Return the hex representation of the serial number, as defined by OpenSSL */
2111 add_assoc_string(return_value, "serialNumberHex", hex_serial);
2112 OPENSSL_free(hex_serial);
2113
2114 php_openssl_add_assoc_asn1_string(return_value, "validFrom", X509_getm_notBefore(cert));
2115 php_openssl_add_assoc_asn1_string(return_value, "validTo", X509_getm_notAfter(cert));
2116
2117 add_assoc_long(return_value, "validFrom_time_t", php_openssl_asn1_time_to_time_t(X509_getm_notBefore(cert)));
2118 add_assoc_long(return_value, "validTo_time_t", php_openssl_asn1_time_to_time_t(X509_getm_notAfter(cert)));
2119
2120 tmpstr = (char *)X509_alias_get0(cert, NULL);
2121 if (tmpstr) {
2122 add_assoc_string(return_value, "alias", tmpstr);
2123 }
2124
2125 sig_nid = X509_get_signature_nid(cert);
2126 add_assoc_string(return_value, "signatureTypeSN", (char*)OBJ_nid2sn(sig_nid));
2127 add_assoc_string(return_value, "signatureTypeLN", (char*)OBJ_nid2ln(sig_nid));
2128 add_assoc_long(return_value, "signatureTypeNID", sig_nid);
2129 array_init(&subitem);
2130
2131 /* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines
2132 in x509v3.h */
2133 for (i = 0; i < X509_PURPOSE_get_count(); i++) {
2134 int id, purpset;
2135 char * pname;
2136 X509_PURPOSE * purp;
2137 zval subsub;
2138
2139 array_init(&subsub);
2140
2141 purp = X509_PURPOSE_get0(i);
2142 id = X509_PURPOSE_get_id(purp);
2143
2144 purpset = X509_check_purpose(cert, id, 0);
2145 add_index_bool(&subsub, 0, purpset);
2146
2147 purpset = X509_check_purpose(cert, id, 1);
2148 add_index_bool(&subsub, 1, purpset);
2149
2150 pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp);
2151 add_index_string(&subsub, 2, pname);
2152
2153 /* NOTE: if purpset > 1 then it's a warning - we should mention it ? */
2154
2155 add_index_zval(&subitem, id, &subsub);
2156 }
2157 add_assoc_zval(return_value, "purposes", &subitem);
2158
2159 array_init(&subitem);
2160
2161
2162 for (i = 0; i < X509_get_ext_count(cert); i++) {
2163 int nid;
2164 extension = X509_get_ext(cert, i);
2165 nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
2166 if (nid != NID_undef) {
2167 extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension)));
2168 } else {
2169 OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1);
2170 extname = buf;
2171 }
2172 bio_out = BIO_new(BIO_s_mem());
2173 if (bio_out == NULL) {
2174 php_openssl_store_errors();
2175 goto err_subitem;
2176 }
2177 if (nid == NID_subject_alt_name) {
2178 if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
2179 BIO_get_mem_ptr(bio_out, &bio_buf);
2180 add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
2181 } else {
2182 BIO_free(bio_out);
2183 goto err_subitem;
2184 }
2185 }
2186 else if (X509V3_EXT_print(bio_out, extension, 0, 0)) {
2187 BIO_get_mem_ptr(bio_out, &bio_buf);
2188 add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
2189 } else {
2190 php_openssl_add_assoc_asn1_string(&subitem, extname, X509_EXTENSION_get_data(extension));
2191 }
2192 BIO_free(bio_out);
2193 }
2194 add_assoc_zval(return_value, "extensions", &subitem);
2195 if (cert_str) {
2196 X509_free(cert);
2197 }
2198 return;
2199
2200 err_subitem:
2201 zval_ptr_dtor(&subitem);
2202 err:
2203 zend_array_destroy(Z_ARR_P(return_value));
2204 if (cert_str) {
2205 X509_free(cert);
2206 }
2207 RETURN_FALSE;
2208 }
2209 /* }}} */
2210
2211 /* {{{ php_openssl_load_all_certs_from_file */
STACK_OF(X509)2212 static STACK_OF(X509) *php_openssl_load_all_certs_from_file(
2213 char *cert_file, size_t cert_file_len, uint32_t arg_num)
2214 {
2215 STACK_OF(X509_INFO) *sk=NULL;
2216 STACK_OF(X509) *stack=NULL, *ret=NULL;
2217 BIO *in=NULL;
2218 X509_INFO *xi;
2219 char cert_path[MAXPATHLEN];
2220
2221 if(!(stack = sk_X509_new_null())) {
2222 php_openssl_store_errors();
2223 php_error_docref(NULL, E_ERROR, "Memory allocation failure");
2224 goto end;
2225 }
2226
2227 if (!php_openssl_check_path(cert_file, cert_file_len, cert_path, arg_num)) {
2228 sk_X509_free(stack);
2229 goto end;
2230 }
2231
2232 if (!(in = BIO_new_file(cert_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY)))) {
2233 php_openssl_store_errors();
2234 php_error_docref(NULL, E_WARNING, "Error opening the file, %s", cert_path);
2235 sk_X509_free(stack);
2236 goto end;
2237 }
2238
2239 /* This loads from a file, a stack of x509/crl/pkey sets */
2240 if (!(sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
2241 php_openssl_store_errors();
2242 php_error_docref(NULL, E_WARNING, "Error reading the file, %s", cert_path);
2243 sk_X509_free(stack);
2244 goto end;
2245 }
2246
2247 /* scan over it and pull out the certs */
2248 while (sk_X509_INFO_num(sk)) {
2249 xi=sk_X509_INFO_shift(sk);
2250 if (xi->x509 != NULL) {
2251 sk_X509_push(stack,xi->x509);
2252 xi->x509=NULL;
2253 }
2254 X509_INFO_free(xi);
2255 }
2256 if (!sk_X509_num(stack)) {
2257 php_error_docref(NULL, E_WARNING, "No certificates in file, %s", cert_path);
2258 sk_X509_free(stack);
2259 goto end;
2260 }
2261 ret = stack;
2262 end:
2263 BIO_free(in);
2264 sk_X509_INFO_free(sk);
2265
2266 return ret;
2267 }
2268 /* }}} */
2269
2270 /* {{{ check_cert */
check_cert(X509_STORE * ctx,X509 * x,STACK_OF (X509)* untrustedchain,int purpose)2271 static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose)
2272 {
2273 int ret=0;
2274 X509_STORE_CTX *csc;
2275
2276 csc = X509_STORE_CTX_new();
2277 if (csc == NULL) {
2278 php_openssl_store_errors();
2279 php_error_docref(NULL, E_ERROR, "Memory allocation failure");
2280 return 0;
2281 }
2282 if (!X509_STORE_CTX_init(csc, ctx, x, untrustedchain)) {
2283 php_openssl_store_errors();
2284 php_error_docref(NULL, E_WARNING, "Certificate store initialization failed");
2285 return 0;
2286 }
2287 if (purpose >= 0 && !X509_STORE_CTX_set_purpose(csc, purpose)) {
2288 php_openssl_store_errors();
2289 }
2290 ret = X509_verify_cert(csc);
2291 if (ret < 0) {
2292 php_openssl_store_errors();
2293 }
2294 X509_STORE_CTX_free(csc);
2295
2296 return ret;
2297 }
2298 /* }}} */
2299
2300 /* {{{ 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)2301 PHP_FUNCTION(openssl_x509_checkpurpose)
2302 {
2303 X509 *cert;
2304 zend_object *cert_obj;
2305 zend_string *cert_str;
2306 zval *zcainfo = NULL;
2307 X509_STORE *cainfo = NULL;
2308 STACK_OF(X509) *untrustedchain = NULL;
2309 zend_long purpose;
2310 char * untrusted = NULL;
2311 size_t untrusted_len = 0;
2312 int ret;
2313
2314 ZEND_PARSE_PARAMETERS_START(2, 4)
2315 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2316 Z_PARAM_LONG(purpose)
2317 Z_PARAM_OPTIONAL
2318 Z_PARAM_ARRAY(zcainfo)
2319 Z_PARAM_STRING_OR_NULL(untrusted, untrusted_len)
2320 ZEND_PARSE_PARAMETERS_END();
2321
2322 RETVAL_LONG(-1);
2323
2324 if (untrusted) {
2325 untrustedchain = php_openssl_load_all_certs_from_file(untrusted, untrusted_len, 4);
2326 if (untrustedchain == NULL) {
2327 goto clean_exit;
2328 }
2329 }
2330
2331 cainfo = php_openssl_setup_verify(zcainfo, 3);
2332 if (cainfo == NULL) {
2333 goto clean_exit;
2334 }
2335 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2336 if (cert == NULL) {
2337 // TODO Add Warning?
2338 goto clean_exit;
2339 }
2340
2341 ret = check_cert(cainfo, cert, untrustedchain, (int)purpose);
2342 if (ret != 0 && ret != 1) {
2343 RETVAL_LONG(ret);
2344 } else {
2345 RETVAL_BOOL(ret);
2346 }
2347 if (cert_str) {
2348 X509_free(cert);
2349 }
2350 clean_exit:
2351 if (cainfo) {
2352 X509_STORE_free(cainfo);
2353 }
2354 if (untrustedchain) {
2355 sk_X509_pop_free(untrustedchain, X509_free);
2356 }
2357 }
2358 /* }}} */
2359
2360 /* {{{ php_openssl_setup_verify
2361 * calist is an array containing file and directory names. create a
2362 * certificate store and add those certs to it for use in verification.
2363 */
php_openssl_setup_verify(zval * calist,uint32_t arg_num)2364 static X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num)
2365 {
2366 X509_STORE *store;
2367 X509_LOOKUP * dir_lookup, * file_lookup;
2368 int ndirs = 0, nfiles = 0;
2369 zval * item;
2370 zend_stat_t sb = {0};
2371 char file_path[MAXPATHLEN];
2372
2373 store = X509_STORE_new();
2374
2375 if (store == NULL) {
2376 php_openssl_store_errors();
2377 return NULL;
2378 }
2379
2380 if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) {
2381 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(calist), item) {
2382 zend_string *str = zval_try_get_string(item);
2383 if (UNEXPECTED(!str)) {
2384 return NULL;
2385 }
2386
2387 if (!php_openssl_check_path_str_ex(str, file_path, arg_num, false, true, NULL)) {
2388 zend_string_release(str);
2389 continue;
2390 }
2391 zend_string_release(str);
2392
2393 if (VCWD_STAT(file_path, &sb) == -1) {
2394 php_error_docref(NULL, E_WARNING, "Unable to stat %s", file_path);
2395 continue;
2396 }
2397
2398 if ((sb.st_mode & S_IFREG) == S_IFREG) {
2399 file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
2400 if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, file_path, X509_FILETYPE_PEM)) {
2401 php_openssl_store_errors();
2402 php_error_docref(NULL, E_WARNING, "Error loading file %s", file_path);
2403 } else {
2404 nfiles++;
2405 }
2406 file_lookup = NULL;
2407 } else {
2408 dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
2409 if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, file_path, X509_FILETYPE_PEM)) {
2410 php_openssl_store_errors();
2411 php_error_docref(NULL, E_WARNING, "Error loading directory %s", file_path);
2412 } else {
2413 ndirs++;
2414 }
2415 dir_lookup = NULL;
2416 }
2417 } ZEND_HASH_FOREACH_END();
2418 }
2419 if (nfiles == 0) {
2420 file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
2421 if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT)) {
2422 php_openssl_store_errors();
2423 }
2424 }
2425 if (ndirs == 0) {
2426 dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
2427 if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT)) {
2428 php_openssl_store_errors();
2429 }
2430 }
2431 return store;
2432 }
2433 /* }}} */
2434
2435 /* {{{ Reads X.509 certificates */
PHP_FUNCTION(openssl_x509_read)2436 PHP_FUNCTION(openssl_x509_read)
2437 {
2438 X509 *cert;
2439 php_openssl_certificate_object *x509_cert_obj;
2440 zend_object *cert_obj;
2441 zend_string *cert_str;
2442
2443 ZEND_PARSE_PARAMETERS_START(1, 1)
2444 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2445 ZEND_PARSE_PARAMETERS_END();
2446
2447 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2448 if (cert == NULL) {
2449 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
2450 RETURN_FALSE;
2451 }
2452
2453 object_init_ex(return_value, php_openssl_certificate_ce);
2454 x509_cert_obj = Z_OPENSSL_CERTIFICATE_P(return_value);
2455 x509_cert_obj->x509 = cert_obj ? X509_dup(cert) : cert;
2456 }
2457 /* }}} */
2458
2459 /* {{{ Frees X.509 certificates */
PHP_FUNCTION(openssl_x509_free)2460 PHP_FUNCTION(openssl_x509_free)
2461 {
2462 zval *x509;
2463
2464 ZEND_PARSE_PARAMETERS_START(1, 1)
2465 Z_PARAM_OBJECT_OF_CLASS(x509, php_openssl_certificate_ce)
2466 ZEND_PARSE_PARAMETERS_END();
2467 }
2468 /* }}} */
2469
2470 /* }}} */
2471
2472 /* Pop all X509 from Stack and free them, free the stack afterwards */
php_sk_X509_free(STACK_OF (X509)* sk)2473 static void php_sk_X509_free(STACK_OF(X509) * sk) /* {{{ */
2474 {
2475 for (;;) {
2476 X509* x = sk_X509_pop(sk);
2477 if (!x) break;
2478 X509_free(x);
2479 }
2480 sk_X509_free(sk);
2481 }
2482 /* }}} */
2483
STACK_OF(X509)2484 static STACK_OF(X509) *php_array_to_X509_sk(zval * zcerts, uint32_t arg_num, const char *option_name) /* {{{ */
2485 {
2486 zval * zcertval;
2487 STACK_OF(X509) * sk = NULL;
2488 X509 * cert;
2489 bool free_cert;
2490
2491 sk = sk_X509_new_null();
2492
2493 /* get certs */
2494 if (Z_TYPE_P(zcerts) == IS_ARRAY) {
2495 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zcerts), zcertval) {
2496 cert = php_openssl_x509_from_zval(zcertval, &free_cert, arg_num, true, option_name);
2497 if (cert == NULL) {
2498 // TODO Add Warning?
2499 goto clean_exit;
2500 }
2501
2502 if (!free_cert) {
2503 cert = X509_dup(cert);
2504
2505 if (cert == NULL) {
2506 php_openssl_store_errors();
2507 goto clean_exit;
2508 }
2509
2510 }
2511 sk_X509_push(sk, cert);
2512 } ZEND_HASH_FOREACH_END();
2513 } else {
2514 /* a single certificate */
2515 cert = php_openssl_x509_from_zval(zcerts, &free_cert, arg_num, false, option_name);
2516
2517 if (cert == NULL) {
2518 // TODO Add Warning?
2519 goto clean_exit;
2520 }
2521
2522 if (!free_cert) {
2523 cert = X509_dup(cert);
2524 if (cert == NULL) {
2525 php_openssl_store_errors();
2526 goto clean_exit;
2527 }
2528 }
2529 sk_X509_push(sk, cert);
2530 }
2531
2532 clean_exit:
2533 return sk;
2534 }
2535 /* }}} */
2536
2537 /* {{{ Creates and exports a PKCS to file */
PHP_FUNCTION(openssl_pkcs12_export_to_file)2538 PHP_FUNCTION(openssl_pkcs12_export_to_file)
2539 {
2540 X509 *cert;
2541 zend_object *cert_obj;
2542 zend_string *cert_str;
2543 BIO * bio_out = NULL;
2544 PKCS12 * p12 = NULL;
2545 char * filename, file_path[MAXPATHLEN];
2546 char * friendly_name = NULL;
2547 size_t filename_len;
2548 char * pass;
2549 size_t pass_len;
2550 zval *zpkey = NULL, *args = NULL;
2551 EVP_PKEY *priv_key = NULL;
2552 zval * item;
2553 STACK_OF(X509) *ca = NULL;
2554
2555 ZEND_PARSE_PARAMETERS_START(4, 5)
2556 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2557 Z_PARAM_PATH(filename, filename_len)
2558 Z_PARAM_ZVAL(zpkey)
2559 Z_PARAM_STRING(pass, pass_len)
2560 Z_PARAM_OPTIONAL
2561 Z_PARAM_ARRAY(args)
2562 ZEND_PARSE_PARAMETERS_END();
2563
2564 RETVAL_FALSE;
2565
2566 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2567 if (cert == NULL) {
2568 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
2569 return;
2570 }
2571
2572 priv_key = php_openssl_pkey_from_zval(zpkey, 0, "", 0, 3);
2573 if (priv_key == NULL) {
2574 if (!EG(exception)) {
2575 php_error_docref(NULL, E_WARNING, "Cannot get private key from parameter 3");
2576 }
2577 goto cleanup;
2578 }
2579 if (!X509_check_private_key(cert, priv_key)) {
2580 php_openssl_store_errors();
2581 php_error_docref(NULL, E_WARNING, "Private key does not correspond to cert");
2582 goto cleanup;
2583 }
2584 if (!php_openssl_check_path(filename, filename_len, file_path, 2)) {
2585 goto cleanup;
2586 }
2587
2588 /* parse extra config from args array, promote this to an extra function */
2589 if (args &&
2590 (item = zend_hash_str_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name")-1)) != NULL &&
2591 Z_TYPE_P(item) == IS_STRING
2592 ) {
2593 friendly_name = Z_STRVAL_P(item);
2594 }
2595 /* certpbe (default RC2-40)
2596 keypbe (default 3DES)
2597 friendly_caname
2598 */
2599
2600 if (args && (item = zend_hash_str_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts")-1)) != NULL) {
2601 ca = php_array_to_X509_sk(item, 5, "extracerts");
2602 }
2603 /* end parse extra config */
2604
2605 /*PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ca,
2606 int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/
2607
2608 p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
2609 if (p12 != NULL) {
2610 bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
2611 if (bio_out != NULL) {
2612 if (i2d_PKCS12_bio(bio_out, p12) == 0) {
2613 php_openssl_store_errors();
2614 php_error_docref(NULL, E_WARNING, "Error writing to file %s", file_path);
2615 } else {
2616 RETVAL_TRUE;
2617 }
2618 BIO_free(bio_out);
2619 } else {
2620 php_openssl_store_errors();
2621 php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
2622 }
2623
2624 PKCS12_free(p12);
2625 } else {
2626 php_openssl_store_errors();
2627 }
2628
2629 php_sk_X509_free(ca);
2630
2631 cleanup:
2632 EVP_PKEY_free(priv_key);
2633
2634 if (cert_str) {
2635 X509_free(cert);
2636 }
2637 }
2638 /* }}} */
2639
2640 /* {{{ Creates and exports a PKCS12 to a var */
PHP_FUNCTION(openssl_pkcs12_export)2641 PHP_FUNCTION(openssl_pkcs12_export)
2642 {
2643 X509 *cert;
2644 zend_object *cert_obj;
2645 zend_string *cert_str;
2646 BIO * bio_out;
2647 PKCS12 * p12 = NULL;
2648 zval *zout = NULL, *zpkey, *args = NULL;
2649 EVP_PKEY *priv_key = NULL;
2650 char * pass;
2651 size_t pass_len;
2652 char * friendly_name = NULL;
2653 zval * item;
2654 STACK_OF(X509) *ca = NULL;
2655
2656 ZEND_PARSE_PARAMETERS_START(4, 5)
2657 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
2658 Z_PARAM_ZVAL(zout)
2659 Z_PARAM_ZVAL(zpkey)
2660 Z_PARAM_STRING(pass, pass_len)
2661 Z_PARAM_OPTIONAL
2662 Z_PARAM_ARRAY(args)
2663 ZEND_PARSE_PARAMETERS_END();
2664
2665 RETVAL_FALSE;
2666
2667 cert = php_openssl_x509_from_param(cert_obj, cert_str, 1);
2668 if (cert == NULL) {
2669 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
2670 return;
2671 }
2672
2673 priv_key = php_openssl_pkey_from_zval(zpkey, 0, "", 0, 3);
2674 if (priv_key == NULL) {
2675 if (!EG(exception)) {
2676 php_error_docref(NULL, E_WARNING, "Cannot get private key from parameter 3");
2677 }
2678 goto cleanup;
2679 }
2680 if (!X509_check_private_key(cert, priv_key)) {
2681 php_error_docref(NULL, E_WARNING, "Private key does not correspond to cert");
2682 goto cleanup;
2683 }
2684
2685 /* parse extra config from args array, promote this to an extra function */
2686 if (args &&
2687 (item = zend_hash_str_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name")-1)) != NULL &&
2688 Z_TYPE_P(item) == IS_STRING
2689 ) {
2690 friendly_name = Z_STRVAL_P(item);
2691 }
2692
2693 if (args && (item = zend_hash_str_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts")-1)) != NULL) {
2694 ca = php_array_to_X509_sk(item, 5, "extracerts");
2695 }
2696 /* end parse extra config */
2697
2698 p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
2699
2700 if (p12 != NULL) {
2701 bio_out = BIO_new(BIO_s_mem());
2702 if (i2d_PKCS12_bio(bio_out, p12)) {
2703 BUF_MEM *bio_buf;
2704
2705 BIO_get_mem_ptr(bio_out, &bio_buf);
2706 ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
2707
2708 RETVAL_TRUE;
2709 } else {
2710 php_openssl_store_errors();
2711 }
2712
2713 BIO_free(bio_out);
2714 PKCS12_free(p12);
2715 } else {
2716 php_openssl_store_errors();
2717 }
2718 php_sk_X509_free(ca);
2719
2720 cleanup:
2721 EVP_PKEY_free(priv_key);
2722 if (cert_str) {
2723 X509_free(cert);
2724 }
2725 }
2726 /* }}} */
2727
2728 /* {{{ Parses a PKCS12 to an array */
PHP_FUNCTION(openssl_pkcs12_read)2729 PHP_FUNCTION(openssl_pkcs12_read)
2730 {
2731 zval *zout = NULL, zextracerts, zcert, zpkey;
2732 char *pass, *zp12;
2733 size_t pass_len, zp12_len;
2734 PKCS12 * p12 = NULL;
2735 EVP_PKEY * pkey = NULL;
2736 X509 * cert = NULL;
2737 STACK_OF(X509) * ca = NULL;
2738 BIO * bio_in = NULL;
2739 int i;
2740
2741 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szs", &zp12, &zp12_len, &zout, &pass, &pass_len) == FAILURE) {
2742 RETURN_THROWS();
2743 }
2744
2745 RETVAL_FALSE;
2746
2747 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(zp12_len, pkcs12, 1);
2748
2749 bio_in = BIO_new(BIO_s_mem());
2750
2751 if (0 >= BIO_write(bio_in, zp12, (int)zp12_len)) {
2752 php_openssl_store_errors();
2753 goto cleanup;
2754 }
2755
2756 if (d2i_PKCS12_bio(bio_in, &p12) && PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
2757 BIO * bio_out;
2758 int cert_num;
2759
2760 zout = zend_try_array_init(zout);
2761 if (!zout) {
2762 goto cleanup;
2763 }
2764
2765 if (cert) {
2766 bio_out = BIO_new(BIO_s_mem());
2767 if (PEM_write_bio_X509(bio_out, cert)) {
2768 BUF_MEM *bio_buf;
2769 BIO_get_mem_ptr(bio_out, &bio_buf);
2770 ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
2771 add_assoc_zval(zout, "cert", &zcert);
2772 } else {
2773 php_openssl_store_errors();
2774 }
2775 BIO_free(bio_out);
2776 }
2777
2778 if (pkey) {
2779 bio_out = BIO_new(BIO_s_mem());
2780 if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
2781 BUF_MEM *bio_buf;
2782 BIO_get_mem_ptr(bio_out, &bio_buf);
2783 ZVAL_STRINGL(&zpkey, bio_buf->data, bio_buf->length);
2784 add_assoc_zval(zout, "pkey", &zpkey);
2785 } else {
2786 php_openssl_store_errors();
2787 }
2788 BIO_free(bio_out);
2789 }
2790
2791 cert_num = sk_X509_num(ca);
2792 if (ca && cert_num) {
2793 array_init(&zextracerts);
2794
2795 for (i = 0; i < cert_num; i++) {
2796 zval zextracert;
2797 X509* aCA = sk_X509_pop(ca);
2798 if (!aCA) break;
2799
2800 bio_out = BIO_new(BIO_s_mem());
2801 if (PEM_write_bio_X509(bio_out, aCA)) {
2802 BUF_MEM *bio_buf;
2803 BIO_get_mem_ptr(bio_out, &bio_buf);
2804 ZVAL_STRINGL(&zextracert, bio_buf->data, bio_buf->length);
2805 add_index_zval(&zextracerts, i, &zextracert);
2806 }
2807
2808 X509_free(aCA);
2809 BIO_free(bio_out);
2810 }
2811
2812 sk_X509_free(ca);
2813 add_assoc_zval(zout, "extracerts", &zextracerts);
2814 }
2815
2816 RETVAL_TRUE;
2817 } else {
2818 php_openssl_store_errors();
2819 }
2820
2821 cleanup:
2822 BIO_free(bio_in);
2823 EVP_PKEY_free(pkey);
2824 if (cert) {
2825 X509_free(cert);
2826 }
2827 if (p12) {
2828 PKCS12_free(p12);
2829 }
2830 }
2831 /* }}} */
2832
2833 /* {{{ x509 CSR functions */
2834
2835 /* {{{ php_openssl_make_REQ */
php_openssl_make_REQ(struct php_x509_request * req,X509_REQ * csr,zval * dn,zval * attribs)2836 static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs)
2837 {
2838 STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
2839 char * str, *dn_sect, *attr_sect;
2840
2841 dn_sect = NCONF_get_string(req->req_config, req->section_name, "distinguished_name");
2842 if (dn_sect == NULL) {
2843 php_openssl_store_errors();
2844 return FAILURE;
2845 }
2846 dn_sk = NCONF_get_section(req->req_config, dn_sect);
2847 if (dn_sk == NULL) {
2848 php_openssl_store_errors();
2849 return FAILURE;
2850 }
2851 attr_sect = php_openssl_conf_get_string(req->req_config, req->section_name, "attributes");
2852 if (attr_sect == NULL) {
2853 attr_sk = NULL;
2854 } else {
2855 attr_sk = NCONF_get_section(req->req_config, attr_sect);
2856 if (attr_sk == NULL) {
2857 php_openssl_store_errors();
2858 return FAILURE;
2859 }
2860 }
2861 /* setup the version number: version 1 */
2862 if (X509_REQ_set_version(csr, 0L)) {
2863 int i, nid;
2864 char * type;
2865 CONF_VALUE * v;
2866 X509_NAME * subj;
2867 zval * item;
2868 zend_string * strindex = NULL;
2869
2870 subj = X509_REQ_get_subject_name(csr);
2871 /* apply values from the dn hash */
2872 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(dn), strindex, item) {
2873 if (strindex) {
2874 int nid = OBJ_txt2nid(ZSTR_VAL(strindex));
2875 if (nid != NID_undef) {
2876 zend_string *str_item = zval_try_get_string(item);
2877 if (UNEXPECTED(!str_item)) {
2878 return FAILURE;
2879 }
2880 if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8,
2881 (unsigned char*)ZSTR_VAL(str_item), -1, -1, 0))
2882 {
2883 php_openssl_store_errors();
2884 php_error_docref(NULL, E_WARNING,
2885 "dn: add_entry_by_NID %d -> %s (failed; check error"
2886 " queue and value of string_mask OpenSSL option "
2887 "if illegal characters are reported)",
2888 nid, ZSTR_VAL(str_item));
2889 zend_string_release(str_item);
2890 return FAILURE;
2891 }
2892 zend_string_release(str_item);
2893 } else {
2894 php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex));
2895 }
2896 }
2897 } ZEND_HASH_FOREACH_END();
2898
2899 /* Finally apply defaults from config file */
2900 for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
2901 size_t len;
2902 char buffer[200 + 1]; /*200 + \0 !*/
2903
2904 v = sk_CONF_VALUE_value(dn_sk, i);
2905 type = v->name;
2906
2907 len = strlen(type);
2908 if (len < sizeof("_default")) {
2909 continue;
2910 }
2911 len -= sizeof("_default") - 1;
2912 if (strcmp("_default", type + len) != 0) {
2913 continue;
2914 }
2915 if (len > 200) {
2916 len = 200;
2917 }
2918 memcpy(buffer, type, len);
2919 buffer[len] = '\0';
2920 type = buffer;
2921
2922 /* Skip past any leading X. X: X, etc to allow for multiple
2923 * instances */
2924 for (str = type; *str; str++) {
2925 if (*str == ':' || *str == ',' || *str == '.') {
2926 str++;
2927 if (*str) {
2928 type = str;
2929 }
2930 break;
2931 }
2932 }
2933 /* if it is already set, skip this */
2934 nid = OBJ_txt2nid(type);
2935 if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) {
2936 continue;
2937 }
2938 if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) {
2939 php_openssl_store_errors();
2940 php_error_docref(NULL, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
2941 return FAILURE;
2942 }
2943 if (!X509_NAME_entry_count(subj)) {
2944 php_error_docref(NULL, E_WARNING, "No objects specified in config file");
2945 return FAILURE;
2946 }
2947 }
2948 if (attribs) {
2949 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(attribs), strindex, item) {
2950 int nid;
2951
2952 if (NULL == strindex) {
2953 php_error_docref(NULL, E_WARNING, "dn: numeric fild names are not supported");
2954 continue;
2955 }
2956
2957 nid = OBJ_txt2nid(ZSTR_VAL(strindex));
2958 if (nid != NID_undef) {
2959 zend_string *str_item = zval_try_get_string(item);
2960 if (UNEXPECTED(!str_item)) {
2961 return FAILURE;
2962 }
2963 if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)ZSTR_VAL(str_item), -1, -1, 0)) {
2964 php_openssl_store_errors();
2965 php_error_docref(NULL, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, ZSTR_VAL(str_item));
2966 zend_string_release(str_item);
2967 return FAILURE;
2968 }
2969 zend_string_release(str_item);
2970 } else {
2971 php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex));
2972 }
2973 } ZEND_HASH_FOREACH_END();
2974 for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
2975 v = sk_CONF_VALUE_value(attr_sk, i);
2976 /* if it is already set, skip this */
2977 nid = OBJ_txt2nid(v->name);
2978 if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) {
2979 continue;
2980 }
2981 if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) {
2982 php_openssl_store_errors();
2983 php_error_docref(NULL, E_WARNING,
2984 "add1_attr_by_txt %s -> %s (failed; check error queue "
2985 "and value of string_mask OpenSSL option if illegal "
2986 "characters are reported)",
2987 v->name, v->value);
2988 return FAILURE;
2989 }
2990 }
2991 }
2992 } else {
2993 php_openssl_store_errors();
2994 }
2995
2996 if (!X509_REQ_set_pubkey(csr, req->priv_key)) {
2997 php_openssl_store_errors();
2998 }
2999 return SUCCESS;
3000 }
3001 /* }}} */
3002
3003
php_openssl_csr_from_str(zend_string * csr_str,uint32_t arg_num)3004 static X509_REQ *php_openssl_csr_from_str(zend_string *csr_str, uint32_t arg_num)
3005 {
3006 X509_REQ * csr = NULL;
3007 char file_path[MAXPATHLEN];
3008 BIO * in;
3009
3010 if (ZSTR_LEN(csr_str) > 7 && memcmp(ZSTR_VAL(csr_str), "file://", sizeof("file://") - 1) == 0) {
3011 if (!php_openssl_check_path_str(csr_str, file_path, arg_num)) {
3012 return NULL;
3013 }
3014 in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3015 } else {
3016 in = BIO_new_mem_buf(ZSTR_VAL(csr_str), (int) ZSTR_LEN(csr_str));
3017 }
3018
3019 if (in == NULL) {
3020 php_openssl_store_errors();
3021 return NULL;
3022 }
3023
3024 csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
3025 if (csr == NULL) {
3026 php_openssl_store_errors();
3027 }
3028
3029 BIO_free(in);
3030
3031 return csr;
3032 }
3033
php_openssl_csr_from_param(zend_object * csr_obj,zend_string * csr_str,uint32_t arg_num)3034 static X509_REQ *php_openssl_csr_from_param(
3035 zend_object *csr_obj, zend_string *csr_str, uint32_t arg_num)
3036 {
3037 if (csr_obj) {
3038 return php_openssl_request_from_obj(csr_obj)->csr;
3039 }
3040
3041 ZEND_ASSERT(csr_str);
3042
3043 return php_openssl_csr_from_str(csr_str, arg_num);
3044 }
3045
3046 /* {{{ Exports a CSR to file */
PHP_FUNCTION(openssl_csr_export_to_file)3047 PHP_FUNCTION(openssl_csr_export_to_file)
3048 {
3049 X509_REQ *csr;
3050 zend_object *csr_obj;
3051 zend_string *csr_str;
3052 bool notext = 1;
3053 char * filename = NULL;
3054 size_t filename_len;
3055 char file_path[MAXPATHLEN];
3056 BIO * bio_out;
3057
3058 ZEND_PARSE_PARAMETERS_START(2, 3)
3059 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3060 Z_PARAM_PATH(filename, filename_len)
3061 Z_PARAM_OPTIONAL
3062 Z_PARAM_BOOL(notext)
3063 ZEND_PARSE_PARAMETERS_END();
3064
3065 RETVAL_FALSE;
3066
3067 csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3068 if (csr == NULL) {
3069 php_error_docref(NULL, E_WARNING, "X.509 Certificate Signing Request cannot be retrieved");
3070 return;
3071 }
3072
3073 if (!php_openssl_check_path(filename, filename_len, file_path, 2)) {
3074 goto exit_cleanup;
3075 }
3076
3077 bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
3078 if (bio_out != NULL) {
3079 if (!notext && !X509_REQ_print(bio_out, csr)) {
3080 php_openssl_store_errors();
3081 }
3082 if (!PEM_write_bio_X509_REQ(bio_out, csr)) {
3083 php_error_docref(NULL, E_WARNING, "Error writing PEM to file %s", file_path);
3084 php_openssl_store_errors();
3085 } else {
3086 RETVAL_TRUE;
3087 }
3088 BIO_free(bio_out);
3089 } else {
3090 php_openssl_store_errors();
3091 php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path);
3092 }
3093
3094 exit_cleanup:
3095 if (csr_str) {
3096 X509_REQ_free(csr);
3097 }
3098 }
3099 /* }}} */
3100
3101 /* {{{ Exports a CSR to file or a var */
PHP_FUNCTION(openssl_csr_export)3102 PHP_FUNCTION(openssl_csr_export)
3103 {
3104 X509_REQ *csr;
3105 zend_object *csr_obj;
3106 zend_string *csr_str;
3107 zval *zout;
3108 bool notext = 1;
3109 BIO * bio_out;
3110
3111 ZEND_PARSE_PARAMETERS_START(2, 3)
3112 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3113 Z_PARAM_ZVAL(zout)
3114 Z_PARAM_OPTIONAL
3115 Z_PARAM_BOOL(notext)
3116 ZEND_PARSE_PARAMETERS_END();
3117
3118 RETVAL_FALSE;
3119
3120 csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3121 if (csr == NULL) {
3122 php_error_docref(NULL, E_WARNING, "X.509 Certificate Signing Request cannot be retrieved");
3123 return;
3124 }
3125
3126 /* export to a var */
3127
3128 bio_out = BIO_new(BIO_s_mem());
3129 if (!notext && !X509_REQ_print(bio_out, csr)) {
3130 php_openssl_store_errors();
3131 }
3132
3133 if (PEM_write_bio_X509_REQ(bio_out, csr)) {
3134 BUF_MEM *bio_buf;
3135
3136 BIO_get_mem_ptr(bio_out, &bio_buf);
3137 ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
3138
3139 RETVAL_TRUE;
3140 } else {
3141 php_openssl_store_errors();
3142 }
3143
3144 if (csr_str) {
3145 X509_REQ_free(csr);
3146 }
3147 BIO_free(bio_out);
3148 }
3149 /* }}} */
3150
3151 /* {{{ Signs a cert with another CERT */
PHP_FUNCTION(openssl_csr_sign)3152 PHP_FUNCTION(openssl_csr_sign)
3153 {
3154 X509_REQ *csr;
3155 zend_object *csr_obj;
3156 zend_string *csr_str;
3157
3158 php_openssl_certificate_object *cert_object;
3159 zend_object *cert_obj;
3160 zend_string *cert_str;
3161 zval *zpkey, *args = NULL;
3162 zend_long num_days;
3163 zend_long serial = Z_L(0);
3164 X509 *cert = NULL, *new_cert = NULL;
3165 EVP_PKEY * key = NULL, *priv_key = NULL;
3166 int i;
3167 bool new_cert_used = false;
3168 struct php_x509_request req;
3169
3170 ZEND_PARSE_PARAMETERS_START(4, 6)
3171 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3172 Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(cert_obj, php_openssl_certificate_ce, cert_str)
3173 Z_PARAM_ZVAL(zpkey)
3174 Z_PARAM_LONG(num_days)
3175 Z_PARAM_OPTIONAL
3176 Z_PARAM_ARRAY_OR_NULL(args)
3177 Z_PARAM_LONG(serial)
3178 ZEND_PARSE_PARAMETERS_END();
3179
3180 RETVAL_FALSE;
3181
3182 csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3183 if (csr == NULL) {
3184 php_error_docref(NULL, E_WARNING, "X.509 Certificate Signing Request cannot be retrieved");
3185 return;
3186 }
3187
3188 PHP_SSL_REQ_INIT(&req);
3189
3190 if (cert_str || cert_obj) {
3191 cert = php_openssl_x509_from_param(cert_obj, cert_str, 2);
3192 if (cert == NULL) {
3193 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
3194 goto cleanup;
3195 }
3196 }
3197
3198 priv_key = php_openssl_pkey_from_zval(zpkey, 0, "", 0, 3);
3199 if (priv_key == NULL) {
3200 if (!EG(exception)) {
3201 php_error_docref(NULL, E_WARNING, "Cannot get private key from parameter 3");
3202 }
3203 goto cleanup;
3204 }
3205 if (cert && !X509_check_private_key(cert, priv_key)) {
3206 php_openssl_store_errors();
3207 php_error_docref(NULL, E_WARNING, "Private key does not correspond to signing cert");
3208 goto cleanup;
3209 }
3210
3211 if (num_days < 0 || num_days > LONG_MAX / 86400) {
3212 php_error_docref(NULL, E_WARNING, "Days must be between 0 and %ld", LONG_MAX / 86400);
3213 goto cleanup;
3214 }
3215
3216 if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) {
3217 goto cleanup;
3218 }
3219 /* Check that the request matches the signature */
3220 key = X509_REQ_get_pubkey(csr);
3221 if (key == NULL) {
3222 php_openssl_store_errors();
3223 php_error_docref(NULL, E_WARNING, "Error unpacking public key");
3224 goto cleanup;
3225 }
3226 i = X509_REQ_verify(csr, key);
3227
3228 if (i < 0) {
3229 php_openssl_store_errors();
3230 php_error_docref(NULL, E_WARNING, "Signature verification problems");
3231 goto cleanup;
3232 }
3233 else if (i == 0) {
3234 php_error_docref(NULL, E_WARNING, "Signature did not match the certificate request");
3235 goto cleanup;
3236 }
3237
3238 /* Now we can get on with it */
3239
3240 new_cert = X509_new();
3241 if (new_cert == NULL) {
3242 php_openssl_store_errors();
3243 php_error_docref(NULL, E_WARNING, "No memory");
3244 goto cleanup;
3245 }
3246 /* Version 3 cert */
3247 if (!X509_set_version(new_cert, 2)) {
3248 goto cleanup;
3249 }
3250
3251 #if PHP_OPENSSL_API_VERSION >= 0x10100 && !defined (LIBRESSL_VERSION_NUMBER)
3252 ASN1_INTEGER_set_int64(X509_get_serialNumber(new_cert), serial);
3253 #else
3254 ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial);
3255 #endif
3256
3257 X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr));
3258
3259 if (cert == NULL) {
3260 cert = new_cert;
3261 }
3262 if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
3263 php_openssl_store_errors();
3264 goto cleanup;
3265 }
3266 X509_gmtime_adj(X509_getm_notBefore(new_cert), 0);
3267 X509_gmtime_adj(X509_getm_notAfter(new_cert), 60*60*24*num_days);
3268 i = X509_set_pubkey(new_cert, key);
3269 if (!i) {
3270 php_openssl_store_errors();
3271 goto cleanup;
3272 }
3273 if (req.extensions_section) {
3274 X509V3_CTX ctx;
3275
3276 X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
3277 X509V3_set_nconf(&ctx, req.req_config);
3278 if (!X509V3_EXT_add_nconf(req.req_config, &ctx, req.extensions_section, new_cert)) {
3279 php_openssl_store_errors();
3280 goto cleanup;
3281 }
3282 }
3283
3284 /* Now sign it */
3285 if (!X509_sign(new_cert, priv_key, req.digest)) {
3286 php_openssl_store_errors();
3287 php_error_docref(NULL, E_WARNING, "Failed to sign it");
3288 goto cleanup;
3289 }
3290
3291 object_init_ex(return_value, php_openssl_certificate_ce);
3292 cert_object = Z_OPENSSL_CERTIFICATE_P(return_value);
3293 cert_object->x509 = new_cert;
3294 new_cert_used = true;
3295
3296 cleanup:
3297
3298 if (!new_cert_used && new_cert) {
3299 X509_free(new_cert);
3300 }
3301
3302 PHP_SSL_REQ_DISPOSE(&req);
3303 EVP_PKEY_free(priv_key);
3304 EVP_PKEY_free(key);
3305 if (csr_str) {
3306 X509_REQ_free(csr);
3307 }
3308 if (cert_str && cert && cert != new_cert) {
3309 X509_free(cert);
3310 }
3311 }
3312 /* }}} */
3313
3314 /* {{{ Generates a privkey and CSR */
PHP_FUNCTION(openssl_csr_new)3315 PHP_FUNCTION(openssl_csr_new)
3316 {
3317 struct php_x509_request req;
3318 php_openssl_request_object *x509_request_obj;
3319 zval *args = NULL, *dn, *attribs = NULL;
3320 zval *out_pkey;
3321 X509_REQ *csr = NULL;
3322
3323 if (zend_parse_parameters(ZEND_NUM_ARGS(), "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) {
3324 RETURN_THROWS();
3325 }
3326 RETVAL_FALSE;
3327
3328 PHP_SSL_REQ_INIT(&req);
3329
3330 if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3331 int we_made_the_key = 0;
3332 zval *out_pkey_val = out_pkey;
3333 ZVAL_DEREF(out_pkey_val);
3334
3335 /* Generate or use a private key */
3336 if (Z_TYPE_P(out_pkey_val) != IS_NULL) {
3337 req.priv_key = php_openssl_pkey_from_zval(out_pkey_val, 0, NULL, 0, 2);
3338 }
3339 if (req.priv_key == NULL) {
3340 php_openssl_generate_private_key(&req);
3341 we_made_the_key = 1;
3342 }
3343 if (req.priv_key == NULL) {
3344 php_error_docref(NULL, E_WARNING, "Unable to generate a private key");
3345 } else {
3346 csr = X509_REQ_new();
3347 if (csr) {
3348 if (php_openssl_make_REQ(&req, csr, dn, attribs) == SUCCESS) {
3349 X509V3_CTX ext_ctx;
3350
3351 X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0);
3352 X509V3_set_nconf(&ext_ctx, req.req_config);
3353
3354 /* Add extensions */
3355 if (req.request_extensions_section && !X509V3_EXT_REQ_add_nconf(req.req_config,
3356 &ext_ctx, req.request_extensions_section, csr))
3357 {
3358 php_openssl_store_errors();
3359 php_error_docref(NULL, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
3360 } else {
3361 RETVAL_TRUE;
3362
3363 if (X509_REQ_sign(csr, req.priv_key, req.digest)) {
3364 object_init_ex(return_value, php_openssl_request_ce);
3365 x509_request_obj = Z_OPENSSL_REQUEST_P(return_value);
3366 x509_request_obj->csr = csr;
3367 csr = NULL;
3368 } else {
3369 php_openssl_store_errors();
3370 php_error_docref(NULL, E_WARNING, "Error signing request");
3371 }
3372
3373 if (we_made_the_key) {
3374 /* and an object for the private key */
3375 zval zkey_object;
3376 php_openssl_pkey_object_init(
3377 &zkey_object, req.priv_key, /* is_private */ true);
3378 ZEND_TRY_ASSIGN_REF_TMP(out_pkey, &zkey_object);
3379 req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
3380 }
3381 }
3382 }
3383 } else {
3384 php_openssl_store_errors();
3385 }
3386
3387 }
3388 }
3389 if (csr) {
3390 X509_REQ_free(csr);
3391 }
3392 PHP_SSL_REQ_DISPOSE(&req);
3393 }
3394 /* }}} */
3395
3396 /* {{{ Returns the subject of a CERT or FALSE on error */
PHP_FUNCTION(openssl_csr_get_subject)3397 PHP_FUNCTION(openssl_csr_get_subject)
3398 {
3399 X509_REQ *csr;
3400 zend_object *csr_obj;
3401 zend_string *csr_str;
3402 bool use_shortnames = 1;
3403 X509_NAME *subject;
3404
3405 ZEND_PARSE_PARAMETERS_START(1, 2)
3406 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3407 Z_PARAM_OPTIONAL
3408 Z_PARAM_BOOL(use_shortnames)
3409 ZEND_PARSE_PARAMETERS_END();
3410
3411 csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3412 if (csr == NULL) {
3413 RETURN_FALSE;
3414 }
3415
3416 subject = X509_REQ_get_subject_name(csr);
3417
3418 array_init(return_value);
3419 php_openssl_add_assoc_name_entry(return_value, NULL, subject, use_shortnames);
3420
3421 if (csr_str) {
3422 X509_REQ_free(csr);
3423 }
3424 }
3425 /* }}} */
3426
php_openssl_extract_public_key(EVP_PKEY * priv_key)3427 static EVP_PKEY *php_openssl_extract_public_key(EVP_PKEY *priv_key)
3428 {
3429 /* Extract public key portion by round-tripping through PEM. */
3430 BIO *bio = BIO_new(BIO_s_mem());
3431 if (!bio || !PEM_write_bio_PUBKEY(bio, priv_key)) {
3432 BIO_free(bio);
3433 return NULL;
3434 }
3435
3436 EVP_PKEY *pub_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
3437 BIO_free(bio);
3438 return pub_key;
3439 }
3440
3441 /* {{{ Returns the subject of a CERT or FALSE on error */
PHP_FUNCTION(openssl_csr_get_public_key)3442 PHP_FUNCTION(openssl_csr_get_public_key)
3443 {
3444 zend_object *csr_obj;
3445 zend_string *csr_str;
3446 bool use_shortnames = 1;
3447
3448 ZEND_PARSE_PARAMETERS_START(1, 2)
3449 Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
3450 Z_PARAM_OPTIONAL
3451 Z_PARAM_BOOL(use_shortnames)
3452 ZEND_PARSE_PARAMETERS_END();
3453
3454 X509_REQ *csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
3455 if (csr == NULL) {
3456 RETURN_FALSE;
3457 }
3458
3459 /* Retrieve the public key from the CSR */
3460 EVP_PKEY *orig_key = X509_REQ_get_pubkey(csr);
3461 EVP_PKEY *tpubkey = php_openssl_extract_public_key(orig_key);
3462 EVP_PKEY_free(orig_key);
3463
3464 if (csr_str) {
3465 /* We need to free the original CSR if it was freshly created */
3466 X509_REQ_free(csr);
3467 }
3468
3469 if (tpubkey == NULL) {
3470 php_openssl_store_errors();
3471 RETURN_FALSE;
3472 }
3473
3474 php_openssl_pkey_object_init(return_value, tpubkey, /* is_private */ false);
3475 }
3476 /* }}} */
3477
3478 /* }}} */
3479
3480 /* {{{ EVP Public/Private key functions */
3481
3482 struct php_openssl_pem_password {
3483 char *key;
3484 int len;
3485 };
3486
3487 /* {{{ php_openssl_pem_password_cb */
php_openssl_pem_password_cb(char * buf,int size,int rwflag,void * userdata)3488 static int php_openssl_pem_password_cb(char *buf, int size, int rwflag, void *userdata)
3489 {
3490 struct php_openssl_pem_password *password = userdata;
3491
3492 if (password == NULL || password->key == NULL) {
3493 return -1;
3494 }
3495
3496 size = (password->len > size) ? size : password->len;
3497 memcpy(buf, password->key, size);
3498
3499 return size;
3500 }
3501 /* }}} */
3502
php_openssl_pkey_from_zval(zval * val,int public_key,char * passphrase,size_t passphrase_len,uint32_t arg_num)3503 static EVP_PKEY *php_openssl_pkey_from_zval(
3504 zval *val, int public_key, char *passphrase, size_t passphrase_len, uint32_t arg_num)
3505 {
3506 EVP_PKEY *key = NULL;
3507 X509 *cert = NULL;
3508 bool free_cert = false, is_file = false;
3509 char file_path[MAXPATHLEN];
3510 zval tmp;
3511
3512 ZVAL_NULL(&tmp);
3513
3514 #define TMP_CLEAN \
3515 if (Z_TYPE(tmp) == IS_STRING) {\
3516 zval_ptr_dtor_str(&tmp); \
3517 } \
3518 return NULL;
3519
3520 if (Z_TYPE_P(val) == IS_ARRAY) {
3521 zval * zphrase;
3522
3523 /* get passphrase */
3524
3525 if ((zphrase = zend_hash_index_find(Z_ARRVAL_P(val), 1)) == NULL) {
3526 zend_value_error("Key array must be of the form array(0 => key, 1 => phrase)");
3527 return NULL;
3528 }
3529
3530 if (Z_TYPE_P(zphrase) == IS_STRING) {
3531 passphrase = Z_STRVAL_P(zphrase);
3532 passphrase_len = Z_STRLEN_P(zphrase);
3533 } else {
3534 ZVAL_COPY(&tmp, zphrase);
3535 if (!try_convert_to_string(&tmp)) {
3536 zval_ptr_dtor(&tmp);
3537 return NULL;
3538 }
3539
3540 passphrase = Z_STRVAL(tmp);
3541 passphrase_len = Z_STRLEN(tmp);
3542 }
3543
3544 /* now set val to be the key param and continue */
3545 if ((val = zend_hash_index_find(Z_ARRVAL_P(val), 0)) == NULL) {
3546 zend_value_error("Key array must be of the form array(0 => key, 1 => phrase)");
3547 TMP_CLEAN;
3548 }
3549 }
3550
3551 if (Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_pkey_ce) {
3552 php_openssl_pkey_object *obj = php_openssl_pkey_from_obj(Z_OBJ_P(val));
3553 key = obj->pkey;
3554 bool is_priv = obj->is_private;
3555
3556 /* check whether it is actually a private key if requested */
3557 if (!public_key && !is_priv) {
3558 php_error_docref(NULL, E_WARNING, "Supplied key param is a public key");
3559 TMP_CLEAN;
3560 }
3561
3562 if (public_key && is_priv) {
3563 php_error_docref(NULL, E_WARNING, "Don't know how to get public key from this private key");
3564 TMP_CLEAN;
3565 } else {
3566 if (Z_TYPE(tmp) == IS_STRING) {
3567 zval_ptr_dtor_str(&tmp);
3568 }
3569
3570 EVP_PKEY_up_ref(key);
3571 return key;
3572 }
3573 } else if (Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_certificate_ce) {
3574 cert = php_openssl_certificate_from_obj(Z_OBJ_P(val))->x509;
3575 } else {
3576 /* force it to be a string and check if it refers to a file */
3577 /* passing non string values leaks, object uses toString, it returns NULL
3578 * See bug38255.phpt
3579 */
3580 if (!(Z_TYPE_P(val) == IS_STRING || Z_TYPE_P(val) == IS_OBJECT)) {
3581 TMP_CLEAN;
3582 }
3583 zend_string *val_str = zval_try_get_string(val);
3584 if (!val_str) {
3585 TMP_CLEAN;
3586 }
3587
3588 if (ZSTR_LEN(val_str) > 7 && memcmp(ZSTR_VAL(val_str), "file://", sizeof("file://") - 1) == 0) {
3589 if (!php_openssl_check_path_str(val_str, file_path, arg_num)) {
3590 zend_string_release_ex(val_str, false);
3591 TMP_CLEAN;
3592 }
3593 is_file = true;
3594 }
3595 /* it's an X509 file/cert of some kind, and we need to extract the data from that */
3596 if (public_key) {
3597 cert = php_openssl_x509_from_str(val_str, arg_num, false, NULL);
3598
3599 if (cert) {
3600 free_cert = 1;
3601 } else {
3602 /* not a X509 certificate, try to retrieve public key */
3603 BIO* in;
3604 if (is_file) {
3605 in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3606 } else {
3607 in = BIO_new_mem_buf(ZSTR_VAL(val_str), (int)ZSTR_LEN(val_str));
3608 }
3609 if (in == NULL) {
3610 php_openssl_store_errors();
3611 zend_string_release_ex(val_str, false);
3612 TMP_CLEAN;
3613 }
3614 key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
3615 BIO_free(in);
3616 }
3617 } else {
3618 /* we want the private key */
3619 BIO *in;
3620
3621 if (is_file) {
3622 in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
3623 } else {
3624 in = BIO_new_mem_buf(ZSTR_VAL(val_str), (int)ZSTR_LEN(val_str));
3625 }
3626
3627 if (in == NULL) {
3628 zend_string_release_ex(val_str, false);
3629 TMP_CLEAN;
3630 }
3631 if (passphrase == NULL) {
3632 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
3633 } else {
3634 struct php_openssl_pem_password password;
3635 password.key = passphrase;
3636 password.len = passphrase_len;
3637 key = PEM_read_bio_PrivateKey(in, NULL, php_openssl_pem_password_cb, &password);
3638 }
3639 BIO_free(in);
3640 }
3641
3642 zend_string_release_ex(val_str, false);
3643 }
3644
3645 if (key == NULL) {
3646 php_openssl_store_errors();
3647
3648 if (public_key && cert) {
3649 /* extract public key from X509 cert */
3650 key = (EVP_PKEY *) X509_get_pubkey(cert);
3651 if (key == NULL) {
3652 php_openssl_store_errors();
3653 }
3654 }
3655 }
3656
3657 if (free_cert) {
3658 X509_free(cert);
3659 }
3660
3661 if (Z_TYPE(tmp) == IS_STRING) {
3662 zval_ptr_dtor_str(&tmp);
3663 }
3664
3665 return key;
3666 }
3667
php_openssl_get_evp_pkey_type(int key_type)3668 static int php_openssl_get_evp_pkey_type(int key_type) {
3669 switch (key_type) {
3670 case OPENSSL_KEYTYPE_RSA:
3671 return EVP_PKEY_RSA;
3672 #if !defined(OPENSSL_NO_DSA)
3673 case OPENSSL_KEYTYPE_DSA:
3674 return EVP_PKEY_DSA;
3675 #endif
3676 #if !defined(NO_DH)
3677 case OPENSSL_KEYTYPE_DH:
3678 return EVP_PKEY_DH;
3679 #endif
3680 #ifdef HAVE_EVP_PKEY_EC
3681 case OPENSSL_KEYTYPE_EC:
3682 return EVP_PKEY_EC;
3683 #endif
3684 default:
3685 return -1;
3686 }
3687 }
3688
3689 /* {{{ php_openssl_generate_private_key */
php_openssl_generate_private_key(struct php_x509_request * req)3690 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req)
3691 {
3692 if (req->priv_key_bits < MIN_KEY_LENGTH) {
3693 php_error_docref(NULL, E_WARNING, "Private key length must be at least %d bits, configured to %d",
3694 MIN_KEY_LENGTH, req->priv_key_bits);
3695 return NULL;
3696 }
3697
3698 int type = php_openssl_get_evp_pkey_type(req->priv_key_type);
3699 if (type < 0) {
3700 php_error_docref(NULL, E_WARNING, "Unsupported private key type");
3701 return NULL;
3702 }
3703
3704 int egdsocket, seeded;
3705 char *randfile = php_openssl_conf_get_string(req->req_config, req->section_name, "RANDFILE");
3706 php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
3707 PHP_OPENSSL_RAND_ADD_TIME();
3708
3709 EVP_PKEY *key = NULL;
3710 EVP_PKEY *params = NULL;
3711 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(type, NULL);
3712 if (!ctx) {
3713 php_openssl_store_errors();
3714 goto cleanup;
3715 }
3716
3717 if (type != EVP_PKEY_RSA) {
3718 if (EVP_PKEY_paramgen_init(ctx) <= 0) {
3719 php_openssl_store_errors();
3720 goto cleanup;
3721 }
3722
3723 switch (type) {
3724 #if !defined(OPENSSL_NO_DSA)
3725 case EVP_PKEY_DSA:
3726 if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, req->priv_key_bits) <= 0) {
3727 php_openssl_store_errors();
3728 goto cleanup;
3729 }
3730 break;
3731 #endif
3732 #if !defined(NO_DH)
3733 case EVP_PKEY_DH:
3734 if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, req->priv_key_bits) <= 0) {
3735 php_openssl_store_errors();
3736 goto cleanup;
3737 }
3738 break;
3739 #endif
3740 #ifdef HAVE_EVP_PKEY_EC
3741 case EVP_PKEY_EC:
3742 if (req->curve_name == NID_undef) {
3743 php_error_docref(NULL, E_WARNING, "Missing configuration value: \"curve_name\" not set");
3744 goto cleanup;
3745 }
3746
3747 if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, req->curve_name) <= 0 ||
3748 EVP_PKEY_CTX_set_ec_param_enc(ctx, OPENSSL_EC_NAMED_CURVE) <= 0) {
3749 php_openssl_store_errors();
3750 goto cleanup;
3751 }
3752 break;
3753 #endif
3754 EMPTY_SWITCH_DEFAULT_CASE()
3755 }
3756
3757 if (EVP_PKEY_paramgen(ctx, ¶ms) <= 0) {
3758 php_openssl_store_errors();
3759 goto cleanup;
3760 }
3761
3762 EVP_PKEY_CTX_free(ctx);
3763 ctx = EVP_PKEY_CTX_new(params, NULL);
3764 if (!ctx) {
3765 php_openssl_store_errors();
3766 goto cleanup;
3767 }
3768 }
3769
3770 if (EVP_PKEY_keygen_init(ctx) <= 0) {
3771 php_openssl_store_errors();
3772 goto cleanup;
3773 }
3774
3775 if (type == EVP_PKEY_RSA && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, req->priv_key_bits) <= 0) {
3776 php_openssl_store_errors();
3777 goto cleanup;
3778 }
3779
3780 if (EVP_PKEY_keygen(ctx, &key) <= 0) {
3781 php_openssl_store_errors();
3782 goto cleanup;
3783 }
3784
3785 req->priv_key = key;
3786
3787 cleanup:
3788 php_openssl_write_rand_file(randfile, egdsocket, seeded);
3789 EVP_PKEY_free(params);
3790 EVP_PKEY_CTX_free(ctx);
3791 return key;
3792 }
3793 /* }}} */
3794
php_openssl_add_bn_to_array(zval * ary,const BIGNUM * bn,const char * name)3795 static void php_openssl_add_bn_to_array(zval *ary, const BIGNUM *bn, const char *name) {
3796 if (bn != NULL) {
3797 int len = BN_num_bytes(bn);
3798 zend_string *str = zend_string_alloc(len, 0);
3799 BN_bn2bin(bn, (unsigned char *)ZSTR_VAL(str));
3800 ZSTR_VAL(str)[len] = 0;
3801 add_assoc_str(ary, name, str);
3802 }
3803 }
3804
3805 #define OPENSSL_PKEY_GET_BN(_type, _name) php_openssl_add_bn_to_array(&_type, _name, #_name)
3806
3807 #define OPENSSL_PKEY_SET_BN(_data, _name) do { \
3808 zval *bn; \
3809 if ((bn = zend_hash_str_find(Z_ARRVAL_P(_data), #_name, sizeof(#_name)-1)) != NULL && \
3810 Z_TYPE_P(bn) == IS_STRING) { \
3811 _name = BN_bin2bn( \
3812 (unsigned char*)Z_STRVAL_P(bn), \
3813 (int)Z_STRLEN_P(bn), NULL); \
3814 } else { \
3815 _name = NULL; \
3816 } \
3817 } while (0);
3818
3819 #if PHP_OPENSSL_API_VERSION < 0x30000
php_openssl_pkey_init_legacy_rsa(RSA * rsa,zval * data)3820 static bool php_openssl_pkey_init_legacy_rsa(RSA *rsa, zval *data)
3821 {
3822 BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
3823
3824 OPENSSL_PKEY_SET_BN(data, n);
3825 OPENSSL_PKEY_SET_BN(data, e);
3826 OPENSSL_PKEY_SET_BN(data, d);
3827 if (!n || !d || !RSA_set0_key(rsa, n, e, d)) {
3828 return 0;
3829 }
3830
3831 OPENSSL_PKEY_SET_BN(data, p);
3832 OPENSSL_PKEY_SET_BN(data, q);
3833 if ((p || q) && !RSA_set0_factors(rsa, p, q)) {
3834 return 0;
3835 }
3836
3837 OPENSSL_PKEY_SET_BN(data, dmp1);
3838 OPENSSL_PKEY_SET_BN(data, dmq1);
3839 OPENSSL_PKEY_SET_BN(data, iqmp);
3840 if ((dmp1 || dmq1 || iqmp) && !RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp)) {
3841 return 0;
3842 }
3843
3844 return 1;
3845 }
3846 #endif
3847
php_openssl_pkey_init_rsa(zval * data)3848 static EVP_PKEY *php_openssl_pkey_init_rsa(zval *data)
3849 {
3850 #if PHP_OPENSSL_API_VERSION >= 0x30000
3851 BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL;
3852 BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
3853 EVP_PKEY *pkey = NULL;
3854 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
3855 OSSL_PARAM *params = NULL;
3856 OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
3857
3858 OPENSSL_PKEY_SET_BN(data, n);
3859 OPENSSL_PKEY_SET_BN(data, e);
3860 OPENSSL_PKEY_SET_BN(data, d);
3861 OPENSSL_PKEY_SET_BN(data, p);
3862 OPENSSL_PKEY_SET_BN(data, q);
3863 OPENSSL_PKEY_SET_BN(data, dmp1);
3864 OPENSSL_PKEY_SET_BN(data, dmq1);
3865 OPENSSL_PKEY_SET_BN(data, iqmp);
3866
3867 if (!ctx || !bld || !n || !d) {
3868 goto cleanup;
3869 }
3870
3871 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n);
3872 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d);
3873 if (e) {
3874 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e);
3875 }
3876 if (p) {
3877 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p);
3878 }
3879 if (q) {
3880 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q);
3881 }
3882 if (dmp1) {
3883 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1);
3884 }
3885 if (dmq1) {
3886 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1);
3887 }
3888 if (iqmp) {
3889 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp);
3890 }
3891
3892 params = OSSL_PARAM_BLD_to_param(bld);
3893 if (!params) {
3894 goto cleanup;
3895 }
3896
3897 if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
3898 EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) {
3899 goto cleanup;
3900 }
3901
3902 cleanup:
3903 php_openssl_store_errors();
3904 EVP_PKEY_CTX_free(ctx);
3905 OSSL_PARAM_free(params);
3906 OSSL_PARAM_BLD_free(bld);
3907 BN_free(n);
3908 BN_free(e);
3909 BN_free(d);
3910 BN_free(p);
3911 BN_free(q);
3912 BN_free(dmp1);
3913 BN_free(dmq1);
3914 BN_free(iqmp);
3915 return pkey;
3916 #else
3917 EVP_PKEY *pkey = EVP_PKEY_new();
3918 if (!pkey) {
3919 php_openssl_store_errors();
3920 return NULL;
3921 }
3922
3923 RSA *rsa = RSA_new();
3924 if (!rsa) {
3925 php_openssl_store_errors();
3926 EVP_PKEY_free(pkey);
3927 return NULL;
3928 }
3929
3930 if (!php_openssl_pkey_init_legacy_rsa(rsa, data)
3931 || !EVP_PKEY_assign_RSA(pkey, rsa)) {
3932 php_openssl_store_errors();
3933 EVP_PKEY_free(pkey);
3934 RSA_free(rsa);
3935 return NULL;
3936 }
3937
3938 return pkey;
3939 #endif
3940 }
3941
3942 #if PHP_OPENSSL_API_VERSION < 0x30000
php_openssl_pkey_init_legacy_dsa(DSA * dsa,zval * data,bool * is_private)3943 static bool php_openssl_pkey_init_legacy_dsa(DSA *dsa, zval *data, bool *is_private)
3944 {
3945 BIGNUM *p, *q, *g, *priv_key, *pub_key;
3946 const BIGNUM *priv_key_const, *pub_key_const;
3947
3948 OPENSSL_PKEY_SET_BN(data, p);
3949 OPENSSL_PKEY_SET_BN(data, q);
3950 OPENSSL_PKEY_SET_BN(data, g);
3951 if (!p || !q || !g || !DSA_set0_pqg(dsa, p, q, g)) {
3952 return 0;
3953 }
3954
3955 OPENSSL_PKEY_SET_BN(data, pub_key);
3956 OPENSSL_PKEY_SET_BN(data, priv_key);
3957 *is_private = priv_key != NULL;
3958 if (pub_key) {
3959 return DSA_set0_key(dsa, pub_key, priv_key);
3960 }
3961
3962 /* generate key */
3963 PHP_OPENSSL_RAND_ADD_TIME();
3964 if (!DSA_generate_key(dsa)) {
3965 php_openssl_store_errors();
3966 return 0;
3967 }
3968
3969 /* if BN_mod_exp return -1, then DSA_generate_key succeed for failed key
3970 * so we need to double check that public key is created */
3971 DSA_get0_key(dsa, &pub_key_const, &priv_key_const);
3972 if (!pub_key_const || BN_is_zero(pub_key_const)) {
3973 return 0;
3974 }
3975 /* all good */
3976 *is_private = true;
3977 return 1;
3978 }
3979 #endif
3980
php_openssl_pkey_init_dsa(zval * data,bool * is_private)3981 static EVP_PKEY *php_openssl_pkey_init_dsa(zval *data, bool *is_private)
3982 {
3983 #if PHP_OPENSSL_API_VERSION >= 0x30000
3984 BIGNUM *p = NULL, *q = NULL, *g = NULL, *priv_key = NULL, *pub_key = NULL;
3985 EVP_PKEY *param_key = NULL, *pkey = NULL;
3986 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
3987 OSSL_PARAM *params = NULL;
3988 OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
3989
3990 OPENSSL_PKEY_SET_BN(data, p);
3991 OPENSSL_PKEY_SET_BN(data, q);
3992 OPENSSL_PKEY_SET_BN(data, g);
3993 OPENSSL_PKEY_SET_BN(data, priv_key);
3994 OPENSSL_PKEY_SET_BN(data, pub_key);
3995
3996 *is_private = false;
3997
3998 if (!ctx || !bld || !p || !q || !g) {
3999 goto cleanup;
4000 }
4001
4002 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p);
4003 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q);
4004 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g);
4005 // TODO: We silently ignore priv_key if pub_key is not given, unlike in the DH case.
4006 if (pub_key) {
4007 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_key);
4008 if (priv_key) {
4009 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv_key);
4010 }
4011 }
4012
4013 params = OSSL_PARAM_BLD_to_param(bld);
4014 if (!params) {
4015 goto cleanup;
4016 }
4017
4018 if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
4019 EVP_PKEY_fromdata(ctx, ¶m_key, EVP_PKEY_KEYPAIR, params) <= 0) {
4020 goto cleanup;
4021 }
4022
4023 if (pub_key) {
4024 *is_private = priv_key != NULL;
4025 EVP_PKEY_up_ref(param_key);
4026 pkey = param_key;
4027 } else {
4028 *is_private = true;
4029 PHP_OPENSSL_RAND_ADD_TIME();
4030 EVP_PKEY_CTX_free(ctx);
4031 ctx = EVP_PKEY_CTX_new(param_key, NULL);
4032 if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0) {
4033 goto cleanup;
4034 }
4035 }
4036
4037 cleanup:
4038 php_openssl_store_errors();
4039 EVP_PKEY_free(param_key);
4040 EVP_PKEY_CTX_free(ctx);
4041 OSSL_PARAM_free(params);
4042 OSSL_PARAM_BLD_free(bld);
4043 BN_free(p);
4044 BN_free(q);
4045 BN_free(g);
4046 BN_free(priv_key);
4047 BN_free(pub_key);
4048 return pkey;
4049 #else
4050 EVP_PKEY *pkey = EVP_PKEY_new();
4051 if (!pkey) {
4052 php_openssl_store_errors();
4053 return NULL;
4054 }
4055
4056 DSA *dsa = DSA_new();
4057 if (!dsa) {
4058 php_openssl_store_errors();
4059 EVP_PKEY_free(pkey);
4060 return NULL;
4061 }
4062
4063 if (!php_openssl_pkey_init_legacy_dsa(dsa, data, is_private)
4064 || !EVP_PKEY_assign_DSA(pkey, dsa)) {
4065 php_openssl_store_errors();
4066 EVP_PKEY_free(pkey);
4067 DSA_free(dsa);
4068 return NULL;
4069 }
4070
4071 return pkey;
4072 #endif
4073 }
4074
4075 /* {{{ php_openssl_dh_pub_from_priv */
php_openssl_dh_pub_from_priv(BIGNUM * priv_key,BIGNUM * g,BIGNUM * p)4076 static BIGNUM *php_openssl_dh_pub_from_priv(BIGNUM *priv_key, BIGNUM *g, BIGNUM *p)
4077 {
4078 BIGNUM *pub_key, *priv_key_const_time;
4079 BN_CTX *ctx;
4080
4081 pub_key = BN_new();
4082 if (pub_key == NULL) {
4083 php_openssl_store_errors();
4084 return NULL;
4085 }
4086
4087 priv_key_const_time = BN_new();
4088 if (priv_key_const_time == NULL) {
4089 BN_free(pub_key);
4090 php_openssl_store_errors();
4091 return NULL;
4092 }
4093 ctx = BN_CTX_new();
4094 if (ctx == NULL) {
4095 BN_free(pub_key);
4096 BN_free(priv_key_const_time);
4097 php_openssl_store_errors();
4098 return NULL;
4099 }
4100
4101 BN_with_flags(priv_key_const_time, priv_key, BN_FLG_CONSTTIME);
4102
4103 if (!BN_mod_exp_mont(pub_key, g, priv_key_const_time, p, ctx, NULL)) {
4104 BN_free(pub_key);
4105 php_openssl_store_errors();
4106 pub_key = NULL;
4107 }
4108
4109 BN_free(priv_key_const_time);
4110 BN_CTX_free(ctx);
4111
4112 return pub_key;
4113 }
4114 /* }}} */
4115
4116 #if PHP_OPENSSL_API_VERSION < 0x30000
php_openssl_pkey_init_legacy_dh(DH * dh,zval * data,bool * is_private)4117 static bool php_openssl_pkey_init_legacy_dh(DH *dh, zval *data, bool *is_private)
4118 {
4119 BIGNUM *p, *q, *g, *priv_key, *pub_key;
4120
4121 OPENSSL_PKEY_SET_BN(data, p);
4122 OPENSSL_PKEY_SET_BN(data, q);
4123 OPENSSL_PKEY_SET_BN(data, g);
4124 if (!p || !g || !DH_set0_pqg(dh, p, q, g)) {
4125 return 0;
4126 }
4127
4128 OPENSSL_PKEY_SET_BN(data, priv_key);
4129 OPENSSL_PKEY_SET_BN(data, pub_key);
4130 *is_private = priv_key != NULL;
4131 if (pub_key) {
4132 return DH_set0_key(dh, pub_key, priv_key);
4133 }
4134 if (priv_key) {
4135 pub_key = php_openssl_dh_pub_from_priv(priv_key, g, p);
4136 if (pub_key == NULL) {
4137 return 0;
4138 }
4139 return DH_set0_key(dh, pub_key, priv_key);
4140 }
4141
4142 /* generate key */
4143 PHP_OPENSSL_RAND_ADD_TIME();
4144 if (!DH_generate_key(dh)) {
4145 php_openssl_store_errors();
4146 return 0;
4147 }
4148 /* all good */
4149 *is_private = true;
4150 return 1;
4151 }
4152 #endif
4153
php_openssl_pkey_init_dh(zval * data,bool * is_private)4154 static EVP_PKEY *php_openssl_pkey_init_dh(zval *data, bool *is_private)
4155 {
4156 #if PHP_OPENSSL_API_VERSION >= 0x30000
4157 BIGNUM *p = NULL, *q = NULL, *g = NULL, *priv_key = NULL, *pub_key = NULL;
4158 EVP_PKEY *param_key = NULL, *pkey = NULL;
4159 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
4160 OSSL_PARAM *params = NULL;
4161 OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
4162
4163 OPENSSL_PKEY_SET_BN(data, p);
4164 OPENSSL_PKEY_SET_BN(data, q);
4165 OPENSSL_PKEY_SET_BN(data, g);
4166 OPENSSL_PKEY_SET_BN(data, priv_key);
4167 OPENSSL_PKEY_SET_BN(data, pub_key);
4168
4169 *is_private = false;
4170
4171 if (!ctx || !bld || !p || !g) {
4172 goto cleanup;
4173 }
4174
4175 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p);
4176 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g);
4177 if (q) {
4178 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q);
4179 }
4180 if (priv_key) {
4181 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv_key);
4182 if (!pub_key) {
4183 pub_key = php_openssl_dh_pub_from_priv(priv_key, g, p);
4184 if (!pub_key) {
4185 goto cleanup;
4186 }
4187 }
4188 }
4189 if (pub_key) {
4190 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_key);
4191 }
4192
4193 params = OSSL_PARAM_BLD_to_param(bld);
4194 if (!params) {
4195 goto cleanup;
4196 }
4197
4198 if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
4199 EVP_PKEY_fromdata(ctx, ¶m_key, EVP_PKEY_KEYPAIR, params) <= 0) {
4200 goto cleanup;
4201 }
4202
4203 if (pub_key || priv_key) {
4204 *is_private = priv_key != NULL;
4205 EVP_PKEY_up_ref(param_key);
4206 pkey = param_key;
4207 } else {
4208 *is_private = true;
4209 PHP_OPENSSL_RAND_ADD_TIME();
4210 EVP_PKEY_CTX_free(ctx);
4211 ctx = EVP_PKEY_CTX_new(param_key, NULL);
4212 if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0) {
4213 goto cleanup;
4214 }
4215 }
4216
4217 cleanup:
4218 php_openssl_store_errors();
4219 EVP_PKEY_free(param_key);
4220 EVP_PKEY_CTX_free(ctx);
4221 OSSL_PARAM_free(params);
4222 OSSL_PARAM_BLD_free(bld);
4223 BN_free(p);
4224 BN_free(q);
4225 BN_free(g);
4226 BN_free(priv_key);
4227 BN_free(pub_key);
4228 return pkey;
4229 #else
4230 EVP_PKEY *pkey = EVP_PKEY_new();
4231 if (!pkey) {
4232 php_openssl_store_errors();
4233 return NULL;
4234 }
4235
4236 DH *dh = DH_new();
4237 if (!dh) {
4238 EVP_PKEY_free(pkey);
4239 return NULL;
4240 }
4241
4242 if (!php_openssl_pkey_init_legacy_dh(dh, data, is_private)
4243 || !EVP_PKEY_assign_DH(pkey, dh)) {
4244 php_openssl_store_errors();
4245 EVP_PKEY_free(pkey);
4246 DH_free(dh);
4247 return NULL;
4248 }
4249
4250 return pkey;
4251 #endif
4252 }
4253
4254 #ifdef HAVE_EVP_PKEY_EC
4255 #if PHP_OPENSSL_API_VERSION < 0x30000
php_openssl_pkey_init_legacy_ec(EC_KEY * eckey,zval * data,bool * is_private)4256 static bool php_openssl_pkey_init_legacy_ec(EC_KEY *eckey, zval *data, bool *is_private) {
4257 EC_GROUP *group = NULL;
4258 EC_POINT *pnt = NULL;
4259 BIGNUM *d = NULL;
4260 zval *bn;
4261 zval *x;
4262 zval *y;
4263
4264 *is_private = false;
4265
4266 if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1)) != NULL &&
4267 Z_TYPE_P(bn) == IS_STRING) {
4268 int nid = OBJ_sn2nid(Z_STRVAL_P(bn));
4269 if (nid != NID_undef) {
4270 group = EC_GROUP_new_by_curve_name(nid);
4271 if (!group) {
4272 php_openssl_store_errors();
4273 goto clean_exit;
4274 }
4275 EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
4276 EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
4277 if (!EC_KEY_set_group(eckey, group)) {
4278 php_openssl_store_errors();
4279 goto clean_exit;
4280 }
4281 }
4282 }
4283
4284 if (group == NULL) {
4285 php_error_docref(NULL, E_WARNING, "Unknown curve name");
4286 goto clean_exit;
4287 }
4288
4289 // The public key 'pnt' can be calculated from 'd' or is defined by 'x' and 'y'
4290 if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "d", sizeof("d") - 1)) != NULL &&
4291 Z_TYPE_P(bn) == IS_STRING) {
4292 *is_private = true;
4293 d = BN_bin2bn((unsigned char*) Z_STRVAL_P(bn), Z_STRLEN_P(bn), NULL);
4294 if (!EC_KEY_set_private_key(eckey, d)) {
4295 php_openssl_store_errors();
4296 goto clean_exit;
4297 }
4298 // Calculate the public key by multiplying the Point Q with the public key
4299 // P = d * Q
4300 pnt = EC_POINT_new(group);
4301 if (!pnt || !EC_POINT_mul(group, pnt, d, NULL, NULL, NULL)) {
4302 php_openssl_store_errors();
4303 goto clean_exit;
4304 }
4305
4306 BN_free(d);
4307 } else if ((x = zend_hash_str_find(Z_ARRVAL_P(data), "x", sizeof("x") - 1)) != NULL &&
4308 Z_TYPE_P(x) == IS_STRING &&
4309 (y = zend_hash_str_find(Z_ARRVAL_P(data), "y", sizeof("y") - 1)) != NULL &&
4310 Z_TYPE_P(y) == IS_STRING) {
4311 pnt = EC_POINT_new(group);
4312 if (pnt == NULL) {
4313 php_openssl_store_errors();
4314 goto clean_exit;
4315 }
4316 if (!EC_POINT_set_affine_coordinates_GFp(
4317 group, pnt, BN_bin2bn((unsigned char*) Z_STRVAL_P(x), Z_STRLEN_P(x), NULL),
4318 BN_bin2bn((unsigned char*) Z_STRVAL_P(y), Z_STRLEN_P(y), NULL), NULL)) {
4319 php_openssl_store_errors();
4320 goto clean_exit;
4321 }
4322 }
4323
4324 if (pnt != NULL) {
4325 if (!EC_KEY_set_public_key(eckey, pnt)) {
4326 php_openssl_store_errors();
4327 goto clean_exit;
4328 }
4329 EC_POINT_free(pnt);
4330 pnt = NULL;
4331 }
4332
4333 if (!EC_KEY_check_key(eckey)) {
4334 *is_private = true;
4335 PHP_OPENSSL_RAND_ADD_TIME();
4336 EC_KEY_generate_key(eckey);
4337 php_openssl_store_errors();
4338 }
4339 if (EC_KEY_check_key(eckey)) {
4340 EC_GROUP_free(group);
4341 return true;
4342 } else {
4343 php_openssl_store_errors();
4344 }
4345
4346 clean_exit:
4347 BN_free(d);
4348 EC_POINT_free(pnt);
4349 EC_GROUP_free(group);
4350 return false;
4351 }
4352 #endif
4353
php_openssl_pkey_init_ec(zval * data,bool * is_private)4354 static EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) {
4355 #if PHP_OPENSSL_API_VERSION >= 0x30000
4356 BIGNUM *d = NULL, *x = NULL, *y = NULL;
4357 EC_GROUP *group = NULL;
4358 EC_POINT *pnt = NULL;
4359 unsigned char *pnt_oct = NULL;
4360 EVP_PKEY *param_key = NULL, *pkey = NULL;
4361 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
4362 OSSL_PARAM *params = NULL;
4363 OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
4364 zval *curve_name_zv = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1);
4365
4366 OPENSSL_PKEY_SET_BN(data, d);
4367 OPENSSL_PKEY_SET_BN(data, x);
4368 OPENSSL_PKEY_SET_BN(data, y);
4369
4370 *is_private = false;
4371
4372 if (!ctx || !bld || !curve_name_zv || Z_TYPE_P(curve_name_zv) != IS_STRING) {
4373 goto cleanup;
4374 }
4375
4376 int nid = OBJ_sn2nid(Z_STRVAL_P(curve_name_zv));
4377 group = EC_GROUP_new_by_curve_name(nid);
4378 if (!group) {
4379 php_error_docref(NULL, E_WARNING, "Unknown curve name");
4380 goto cleanup;
4381 }
4382
4383 OSSL_PARAM_BLD_push_utf8_string(
4384 bld, OSSL_PKEY_PARAM_GROUP_NAME, Z_STRVAL_P(curve_name_zv), Z_STRLEN_P(curve_name_zv));
4385
4386 if (d) {
4387 OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, d);
4388
4389 pnt = EC_POINT_new(group);
4390 if (!pnt || !EC_POINT_mul(group, pnt, d, NULL, NULL, NULL)) {
4391 goto cleanup;
4392 }
4393 } else if (x && y) {
4394 /* OpenSSL does not allow setting EC_PUB_X/EC_PUB_Y, so convert to encoded format. */
4395 pnt = EC_POINT_new(group);
4396 if (!pnt || !EC_POINT_set_affine_coordinates(group, pnt, x, y, NULL)) {
4397 goto cleanup;
4398 }
4399 }
4400
4401 if (pnt) {
4402 size_t pnt_oct_len =
4403 EC_POINT_point2buf(group, pnt, POINT_CONVERSION_COMPRESSED, &pnt_oct, NULL);
4404 if (!pnt_oct_len) {
4405 goto cleanup;
4406 }
4407
4408 OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pnt_oct, pnt_oct_len);
4409 }
4410
4411 params = OSSL_PARAM_BLD_to_param(bld);
4412 if (!params) {
4413 goto cleanup;
4414 }
4415
4416 if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
4417 EVP_PKEY_fromdata(ctx, ¶m_key, EVP_PKEY_KEYPAIR, params) <= 0) {
4418 goto cleanup;
4419 }
4420
4421 EVP_PKEY_CTX_free(ctx);
4422 ctx = EVP_PKEY_CTX_new(param_key, NULL);
4423 if (EVP_PKEY_check(ctx)) {
4424 *is_private = d != NULL;
4425 EVP_PKEY_up_ref(param_key);
4426 pkey = param_key;
4427 } else {
4428 *is_private = true;
4429 PHP_OPENSSL_RAND_ADD_TIME();
4430 if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0) {
4431 goto cleanup;
4432 }
4433 }
4434
4435 cleanup:
4436 php_openssl_store_errors();
4437 EVP_PKEY_free(param_key);
4438 EVP_PKEY_CTX_free(ctx);
4439 OSSL_PARAM_free(params);
4440 OSSL_PARAM_BLD_free(bld);
4441 EC_POINT_free(pnt);
4442 EC_GROUP_free(group);
4443 OPENSSL_free(pnt_oct);
4444 BN_free(d);
4445 BN_free(x);
4446 BN_free(y);
4447 return pkey;
4448 #else
4449 EVP_PKEY *pkey = EVP_PKEY_new();
4450 if (!pkey) {
4451 php_openssl_store_errors();
4452 return NULL;
4453 }
4454
4455 EC_KEY *ec = EC_KEY_new();
4456 if (!ec) {
4457 EVP_PKEY_free(pkey);
4458 return NULL;
4459 }
4460
4461 if (!php_openssl_pkey_init_legacy_ec(ec, data, is_private)
4462 || !EVP_PKEY_assign_EC_KEY(pkey, ec)) {
4463 php_openssl_store_errors();
4464 EVP_PKEY_free(pkey);
4465 EC_KEY_free(ec);
4466 return NULL;
4467 }
4468
4469 return pkey;
4470 #endif
4471 }
4472 #endif
4473
4474 /* {{{ Generates a new private key */
PHP_FUNCTION(openssl_pkey_new)4475 PHP_FUNCTION(openssl_pkey_new)
4476 {
4477 struct php_x509_request req;
4478 zval * args = NULL;
4479 zval *data;
4480
4481 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &args) == FAILURE) {
4482 RETURN_THROWS();
4483 }
4484 RETVAL_FALSE;
4485
4486 if (args && Z_TYPE_P(args) == IS_ARRAY) {
4487 EVP_PKEY *pkey;
4488
4489 if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa")-1)) != NULL &&
4490 Z_TYPE_P(data) == IS_ARRAY) {
4491 pkey = php_openssl_pkey_init_rsa(data);
4492 if (!pkey) {
4493 RETURN_FALSE;
4494 }
4495 php_openssl_pkey_object_init(return_value, pkey, /* is_private */ true);
4496 return;
4497 } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa") - 1)) != NULL &&
4498 Z_TYPE_P(data) == IS_ARRAY) {
4499 bool is_private;
4500 pkey = php_openssl_pkey_init_dsa(data, &is_private);
4501 if (!pkey) {
4502 RETURN_FALSE;
4503 }
4504 php_openssl_pkey_object_init(return_value, pkey, is_private);
4505 return;
4506 } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dh", sizeof("dh") - 1)) != NULL &&
4507 Z_TYPE_P(data) == IS_ARRAY) {
4508 bool is_private;
4509 pkey = php_openssl_pkey_init_dh(data, &is_private);
4510 if (!pkey) {
4511 RETURN_FALSE;
4512 }
4513 php_openssl_pkey_object_init(return_value, pkey, is_private);
4514 return;
4515 #ifdef HAVE_EVP_PKEY_EC
4516 } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "ec", sizeof("ec") - 1)) != NULL &&
4517 Z_TYPE_P(data) == IS_ARRAY) {
4518 bool is_private;
4519 pkey = php_openssl_pkey_init_ec(data, &is_private);
4520 if (!pkey) {
4521 RETURN_FALSE;
4522 }
4523 php_openssl_pkey_object_init(return_value, pkey, is_private);
4524 return;
4525 #endif
4526 }
4527 }
4528
4529 PHP_SSL_REQ_INIT(&req);
4530
4531 if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4532 if (php_openssl_generate_private_key(&req)) {
4533 /* pass back a key resource */
4534 php_openssl_pkey_object_init(return_value, req.priv_key, /* is_private */ true);
4535 /* make sure the cleanup code doesn't zap it! */
4536 req.priv_key = NULL;
4537 }
4538 }
4539 PHP_SSL_REQ_DISPOSE(&req);
4540 }
4541 /* }}} */
4542
4543 /* {{{ Gets an exportable representation of a key into a file */
PHP_FUNCTION(openssl_pkey_export_to_file)4544 PHP_FUNCTION(openssl_pkey_export_to_file)
4545 {
4546 struct php_x509_request req;
4547 zval * zpkey, * args = NULL;
4548 char * passphrase = NULL;
4549 size_t passphrase_len = 0;
4550 char * filename = NULL, file_path[MAXPATHLEN];
4551 size_t filename_len = 0;
4552 int pem_write = 0;
4553 EVP_PKEY * key;
4554 BIO * bio_out = NULL;
4555 const EVP_CIPHER * cipher;
4556
4557 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zp|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) {
4558 RETURN_THROWS();
4559 }
4560 RETVAL_FALSE;
4561
4562 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase, 3);
4563
4564 key = php_openssl_pkey_from_zval(zpkey, 0, passphrase, passphrase_len, 1);
4565 if (key == NULL) {
4566 if (!EG(exception)) {
4567 php_error_docref(NULL, E_WARNING, "Cannot get key from parameter 1");
4568 }
4569 RETURN_FALSE;
4570 }
4571
4572 if (!php_openssl_check_path(filename, filename_len, file_path, 2)) {
4573 goto clean_exit_key;
4574 }
4575
4576 PHP_SSL_REQ_INIT(&req);
4577
4578 if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4579 bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
4580 if (bio_out == NULL) {
4581 php_openssl_store_errors();
4582 goto clean_exit;
4583 }
4584
4585 if (passphrase && req.priv_key_encrypt) {
4586 if (req.priv_key_encrypt_cipher) {
4587 cipher = req.priv_key_encrypt_cipher;
4588 } else {
4589 cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
4590 }
4591 } else {
4592 cipher = NULL;
4593 }
4594
4595 pem_write = PEM_write_bio_PrivateKey(
4596 bio_out, key, cipher,
4597 (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4598 if (pem_write) {
4599 /* Success!
4600 * If returning the output as a string, do so now */
4601 RETVAL_TRUE;
4602 } else {
4603 php_openssl_store_errors();
4604 }
4605 }
4606
4607 clean_exit:
4608 PHP_SSL_REQ_DISPOSE(&req);
4609 BIO_free(bio_out);
4610 clean_exit_key:
4611 EVP_PKEY_free(key);
4612 }
4613 /* }}} */
4614
4615 /* {{{ Gets an exportable representation of a key into a string or file */
PHP_FUNCTION(openssl_pkey_export)4616 PHP_FUNCTION(openssl_pkey_export)
4617 {
4618 struct php_x509_request req;
4619 zval * zpkey, * args = NULL, *out;
4620 char * passphrase = NULL; size_t passphrase_len = 0;
4621 int pem_write = 0;
4622 EVP_PKEY * key;
4623 BIO * bio_out = NULL;
4624 const EVP_CIPHER * cipher;
4625
4626 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) {
4627 RETURN_THROWS();
4628 }
4629 RETVAL_FALSE;
4630
4631 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase, 3);
4632
4633 key = php_openssl_pkey_from_zval(zpkey, 0, passphrase, passphrase_len, 1);
4634 if (key == NULL) {
4635 if (!EG(exception)) {
4636 php_error_docref(NULL, E_WARNING, "Cannot get key from parameter 1");
4637 }
4638 RETURN_FALSE;
4639 }
4640
4641 PHP_SSL_REQ_INIT(&req);
4642
4643 if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
4644 bio_out = BIO_new(BIO_s_mem());
4645
4646 if (passphrase && req.priv_key_encrypt) {
4647 if (req.priv_key_encrypt_cipher) {
4648 cipher = req.priv_key_encrypt_cipher;
4649 } else {
4650 cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
4651 }
4652 } else {
4653 cipher = NULL;
4654 }
4655
4656 pem_write = PEM_write_bio_PrivateKey(
4657 bio_out, key, cipher,
4658 (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
4659 if (pem_write) {
4660 /* Success!
4661 * If returning the output as a string, do so now */
4662
4663 char * bio_mem_ptr;
4664 long bio_mem_len;
4665 RETVAL_TRUE;
4666
4667 bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
4668 ZEND_TRY_ASSIGN_REF_STRINGL(out, bio_mem_ptr, bio_mem_len);
4669 } else {
4670 php_openssl_store_errors();
4671 }
4672 }
4673 PHP_SSL_REQ_DISPOSE(&req);
4674 EVP_PKEY_free(key);
4675 BIO_free(bio_out);
4676 }
4677 /* }}} */
4678
4679 /* {{{ Gets public key from X.509 certificate */
PHP_FUNCTION(openssl_pkey_get_public)4680 PHP_FUNCTION(openssl_pkey_get_public)
4681 {
4682 zval *cert;
4683 EVP_PKEY *pkey;
4684
4685 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &cert) == FAILURE) {
4686 RETURN_THROWS();
4687 }
4688 pkey = php_openssl_pkey_from_zval(cert, 1, NULL, 0, 1);
4689 if (pkey == NULL) {
4690 RETURN_FALSE;
4691 }
4692
4693 php_openssl_pkey_object_init(return_value, pkey, /* is_private */ false);
4694 }
4695 /* }}} */
4696
4697 /* {{{ Frees a key */
PHP_FUNCTION(openssl_pkey_free)4698 PHP_FUNCTION(openssl_pkey_free)
4699 {
4700 zval *key;
4701
4702 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &key, php_openssl_pkey_ce) == FAILURE) {
4703 RETURN_THROWS();
4704 }
4705 }
4706 /* }}} */
4707
4708 /* {{{ Gets private keys */
PHP_FUNCTION(openssl_pkey_get_private)4709 PHP_FUNCTION(openssl_pkey_get_private)
4710 {
4711 zval *cert;
4712 EVP_PKEY *pkey;
4713 char * passphrase = "";
4714 size_t passphrase_len = sizeof("")-1;
4715
4716 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!", &cert, &passphrase, &passphrase_len) == FAILURE) {
4717 RETURN_THROWS();
4718 }
4719
4720 if (passphrase) {
4721 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase, 2);
4722 }
4723
4724 pkey = php_openssl_pkey_from_zval(cert, 0, passphrase, passphrase_len, 1);
4725 if (pkey == NULL) {
4726 RETURN_FALSE;
4727 }
4728
4729 php_openssl_pkey_object_init(return_value, pkey, /* is_private */ true);
4730 }
4731
4732 /* }}} */
4733
4734 #if PHP_OPENSSL_API_VERSION >= 0x30000
php_openssl_copy_bn_param(zval * ary,EVP_PKEY * pkey,const char * param,const char * name)4735 static void php_openssl_copy_bn_param(
4736 zval *ary, EVP_PKEY *pkey, const char *param, const char *name) {
4737 BIGNUM *bn = NULL;
4738 if (EVP_PKEY_get_bn_param(pkey, param, &bn) > 0) {
4739 php_openssl_add_bn_to_array(ary, bn, name);
4740 BN_free(bn);
4741 }
4742 }
4743
4744 #ifdef HAVE_EVP_PKEY_EC
php_openssl_get_utf8_param(EVP_PKEY * pkey,const char * param,const char * name)4745 static zend_string *php_openssl_get_utf8_param(
4746 EVP_PKEY *pkey, const char *param, const char *name) {
4747 char buf[64];
4748 size_t len;
4749 if (EVP_PKEY_get_utf8_string_param(pkey, param, buf, sizeof(buf), &len) > 0) {
4750 zend_string *str = zend_string_alloc(len, 0);
4751 memcpy(ZSTR_VAL(str), buf, len);
4752 ZSTR_VAL(str)[len] = '\0';
4753 return str;
4754 }
4755 return NULL;
4756 }
4757 #endif
4758 #endif
4759
4760 /* {{{ returns an array with the key details (bits, pkey, type)*/
PHP_FUNCTION(openssl_pkey_get_details)4761 PHP_FUNCTION(openssl_pkey_get_details)
4762 {
4763 zval *key;
4764 unsigned int pbio_len;
4765 char *pbio;
4766 zend_long ktype;
4767
4768 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &key, php_openssl_pkey_ce) == FAILURE) {
4769 RETURN_THROWS();
4770 }
4771
4772 EVP_PKEY *pkey = Z_OPENSSL_PKEY_P(key)->pkey;
4773
4774 BIO *out = BIO_new(BIO_s_mem());
4775 if (!PEM_write_bio_PUBKEY(out, pkey)) {
4776 BIO_free(out);
4777 php_openssl_store_errors();
4778 RETURN_FALSE;
4779 }
4780 pbio_len = BIO_get_mem_data(out, &pbio);
4781
4782 array_init(return_value);
4783 add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey));
4784 add_assoc_stringl(return_value, "key", pbio, pbio_len);
4785 /*TODO: Use the real values once the openssl constants are used
4786 * See the enum at the top of this file
4787 */
4788 #if PHP_OPENSSL_API_VERSION >= 0x30000
4789 zval ary;
4790 switch (EVP_PKEY_base_id(pkey)) {
4791 case EVP_PKEY_RSA:
4792 ktype = OPENSSL_KEYTYPE_RSA;
4793 array_init(&ary);
4794 add_assoc_zval(return_value, "rsa", &ary);
4795 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_N, "n");
4796 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_E, "e");
4797 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_D, "d");
4798 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, "p");
4799 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, "q");
4800 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_EXPONENT1, "dmp1");
4801 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_EXPONENT2, "dmq1");
4802 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, "iqmp");
4803 break;
4804 case EVP_PKEY_DSA:
4805 ktype = OPENSSL_KEYTYPE_DSA;
4806 array_init(&ary);
4807 add_assoc_zval(return_value, "dsa", &ary);
4808 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_P, "p");
4809 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_Q, "q");
4810 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_G, "g");
4811 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PRIV_KEY, "priv_key");
4812 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PUB_KEY, "pub_key");
4813 break;
4814 case EVP_PKEY_DH:
4815 ktype = OPENSSL_KEYTYPE_DH;
4816 array_init(&ary);
4817 add_assoc_zval(return_value, "dh", &ary);
4818 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_P, "p");
4819 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_G, "g");
4820 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PRIV_KEY, "priv_key");
4821 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PUB_KEY, "pub_key");
4822 break;
4823 #ifdef HAVE_EVP_PKEY_EC
4824 case EVP_PKEY_EC: {
4825 ktype = OPENSSL_KEYTYPE_EC;
4826 array_init(&ary);
4827 add_assoc_zval(return_value, "ec", &ary);
4828
4829 zend_string *curve_name = php_openssl_get_utf8_param(
4830 pkey, OSSL_PKEY_PARAM_GROUP_NAME, "curve_name");
4831 if (curve_name) {
4832 add_assoc_str(&ary, "curve_name", curve_name);
4833
4834 int nid = OBJ_sn2nid(ZSTR_VAL(curve_name));
4835 if (nid != NID_undef) {
4836 ASN1_OBJECT *obj = OBJ_nid2obj(nid);
4837 if (obj) {
4838 // OpenSSL recommends a buffer length of 80.
4839 char oir_buf[80];
4840 int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
4841 add_assoc_stringl(&ary, "curve_oid", oir_buf, oir_len);
4842 ASN1_OBJECT_free(obj);
4843 }
4844 }
4845 }
4846
4847 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_EC_PUB_X, "x");
4848 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_EC_PUB_Y, "y");
4849 php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PRIV_KEY, "d");
4850 break;
4851 }
4852 #endif
4853 EMPTY_SWITCH_DEFAULT_CASE();
4854 }
4855 #else
4856 switch (EVP_PKEY_base_id(pkey)) {
4857 case EVP_PKEY_RSA:
4858 case EVP_PKEY_RSA2:
4859 {
4860 RSA *rsa = EVP_PKEY_get0_RSA(pkey);
4861 ktype = OPENSSL_KEYTYPE_RSA;
4862
4863 if (rsa != NULL) {
4864 zval z_rsa;
4865 const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
4866
4867 RSA_get0_key(rsa, &n, &e, &d);
4868 RSA_get0_factors(rsa, &p, &q);
4869 RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
4870
4871 array_init(&z_rsa);
4872 OPENSSL_PKEY_GET_BN(z_rsa, n);
4873 OPENSSL_PKEY_GET_BN(z_rsa, e);
4874 OPENSSL_PKEY_GET_BN(z_rsa, d);
4875 OPENSSL_PKEY_GET_BN(z_rsa, p);
4876 OPENSSL_PKEY_GET_BN(z_rsa, q);
4877 OPENSSL_PKEY_GET_BN(z_rsa, dmp1);
4878 OPENSSL_PKEY_GET_BN(z_rsa, dmq1);
4879 OPENSSL_PKEY_GET_BN(z_rsa, iqmp);
4880 add_assoc_zval(return_value, "rsa", &z_rsa);
4881 }
4882 }
4883 break;
4884 case EVP_PKEY_DSA:
4885 case EVP_PKEY_DSA2:
4886 case EVP_PKEY_DSA3:
4887 case EVP_PKEY_DSA4:
4888 {
4889 DSA *dsa = EVP_PKEY_get0_DSA(pkey);
4890 ktype = OPENSSL_KEYTYPE_DSA;
4891
4892 if (dsa != NULL) {
4893 zval z_dsa;
4894 const BIGNUM *p, *q, *g, *priv_key, *pub_key;
4895
4896 DSA_get0_pqg(dsa, &p, &q, &g);
4897 DSA_get0_key(dsa, &pub_key, &priv_key);
4898
4899 array_init(&z_dsa);
4900 OPENSSL_PKEY_GET_BN(z_dsa, p);
4901 OPENSSL_PKEY_GET_BN(z_dsa, q);
4902 OPENSSL_PKEY_GET_BN(z_dsa, g);
4903 OPENSSL_PKEY_GET_BN(z_dsa, priv_key);
4904 OPENSSL_PKEY_GET_BN(z_dsa, pub_key);
4905 add_assoc_zval(return_value, "dsa", &z_dsa);
4906 }
4907 }
4908 break;
4909 case EVP_PKEY_DH:
4910 {
4911 DH *dh = EVP_PKEY_get0_DH(pkey);
4912 ktype = OPENSSL_KEYTYPE_DH;
4913
4914 if (dh != NULL) {
4915 zval z_dh;
4916 const BIGNUM *p, *q, *g, *priv_key, *pub_key;
4917
4918 DH_get0_pqg(dh, &p, &q, &g);
4919 DH_get0_key(dh, &pub_key, &priv_key);
4920
4921 array_init(&z_dh);
4922 OPENSSL_PKEY_GET_BN(z_dh, p);
4923 OPENSSL_PKEY_GET_BN(z_dh, g);
4924 OPENSSL_PKEY_GET_BN(z_dh, priv_key);
4925 OPENSSL_PKEY_GET_BN(z_dh, pub_key);
4926 add_assoc_zval(return_value, "dh", &z_dh);
4927 }
4928 }
4929 break;
4930 #ifdef HAVE_EVP_PKEY_EC
4931 case EVP_PKEY_EC:
4932 ktype = OPENSSL_KEYTYPE_EC;
4933 if (EVP_PKEY_get0_EC_KEY(pkey) != NULL) {
4934 zval ec;
4935 const EC_GROUP *ec_group;
4936 const EC_POINT *pub;
4937 int nid;
4938 char *crv_sn;
4939 ASN1_OBJECT *obj;
4940 // openssl recommends a buffer length of 80
4941 char oir_buf[80];
4942 const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
4943 BIGNUM *x = BN_new();
4944 BIGNUM *y = BN_new();
4945 const BIGNUM *d;
4946
4947 ec_group = EC_KEY_get0_group(ec_key);
4948
4949 // Curve nid (numerical identifier) used for ASN1 mapping
4950 nid = EC_GROUP_get_curve_name(ec_group);
4951 if (nid == NID_undef) {
4952 break;
4953 }
4954 array_init(&ec);
4955
4956 // Short object name
4957 crv_sn = (char*) OBJ_nid2sn(nid);
4958 if (crv_sn != NULL) {
4959 add_assoc_string(&ec, "curve_name", crv_sn);
4960 }
4961
4962 obj = OBJ_nid2obj(nid);
4963 if (obj != NULL) {
4964 int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
4965 add_assoc_stringl(&ec, "curve_oid", (char*) oir_buf, oir_len);
4966 ASN1_OBJECT_free(obj);
4967 }
4968
4969 pub = EC_KEY_get0_public_key(ec_key);
4970
4971 if (EC_POINT_get_affine_coordinates_GFp(ec_group, pub, x, y, NULL)) {
4972 php_openssl_add_bn_to_array(&ec, x, "x");
4973 php_openssl_add_bn_to_array(&ec, y, "y");
4974 } else {
4975 php_openssl_store_errors();
4976 }
4977
4978 if ((d = EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(pkey))) != NULL) {
4979 php_openssl_add_bn_to_array(&ec, d, "d");
4980 }
4981
4982 add_assoc_zval(return_value, "ec", &ec);
4983
4984 BN_free(x);
4985 BN_free(y);
4986 }
4987 break;
4988 #endif
4989 default:
4990 ktype = -1;
4991 break;
4992 }
4993 #endif
4994 add_assoc_long(return_value, "type", ktype);
4995
4996 BIO_free(out);
4997 }
4998 /* }}} */
4999
php_openssl_pkey_derive(EVP_PKEY * key,EVP_PKEY * peer_key,size_t key_size)5000 static zend_string *php_openssl_pkey_derive(EVP_PKEY *key, EVP_PKEY *peer_key, size_t key_size) {
5001 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key, NULL);
5002 if (!ctx) {
5003 return NULL;
5004 }
5005
5006 if (EVP_PKEY_derive_init(ctx) <= 0 ||
5007 EVP_PKEY_derive_set_peer(ctx, peer_key) <= 0 ||
5008 (key_size == 0 && EVP_PKEY_derive(ctx, NULL, &key_size) <= 0)) {
5009 php_openssl_store_errors();
5010 EVP_PKEY_CTX_free(ctx);
5011 return NULL;
5012 }
5013
5014 zend_string *result = zend_string_alloc(key_size, 0);
5015 if (EVP_PKEY_derive(ctx, (unsigned char *)ZSTR_VAL(result), &key_size) <= 0) {
5016 php_openssl_store_errors();
5017 zend_string_release_ex(result, 0);
5018 EVP_PKEY_CTX_free(ctx);
5019 return NULL;
5020 }
5021
5022 ZSTR_LEN(result) = key_size;
5023 ZSTR_VAL(result)[key_size] = 0;
5024 EVP_PKEY_CTX_free(ctx);
5025 return result;
5026 }
5027
php_openssl_dh_compute_key(EVP_PKEY * pkey,char * pub_str,size_t pub_len)5028 static zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pub_len) {
5029 #if PHP_OPENSSL_API_VERSION >= 0x30000
5030 EVP_PKEY *peer_key = EVP_PKEY_new();
5031 if (!peer_key || EVP_PKEY_copy_parameters(peer_key, pkey) <= 0 ||
5032 EVP_PKEY_set1_encoded_public_key(peer_key, (unsigned char *) pub_str, pub_len) <= 0) {
5033 php_openssl_store_errors();
5034 EVP_PKEY_free(peer_key);
5035 return NULL;
5036 }
5037
5038 zend_string *result = php_openssl_pkey_derive(pkey, peer_key, 0);
5039 EVP_PKEY_free(peer_key);
5040 return result;
5041 #else
5042 DH *dh = EVP_PKEY_get0_DH(pkey);
5043 if (dh == NULL) {
5044 return NULL;
5045 }
5046
5047 BIGNUM *pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
5048 zend_string *data = zend_string_alloc(DH_size(dh), 0);
5049 int len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, dh);
5050 BN_free(pub);
5051
5052 if (len < 0) {
5053 php_openssl_store_errors();
5054 zend_string_release_ex(data, 0);
5055 return NULL;
5056 }
5057
5058 ZSTR_LEN(data) = len;
5059 ZSTR_VAL(data)[len] = 0;
5060 return data;
5061 #endif
5062 }
5063
5064 /* {{{ Computes shared secret for public value of remote DH key and local DH key */
PHP_FUNCTION(openssl_dh_compute_key)5065 PHP_FUNCTION(openssl_dh_compute_key)
5066 {
5067 zval *key;
5068 char *pub_str;
5069 size_t pub_len;
5070
5071 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sO", &pub_str, &pub_len, &key, php_openssl_pkey_ce) == FAILURE) {
5072 RETURN_THROWS();
5073 }
5074
5075 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(pub_len, pub_key, 1);
5076
5077 EVP_PKEY *pkey = Z_OPENSSL_PKEY_P(key)->pkey;
5078 if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) {
5079 RETURN_FALSE;
5080 }
5081
5082 zend_string *result = php_openssl_dh_compute_key(pkey, pub_str, pub_len);
5083 if (result) {
5084 RETURN_NEW_STR(result);
5085 } else {
5086 RETURN_FALSE;
5087 }
5088 }
5089 /* }}} */
5090
5091 /* {{{ Computes shared secret for public value of remote and local DH or ECDH key */
PHP_FUNCTION(openssl_pkey_derive)5092 PHP_FUNCTION(openssl_pkey_derive)
5093 {
5094 zval *priv_key;
5095 zval *peer_pub_key;
5096 zend_long key_len = 0;
5097
5098 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &peer_pub_key, &priv_key, &key_len) == FAILURE) {
5099 RETURN_THROWS();
5100 }
5101
5102 if (key_len < 0) {
5103 zend_argument_value_error(3, "must be greater than or equal to 0");
5104 RETURN_THROWS();
5105 }
5106
5107 EVP_PKEY *pkey = php_openssl_pkey_from_zval(priv_key, 0, "", 0, 2);
5108 if (!pkey) {
5109 RETURN_FALSE;
5110 }
5111
5112 EVP_PKEY *peer_key = php_openssl_pkey_from_zval(peer_pub_key, 1, NULL, 0, 1);
5113 if (!peer_key) {
5114 EVP_PKEY_free(pkey);
5115 RETURN_FALSE;
5116 }
5117
5118 zend_string *result = php_openssl_pkey_derive(pkey, peer_key, key_len);
5119 EVP_PKEY_free(pkey);
5120 EVP_PKEY_free(peer_key);
5121
5122 if (result) {
5123 RETURN_NEW_STR(result);
5124 } else {
5125 RETURN_FALSE;
5126 }
5127 }
5128 /* }}} */
5129
5130
5131 /* {{{ Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
PHP_FUNCTION(openssl_pbkdf2)5132 PHP_FUNCTION(openssl_pbkdf2)
5133 {
5134 zend_long key_length = 0, iterations = 0;
5135 char *password;
5136 size_t password_len;
5137 char *salt;
5138 size_t salt_len;
5139 char *method;
5140 size_t method_len = 0;
5141 zend_string *out_buffer;
5142
5143 const EVP_MD *digest;
5144
5145 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssll|s",
5146 &password, &password_len,
5147 &salt, &salt_len,
5148 &key_length, &iterations,
5149 &method, &method_len) == FAILURE) {
5150 RETURN_THROWS();
5151 }
5152
5153 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password, 1);
5154 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(salt_len, salt, 2);
5155 PHP_OPENSSL_CHECK_LONG_TO_INT(key_length, key, 3);
5156 PHP_OPENSSL_CHECK_LONG_TO_INT(iterations, iterations, 4);
5157
5158 if (key_length <= 0) {
5159 zend_argument_value_error(3, "must be greater than 0");
5160 RETURN_THROWS();
5161 }
5162
5163 if (method_len) {
5164 digest = EVP_get_digestbyname(method);
5165 } else {
5166 digest = EVP_sha1();
5167 }
5168
5169 if (!digest) {
5170 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
5171 RETURN_FALSE;
5172 }
5173
5174 out_buffer = zend_string_alloc(key_length, 0);
5175
5176 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) {
5177 ZSTR_VAL(out_buffer)[key_length] = 0;
5178 RETURN_NEW_STR(out_buffer);
5179 } else {
5180 php_openssl_store_errors();
5181 zend_string_release_ex(out_buffer, 0);
5182 RETURN_FALSE;
5183 }
5184 }
5185 /* }}} */
5186
5187 /** openssl bio new file helper */
php_openssl_bio_new_file(const char * filename,size_t filename_len,uint32_t arg_num,const char * mode)5188 static BIO *php_openssl_bio_new_file(
5189 const char *filename, size_t filename_len, uint32_t arg_num, const char *mode) {
5190 char file_path[MAXPATHLEN];
5191 BIO *bio;
5192
5193 if (!php_openssl_check_path(filename, filename_len, file_path, arg_num)) {
5194 return NULL;
5195 }
5196
5197 bio = BIO_new_file(file_path, mode);
5198 if (bio == NULL) {
5199 php_openssl_store_errors();
5200 return NULL;
5201 }
5202
5203 return bio;
5204 }
5205
5206 /* {{{ PKCS7 S/MIME functions */
5207
5208 /* {{{ 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)5209 PHP_FUNCTION(openssl_pkcs7_verify)
5210 {
5211 X509_STORE * store = NULL;
5212 zval * cainfo = NULL;
5213 STACK_OF(X509) *signers= NULL;
5214 STACK_OF(X509) *others = NULL;
5215 PKCS7 * p7 = NULL;
5216 BIO * in = NULL, * datain = NULL, * dataout = NULL, * p7bout = NULL;
5217 zend_long flags = 0;
5218 char * filename;
5219 size_t filename_len;
5220 char * extracerts = NULL;
5221 size_t extracerts_len = 0;
5222 char * signersfilename = NULL;
5223 size_t signersfilename_len = 0;
5224 char * datafilename = NULL;
5225 size_t datafilename_len = 0;
5226 char * p7bfilename = NULL;
5227 size_t p7bfilename_len = 0;
5228
5229 RETVAL_LONG(-1);
5230
5231 if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|p!ap!p!p!", &filename, &filename_len,
5232 &flags, &signersfilename, &signersfilename_len, &cainfo,
5233 &extracerts, &extracerts_len, &datafilename, &datafilename_len, &p7bfilename, &p7bfilename_len) == FAILURE) {
5234 RETURN_THROWS();
5235 }
5236
5237 if (extracerts) {
5238 others = php_openssl_load_all_certs_from_file(extracerts, extracerts_len, 5);
5239 if (others == NULL) {
5240 goto clean_exit;
5241 }
5242 }
5243
5244 flags = flags & ~PKCS7_DETACHED;
5245
5246 store = php_openssl_setup_verify(cainfo, 4);
5247
5248 if (!store) {
5249 goto clean_exit;
5250 }
5251
5252 in = php_openssl_bio_new_file(filename, filename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5253 if (in == NULL) {
5254 goto clean_exit;
5255 }
5256
5257 p7 = SMIME_read_PKCS7(in, &datain);
5258 if (p7 == NULL) {
5259 #if DEBUG_SMIME
5260 zend_printf("SMIME_read_PKCS7 failed\n");
5261 #endif
5262 php_openssl_store_errors();
5263 goto clean_exit;
5264 }
5265 if (datafilename) {
5266 dataout = php_openssl_bio_new_file(
5267 datafilename, datafilename_len, 6, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5268 if (dataout == NULL) {
5269 goto clean_exit;
5270 }
5271 }
5272 if (p7bfilename) {
5273 p7bout = php_openssl_bio_new_file(
5274 p7bfilename, p7bfilename_len, 7, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5275 if (p7bout == NULL) {
5276 goto clean_exit;
5277 }
5278 }
5279 #if DEBUG_SMIME
5280 zend_printf("Calling PKCS7 verify\n");
5281 #endif
5282
5283 if (PKCS7_verify(p7, others, store, datain, dataout, (int)flags)) {
5284
5285 RETVAL_TRUE;
5286
5287 if (signersfilename) {
5288 BIO *certout;
5289
5290 certout = php_openssl_bio_new_file(
5291 signersfilename, signersfilename_len, 3, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5292 if (certout) {
5293 int i;
5294 signers = PKCS7_get0_signers(p7, others, (int)flags);
5295 if (signers != NULL) {
5296
5297 for (i = 0; i < sk_X509_num(signers); i++) {
5298 if (!PEM_write_bio_X509(certout, sk_X509_value(signers, i))) {
5299 php_openssl_store_errors();
5300 RETVAL_LONG(-1);
5301 php_error_docref(NULL, E_WARNING, "Failed to write signer %d", i);
5302 }
5303 }
5304
5305 sk_X509_free(signers);
5306 } else {
5307 RETVAL_LONG(-1);
5308 php_openssl_store_errors();
5309 }
5310
5311 BIO_free(certout);
5312 } else {
5313 php_error_docref(NULL, E_WARNING, "Signature OK, but cannot open %s for writing", signersfilename);
5314 RETVAL_LONG(-1);
5315 }
5316
5317 if (p7bout) {
5318 if (PEM_write_bio_PKCS7(p7bout, p7) == 0) {
5319 php_error_docref(NULL, E_WARNING, "Failed to write PKCS7 to file");
5320 php_openssl_store_errors();
5321 RETVAL_FALSE;
5322 }
5323 }
5324 }
5325 } else {
5326 php_openssl_store_errors();
5327 RETVAL_FALSE;
5328 }
5329 clean_exit:
5330 if (p7bout) {
5331 BIO_free(p7bout);
5332 }
5333 X509_STORE_free(store);
5334 BIO_free(datain);
5335 BIO_free(in);
5336 BIO_free(dataout);
5337 PKCS7_free(p7);
5338 sk_X509_pop_free(others, X509_free);
5339 }
5340 /* }}} */
5341
5342 /* {{{ 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)5343 PHP_FUNCTION(openssl_pkcs7_encrypt)
5344 {
5345 zval * zrecipcerts, * zheaders = NULL;
5346 STACK_OF(X509) * recipcerts = NULL;
5347 BIO * infile = NULL, * outfile = NULL;
5348 zend_long flags = 0;
5349 PKCS7 * p7 = NULL;
5350 zval * zcertval;
5351 X509 * cert;
5352 const EVP_CIPHER *cipher = NULL;
5353 zend_long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
5354 zend_string * strindex;
5355 char * infilename = NULL;
5356 size_t infilename_len;
5357 char * outfilename = NULL;
5358 size_t outfilename_len;
5359
5360 RETVAL_FALSE;
5361
5362 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppza!|ll", &infilename, &infilename_len,
5363 &outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE) {
5364 RETURN_THROWS();
5365 }
5366
5367 infile = php_openssl_bio_new_file(infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5368 if (infile == NULL) {
5369 goto clean_exit;
5370 }
5371
5372 outfile = php_openssl_bio_new_file(outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(flags));
5373 if (outfile == NULL) {
5374 goto clean_exit;
5375 }
5376
5377 recipcerts = sk_X509_new_null();
5378
5379 /* get certs */
5380 if (Z_TYPE_P(zrecipcerts) == IS_ARRAY) {
5381 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zrecipcerts), zcertval) {
5382 bool free_cert;
5383
5384 cert = php_openssl_x509_from_zval(zcertval, &free_cert, 3, true, NULL);
5385 if (cert == NULL) {
5386 // TODO Add warning?
5387 goto clean_exit;
5388 }
5389
5390 if (!free_cert) {
5391 /* we shouldn't free this particular cert, as it is a resource.
5392 make a copy and push that on the stack instead */
5393 cert = X509_dup(cert);
5394 if (cert == NULL) {
5395 php_openssl_store_errors();
5396 goto clean_exit;
5397 }
5398 }
5399 sk_X509_push(recipcerts, cert);
5400 } ZEND_HASH_FOREACH_END();
5401 } else {
5402 /* a single certificate */
5403 bool free_cert;
5404
5405 cert = php_openssl_x509_from_zval(zrecipcerts, &free_cert, 3, false, NULL);
5406 if (cert == NULL) {
5407 // TODO Add warning?
5408 goto clean_exit;
5409 }
5410
5411 if (!free_cert) {
5412 /* we shouldn't free this particular cert, as it is a resource.
5413 make a copy and push that on the stack instead */
5414 cert = X509_dup(cert);
5415 if (cert == NULL) {
5416 php_openssl_store_errors();
5417 goto clean_exit;
5418 }
5419 }
5420 sk_X509_push(recipcerts, cert);
5421 }
5422
5423 /* sanity check the cipher */
5424 cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
5425 if (cipher == NULL) {
5426 /* shouldn't happen */
5427 php_error_docref(NULL, E_WARNING, "Failed to get cipher");
5428 goto clean_exit;
5429 }
5430
5431 p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, (int)flags);
5432
5433 if (p7 == NULL) {
5434 php_openssl_store_errors();
5435 goto clean_exit;
5436 }
5437
5438 /* tack on extra headers */
5439 if (zheaders) {
5440 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, zcertval) {
5441 zend_string *str = zval_try_get_string(zcertval);
5442 if (UNEXPECTED(!str)) {
5443 goto clean_exit;
5444 }
5445 if (strindex) {
5446 BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
5447 } else {
5448 BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
5449 }
5450 zend_string_release(str);
5451 } ZEND_HASH_FOREACH_END();
5452 }
5453
5454 (void)BIO_reset(infile);
5455
5456 /* write the encrypted data */
5457 if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
5458 php_openssl_store_errors();
5459 goto clean_exit;
5460 }
5461
5462 RETVAL_TRUE;
5463
5464 clean_exit:
5465 PKCS7_free(p7);
5466 BIO_free(infile);
5467 BIO_free(outfile);
5468 if (recipcerts) {
5469 sk_X509_pop_free(recipcerts, X509_free);
5470 }
5471 }
5472 /* }}} */
5473
5474 /* {{{ Exports the PKCS7 file to an array of PEM certificates */
PHP_FUNCTION(openssl_pkcs7_read)5475 PHP_FUNCTION(openssl_pkcs7_read)
5476 {
5477 zval * zout = NULL, zcert;
5478 char *p7b;
5479 size_t p7b_len;
5480 STACK_OF(X509) *certs = NULL;
5481 STACK_OF(X509_CRL) *crls = NULL;
5482 BIO * bio_in = NULL, * bio_out = NULL;
5483 PKCS7 * p7 = NULL;
5484 int i;
5485
5486 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &p7b, &p7b_len,
5487 &zout) == FAILURE) {
5488 RETURN_THROWS();
5489 }
5490
5491 RETVAL_FALSE;
5492
5493 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(p7b_len, p7b, 1);
5494
5495 bio_in = BIO_new(BIO_s_mem());
5496 if (bio_in == NULL) {
5497 goto clean_exit;
5498 }
5499
5500 if (0 >= BIO_write(bio_in, p7b, (int)p7b_len)) {
5501 php_openssl_store_errors();
5502 goto clean_exit;
5503 }
5504
5505 p7 = PEM_read_bio_PKCS7(bio_in, NULL, NULL, NULL);
5506 if (p7 == NULL) {
5507 php_openssl_store_errors();
5508 goto clean_exit;
5509 }
5510
5511 switch (OBJ_obj2nid(p7->type)) {
5512 case NID_pkcs7_signed:
5513 if (p7->d.sign != NULL) {
5514 certs = p7->d.sign->cert;
5515 crls = p7->d.sign->crl;
5516 }
5517 break;
5518 case NID_pkcs7_signedAndEnveloped:
5519 if (p7->d.signed_and_enveloped != NULL) {
5520 certs = p7->d.signed_and_enveloped->cert;
5521 crls = p7->d.signed_and_enveloped->crl;
5522 }
5523 break;
5524 default:
5525 break;
5526 }
5527
5528 zout = zend_try_array_init(zout);
5529 if (!zout) {
5530 goto clean_exit;
5531 }
5532
5533 if (certs != NULL) {
5534 for (i = 0; i < sk_X509_num(certs); i++) {
5535 X509* ca = sk_X509_value(certs, i);
5536
5537 bio_out = BIO_new(BIO_s_mem());
5538 if (bio_out && PEM_write_bio_X509(bio_out, ca)) {
5539 BUF_MEM *bio_buf;
5540 BIO_get_mem_ptr(bio_out, &bio_buf);
5541 ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
5542 add_index_zval(zout, i, &zcert);
5543 BIO_free(bio_out);
5544 }
5545 }
5546 }
5547
5548 if (crls != NULL) {
5549 for (i = 0; i < sk_X509_CRL_num(crls); i++) {
5550 X509_CRL* crl = sk_X509_CRL_value(crls, i);
5551
5552 bio_out = BIO_new(BIO_s_mem());
5553 if (bio_out && PEM_write_bio_X509_CRL(bio_out, crl)) {
5554 BUF_MEM *bio_buf;
5555 BIO_get_mem_ptr(bio_out, &bio_buf);
5556 ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
5557 add_index_zval(zout, i, &zcert);
5558 BIO_free(bio_out);
5559 }
5560 }
5561 }
5562
5563 RETVAL_TRUE;
5564
5565 clean_exit:
5566 BIO_free(bio_in);
5567
5568 if (p7 != NULL) {
5569 PKCS7_free(p7);
5570 }
5571 }
5572 /* }}} */
5573
5574 /* {{{ 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 */
5575
PHP_FUNCTION(openssl_pkcs7_sign)5576 PHP_FUNCTION(openssl_pkcs7_sign)
5577 {
5578 X509 *cert = NULL;
5579 zend_object *cert_obj;
5580 zend_string *cert_str;
5581 zval *zprivkey, * zheaders;
5582 zval * hval;
5583 EVP_PKEY * privkey = NULL;
5584 zend_long flags = PKCS7_DETACHED;
5585 PKCS7 * p7 = NULL;
5586 BIO * infile = NULL, * outfile = NULL;
5587 STACK_OF(X509) *others = NULL;
5588 zend_string * strindex;
5589 char * infilename;
5590 size_t infilename_len;
5591 char * outfilename;
5592 size_t outfilename_len;
5593 char * extracertsfilename = NULL;
5594 size_t extracertsfilename_len;
5595
5596 ZEND_PARSE_PARAMETERS_START(5, 7)
5597 Z_PARAM_PATH(infilename, infilename_len)
5598 Z_PARAM_PATH(outfilename, outfilename_len)
5599 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
5600 Z_PARAM_ZVAL(zprivkey)
5601 Z_PARAM_ARRAY_OR_NULL(zheaders)
5602 Z_PARAM_OPTIONAL
5603 Z_PARAM_LONG(flags)
5604 Z_PARAM_PATH_OR_NULL(extracertsfilename, extracertsfilename_len)
5605 ZEND_PARSE_PARAMETERS_END();
5606
5607 RETVAL_FALSE;
5608
5609 if (extracertsfilename) {
5610 others = php_openssl_load_all_certs_from_file(
5611 extracertsfilename, extracertsfilename_len, 7);
5612 if (others == NULL) {
5613 goto clean_exit;
5614 }
5615 }
5616
5617 privkey = php_openssl_pkey_from_zval(zprivkey, 0, "", 0, 4);
5618 if (privkey == NULL) {
5619 if (!EG(exception)) {
5620 php_error_docref(NULL, E_WARNING, "Error getting private key");
5621 }
5622 goto clean_exit;
5623 }
5624
5625 cert = php_openssl_x509_from_param(cert_obj, cert_str, 3);
5626 if (cert == NULL) {
5627 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
5628 goto clean_exit;
5629 }
5630
5631 infile = php_openssl_bio_new_file(infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5632 if (infile == NULL) {
5633 php_error_docref(NULL, E_WARNING, "Error opening input file %s!", infilename);
5634 goto clean_exit;
5635 }
5636
5637 outfile = php_openssl_bio_new_file(outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5638 if (outfile == NULL) {
5639 php_error_docref(NULL, E_WARNING, "Error opening output file %s!", outfilename);
5640 goto clean_exit;
5641 }
5642
5643 p7 = PKCS7_sign(cert, privkey, others, infile, (int)flags);
5644 if (p7 == NULL) {
5645 php_openssl_store_errors();
5646 php_error_docref(NULL, E_WARNING, "Error creating PKCS7 structure!");
5647 goto clean_exit;
5648 }
5649
5650 (void)BIO_reset(infile);
5651
5652 /* tack on extra headers */
5653 if (zheaders) {
5654 int ret;
5655
5656 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) {
5657 zend_string *str = zval_try_get_string(hval);
5658 if (UNEXPECTED(!str)) {
5659 goto clean_exit;
5660 }
5661 if (strindex) {
5662 ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
5663 } else {
5664 ret = BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
5665 }
5666 zend_string_release(str);
5667 if (ret < 0) {
5668 php_openssl_store_errors();
5669 }
5670 } ZEND_HASH_FOREACH_END();
5671 }
5672 /* write the signed data */
5673 if (!SMIME_write_PKCS7(outfile, p7, infile, (int)flags)) {
5674 php_openssl_store_errors();
5675 goto clean_exit;
5676 }
5677
5678 RETVAL_TRUE;
5679
5680 clean_exit:
5681 PKCS7_free(p7);
5682 BIO_free(infile);
5683 BIO_free(outfile);
5684 if (others) {
5685 sk_X509_pop_free(others, X509_free);
5686 }
5687 EVP_PKEY_free(privkey);
5688 if (cert && cert_str) {
5689 X509_free(cert);
5690 }
5691 }
5692 /* }}} */
5693
5694 /* {{{ 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 */
5695
PHP_FUNCTION(openssl_pkcs7_decrypt)5696 PHP_FUNCTION(openssl_pkcs7_decrypt)
5697 {
5698 X509 *cert;
5699 zval *recipcert, *recipkey = NULL;
5700 bool free_recipcert;
5701 EVP_PKEY * key = NULL;
5702 BIO * in = NULL, *out = NULL, *datain = NULL;
5703 PKCS7 * p7 = NULL;
5704 char * infilename;
5705 size_t infilename_len;
5706 char * outfilename;
5707 size_t outfilename_len;
5708
5709 ZEND_PARSE_PARAMETERS_START(3, 4)
5710 Z_PARAM_PATH(infilename, infilename_len)
5711 Z_PARAM_PATH(outfilename, outfilename_len)
5712 Z_PARAM_ZVAL(recipcert)
5713 Z_PARAM_OPTIONAL
5714 Z_PARAM_ZVAL_OR_NULL(recipkey)
5715 ZEND_PARSE_PARAMETERS_END();
5716
5717 RETVAL_FALSE;
5718
5719 cert = php_openssl_x509_from_zval(recipcert, &free_recipcert, 3, false, NULL);
5720 if (cert == NULL) {
5721 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
5722 goto clean_exit;
5723 }
5724
5725 key = php_openssl_pkey_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, 4);
5726 if (key == NULL) {
5727 if (!EG(exception)) {
5728 php_error_docref(NULL, E_WARNING, "Unable to get private key");
5729 }
5730 goto clean_exit;
5731 }
5732
5733 in = php_openssl_bio_new_file(
5734 infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
5735 if (in == NULL) {
5736 goto clean_exit;
5737 }
5738
5739 out = php_openssl_bio_new_file(
5740 outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY));
5741 if (out == NULL) {
5742 goto clean_exit;
5743 }
5744
5745 p7 = SMIME_read_PKCS7(in, &datain);
5746
5747 if (p7 == NULL) {
5748 php_openssl_store_errors();
5749 goto clean_exit;
5750 }
5751 if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
5752 RETVAL_TRUE;
5753 } else {
5754 php_openssl_store_errors();
5755 }
5756 clean_exit:
5757 PKCS7_free(p7);
5758 BIO_free(datain);
5759 BIO_free(in);
5760 BIO_free(out);
5761 if (cert && free_recipcert) {
5762 X509_free(cert);
5763 }
5764 EVP_PKEY_free(key);
5765 }
5766 /* }}} */
5767
5768 /* }}} */
5769
5770 /* {{{ CMS S/MIME functions taken from PKCS#7 functions */
5771
5772 /* {{{ 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)5773 PHP_FUNCTION(openssl_cms_verify)
5774 {
5775 X509_STORE * store = NULL;
5776 zval * cainfo = NULL;
5777 STACK_OF(X509) *signers= NULL;
5778 STACK_OF(X509) *others = NULL;
5779 CMS_ContentInfo * cms = NULL;
5780 BIO * in = NULL, * datain = NULL, * dataout = NULL, * p7bout = NULL;
5781 BIO *certout = NULL, *sigbio = NULL;
5782 zend_long flags = 0;
5783 char * filename;
5784 size_t filename_len;
5785 char * extracerts = NULL;
5786 size_t extracerts_len = 0;
5787 char * signersfilename = NULL;
5788 size_t signersfilename_len = 0;
5789 char * datafilename = NULL;
5790 size_t datafilename_len = 0;
5791 char * p7bfilename = NULL;
5792 size_t p7bfilename_len = 0;
5793 char * sigfile = NULL;
5794 size_t sigfile_len = 0;
5795 zend_long encoding = ENCODING_SMIME;
5796
5797 RETVAL_FALSE;
5798
5799 if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|p!ap!p!p!p!l", &filename, &filename_len,
5800 &flags, &signersfilename, &signersfilename_len, &cainfo,
5801 &extracerts, &extracerts_len, &datafilename, &datafilename_len,
5802 &p7bfilename, &p7bfilename_len,
5803 &sigfile, &sigfile_len, &encoding) == FAILURE) {
5804 RETURN_THROWS();
5805 }
5806
5807 in = php_openssl_bio_new_file(filename, filename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5808 if (in == NULL) {
5809 goto clean_exit;
5810 }
5811 if (sigfile && (flags & CMS_DETACHED)) {
5812 if (encoding == ENCODING_SMIME) {
5813 php_error_docref(NULL, E_WARNING,
5814 "Detached signatures not possible with S/MIME encoding");
5815 goto clean_exit;
5816 }
5817 sigbio = php_openssl_bio_new_file(sigfile, sigfile_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5818 if (sigbio == NULL) {
5819 goto clean_exit;
5820 }
5821 } else {
5822 sigbio = in; /* non-detached signature */
5823 }
5824
5825 switch (encoding) {
5826 case ENCODING_PEM:
5827 cms = PEM_read_bio_CMS(sigbio, NULL, 0, NULL);
5828 datain = in;
5829 break;
5830 case ENCODING_DER:
5831 cms = d2i_CMS_bio(sigbio, NULL);
5832 datain = in;
5833 break;
5834 case ENCODING_SMIME:
5835 cms = SMIME_read_CMS(sigbio, &datain);
5836 break;
5837 default:
5838 php_error_docref(NULL, E_WARNING, "Unknown encoding");
5839 goto clean_exit;
5840 }
5841 if (cms == NULL) {
5842 php_openssl_store_errors();
5843 goto clean_exit;
5844 }
5845 if (encoding != ENCODING_SMIME && !(flags & CMS_DETACHED)) {
5846 datain = NULL; /* when not detached, don't pass a real BIO */
5847 }
5848
5849 if (extracerts) {
5850 others = php_openssl_load_all_certs_from_file(extracerts, extracerts_len, 5);
5851 if (others == NULL) {
5852 goto clean_exit;
5853 }
5854 }
5855
5856 store = php_openssl_setup_verify(cainfo, 4);
5857
5858 if (!store) {
5859 goto clean_exit;
5860 }
5861
5862 if (datafilename) {
5863 dataout = php_openssl_bio_new_file(
5864 datafilename, datafilename_len, 6, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
5865 if (dataout == NULL) {
5866 goto clean_exit;
5867 }
5868 }
5869
5870 if (p7bfilename) {
5871 p7bout = php_openssl_bio_new_file(
5872 p7bfilename, p7bfilename_len, 7, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
5873 if (p7bout == NULL) {
5874 goto clean_exit;
5875 }
5876 }
5877 #if DEBUG_SMIME
5878 zend_printf("Calling CMS verify\n");
5879 #endif
5880 if (CMS_verify(cms, others, store, datain, dataout, (unsigned int)flags)) {
5881 RETVAL_TRUE;
5882
5883 if (signersfilename) {
5884 certout = php_openssl_bio_new_file(
5885 signersfilename, signersfilename_len, 3, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
5886 if (certout) {
5887 int i;
5888 signers = CMS_get0_signers(cms);
5889 if (signers != NULL) {
5890
5891 for (i = 0; i < sk_X509_num(signers); i++) {
5892 if (!PEM_write_bio_X509(certout, sk_X509_value(signers, i))) {
5893 php_openssl_store_errors();
5894 RETVAL_FALSE;
5895 php_error_docref(NULL, E_WARNING, "Failed to write signer %d", i);
5896 }
5897 }
5898
5899 sk_X509_free(signers);
5900 } else {
5901 RETVAL_FALSE;
5902 php_openssl_store_errors();
5903 }
5904 } else {
5905 php_error_docref(NULL, E_WARNING, "Signature OK, but cannot open %s for writing", signersfilename);
5906 RETVAL_FALSE;
5907 }
5908
5909 if (p7bout) {
5910 if (PEM_write_bio_CMS(p7bout, cms) == 0) {
5911 php_error_docref(NULL, E_WARNING, "Failed to write CMS to file");
5912 php_openssl_store_errors();
5913 RETVAL_FALSE;
5914 }
5915 }
5916 }
5917 } else {
5918 php_openssl_store_errors();
5919 RETVAL_FALSE;
5920 }
5921 clean_exit:
5922 BIO_free(p7bout);
5923 if (store) {
5924 X509_STORE_free(store);
5925 }
5926 if (datain != in) {
5927 BIO_free(datain);
5928 }
5929 if (sigbio != in) {
5930 BIO_free(sigbio);
5931 }
5932 BIO_free(in);
5933 BIO_free(dataout);
5934 BIO_free(certout);
5935 if (cms) {
5936 CMS_ContentInfo_free(cms);
5937 }
5938 if (others) {
5939 sk_X509_pop_free(others, X509_free);
5940 }
5941 }
5942 /* }}} */
5943
5944 /* {{{ 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)5945 PHP_FUNCTION(openssl_cms_encrypt)
5946 {
5947 zval * zrecipcerts, * zheaders = NULL;
5948 STACK_OF(X509) * recipcerts = NULL;
5949 BIO * infile = NULL, * outfile = NULL;
5950 zend_long flags = 0;
5951 zend_long encoding = ENCODING_SMIME;
5952 CMS_ContentInfo * cms = NULL;
5953 zval * zcertval;
5954 X509 * cert;
5955 const EVP_CIPHER *cipher = NULL;
5956 zend_long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
5957 zend_string * strindex;
5958 char * infilename = NULL;
5959 size_t infilename_len;
5960 char * outfilename = NULL;
5961 size_t outfilename_len;
5962 int need_final = 0;
5963
5964 RETVAL_FALSE;
5965
5966 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppza!|lll", &infilename, &infilename_len,
5967 &outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &encoding, &cipherid) == FAILURE) {
5968 RETURN_THROWS();
5969 }
5970
5971
5972 infile = php_openssl_bio_new_file(
5973 infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
5974 if (infile == NULL) {
5975 goto clean_exit;
5976 }
5977
5978 outfile = php_openssl_bio_new_file(
5979 outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(flags));
5980 if (outfile == NULL) {
5981 goto clean_exit;
5982 }
5983
5984 recipcerts = sk_X509_new_null();
5985
5986 /* get certs */
5987 if (Z_TYPE_P(zrecipcerts) == IS_ARRAY) {
5988 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zrecipcerts), zcertval) {
5989 bool free_cert;
5990
5991 cert = php_openssl_x509_from_zval(zcertval, &free_cert, 3, true, NULL);
5992 if (cert == NULL) {
5993 goto clean_exit;
5994 }
5995
5996 if (!free_cert) {
5997 /* we shouldn't free this particular cert, as it is a resource.
5998 make a copy and push that on the stack instead */
5999 cert = X509_dup(cert);
6000 if (cert == NULL) {
6001 php_openssl_store_errors();
6002 goto clean_exit;
6003 }
6004 }
6005 sk_X509_push(recipcerts, cert);
6006 } ZEND_HASH_FOREACH_END();
6007 } else {
6008 /* a single certificate */
6009 bool free_cert;
6010
6011 cert = php_openssl_x509_from_zval(zrecipcerts, &free_cert, 3, false, NULL);
6012 if (cert == NULL) {
6013 goto clean_exit;
6014 }
6015
6016 if (!free_cert) {
6017 /* we shouldn't free this particular cert, as it is a resource.
6018 make a copy and push that on the stack instead */
6019 cert = X509_dup(cert);
6020 if (cert == NULL) {
6021 php_openssl_store_errors();
6022 goto clean_exit;
6023 }
6024 }
6025 sk_X509_push(recipcerts, cert);
6026 }
6027
6028 /* sanity check the cipher */
6029 cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
6030 if (cipher == NULL) {
6031 /* shouldn't happen */
6032 php_error_docref(NULL, E_WARNING, "Failed to get cipher");
6033 goto clean_exit;
6034 }
6035
6036 cms = CMS_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, (unsigned int)flags);
6037
6038 if (cms == NULL) {
6039 php_openssl_store_errors();
6040 goto clean_exit;
6041 }
6042
6043 if (flags & CMS_PARTIAL && !(flags & CMS_STREAM)) {
6044 need_final=1;
6045 }
6046
6047 /* tack on extra headers */
6048 if (zheaders && encoding == ENCODING_SMIME) {
6049 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, zcertval) {
6050 zend_string *str = zval_try_get_string(zcertval);
6051 if (UNEXPECTED(!str)) {
6052 goto clean_exit;
6053 }
6054 if (strindex) {
6055 BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
6056 } else {
6057 BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
6058 }
6059 zend_string_release(str);
6060 } ZEND_HASH_FOREACH_END();
6061 }
6062
6063 (void)BIO_reset(infile);
6064
6065 switch (encoding) {
6066 case ENCODING_SMIME:
6067 if (!SMIME_write_CMS(outfile, cms, infile, (int)flags)) {
6068 php_openssl_store_errors();
6069 goto clean_exit;
6070 }
6071 break;
6072 case ENCODING_DER:
6073 if (need_final) {
6074 if (CMS_final(cms, infile, NULL, (unsigned int) flags) != 1) {
6075 php_openssl_store_errors();
6076 goto clean_exit;
6077 }
6078 }
6079 if (i2d_CMS_bio(outfile, cms) != 1) {
6080 php_openssl_store_errors();
6081 goto clean_exit;
6082 }
6083 break;
6084 case ENCODING_PEM:
6085 if (need_final) {
6086 if (CMS_final(cms, infile, NULL, (unsigned int) flags) != 1) {
6087 php_openssl_store_errors();
6088 goto clean_exit;
6089 }
6090 }
6091 if (flags & CMS_STREAM) {
6092 if (PEM_write_bio_CMS_stream(outfile, cms, infile, flags) == 0) {
6093 php_openssl_store_errors();
6094 goto clean_exit;
6095 }
6096 } else {
6097 if (PEM_write_bio_CMS(outfile, cms) == 0) {
6098 php_openssl_store_errors();
6099 goto clean_exit;
6100 }
6101 }
6102 break;
6103 default:
6104 php_error_docref(NULL, E_WARNING, "Unknown OPENSSL encoding");
6105 goto clean_exit;
6106 }
6107
6108 RETVAL_TRUE;
6109
6110 clean_exit:
6111 if (cms) {
6112 CMS_ContentInfo_free(cms);
6113 }
6114 BIO_free(infile);
6115 BIO_free(outfile);
6116 if (recipcerts) {
6117 sk_X509_pop_free(recipcerts, X509_free);
6118 }
6119 }
6120 /* }}} */
6121
6122 /* {{{ Exports the CMS file to an array of PEM certificates */
PHP_FUNCTION(openssl_cms_read)6123 PHP_FUNCTION(openssl_cms_read)
6124 {
6125 zval * zout = NULL, zcert;
6126 char *p7b;
6127 size_t p7b_len;
6128 STACK_OF(X509) *certs = NULL;
6129 STACK_OF(X509_CRL) *crls = NULL;
6130 BIO * bio_in = NULL, * bio_out = NULL;
6131 CMS_ContentInfo * cms = NULL;
6132 int i;
6133
6134 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &p7b, &p7b_len,
6135 &zout) == FAILURE) {
6136 RETURN_THROWS();
6137 }
6138
6139 RETVAL_FALSE;
6140
6141 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(p7b_len, p7b, 1);
6142
6143 bio_in = BIO_new(BIO_s_mem());
6144 if (bio_in == NULL) {
6145 goto clean_exit;
6146 }
6147
6148 if (0 >= BIO_write(bio_in, p7b, (int)p7b_len)) {
6149 php_openssl_store_errors();
6150 goto clean_exit;
6151 }
6152
6153 cms = PEM_read_bio_CMS(bio_in, NULL, NULL, NULL);
6154 if (cms == NULL) {
6155 php_openssl_store_errors();
6156 goto clean_exit;
6157 }
6158
6159 switch (OBJ_obj2nid(CMS_get0_type(cms))) {
6160 case NID_pkcs7_signed:
6161 case NID_pkcs7_signedAndEnveloped:
6162 certs = CMS_get1_certs(cms);
6163 crls = CMS_get1_crls(cms);
6164 break;
6165 default:
6166 break;
6167 }
6168
6169 zout = zend_try_array_init(zout);
6170 if (!zout) {
6171 goto clean_exit;
6172 }
6173
6174 if (certs != NULL) {
6175 for (i = 0; i < sk_X509_num(certs); i++) {
6176 X509* ca = sk_X509_value(certs, i);
6177
6178 bio_out = BIO_new(BIO_s_mem());
6179 if (bio_out && PEM_write_bio_X509(bio_out, ca)) {
6180 BUF_MEM *bio_buf;
6181 BIO_get_mem_ptr(bio_out, &bio_buf);
6182 ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
6183 add_index_zval(zout, i, &zcert);
6184 BIO_free(bio_out);
6185 }
6186 }
6187 }
6188
6189 if (crls != NULL) {
6190 for (i = 0; i < sk_X509_CRL_num(crls); i++) {
6191 X509_CRL* crl = sk_X509_CRL_value(crls, i);
6192
6193 bio_out = BIO_new(BIO_s_mem());
6194 if (bio_out && PEM_write_bio_X509_CRL(bio_out, crl)) {
6195 BUF_MEM *bio_buf;
6196 BIO_get_mem_ptr(bio_out, &bio_buf);
6197 ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
6198 add_index_zval(zout, i, &zcert);
6199 BIO_free(bio_out);
6200 }
6201 }
6202 }
6203
6204 RETVAL_TRUE;
6205
6206 clean_exit:
6207 BIO_free(bio_in);
6208 if (cms != NULL) {
6209 CMS_ContentInfo_free(cms);
6210 }
6211 if (certs != NULL) {
6212 sk_X509_pop_free(certs, X509_free);
6213 }
6214 if (crls != NULL) {
6215 sk_X509_CRL_pop_free(crls, X509_CRL_free);
6216 }
6217 }
6218 /* }}} */
6219
6220 /* {{{ 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 */
6221
PHP_FUNCTION(openssl_cms_sign)6222 PHP_FUNCTION(openssl_cms_sign)
6223 {
6224 X509 *cert = NULL;
6225 zend_object *cert_obj;
6226 zend_string *cert_str;
6227 zval *zprivkey, *zheaders;
6228 zval * hval;
6229 EVP_PKEY * privkey = NULL;
6230 zend_long flags = 0;
6231 zend_long encoding = ENCODING_SMIME;
6232 CMS_ContentInfo * cms = NULL;
6233 BIO * infile = NULL, * outfile = NULL;
6234 STACK_OF(X509) *others = NULL;
6235 zend_string * strindex;
6236 char * infilename;
6237 size_t infilename_len;
6238 char * outfilename;
6239 size_t outfilename_len;
6240 char * extracertsfilename = NULL;
6241 size_t extracertsfilename_len;
6242 int need_final = 0;
6243
6244 ZEND_PARSE_PARAMETERS_START(5, 8)
6245 Z_PARAM_PATH(infilename, infilename_len)
6246 Z_PARAM_PATH(outfilename, outfilename_len)
6247 Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
6248 Z_PARAM_ZVAL(zprivkey)
6249 Z_PARAM_ARRAY_OR_NULL(zheaders)
6250 Z_PARAM_OPTIONAL
6251 Z_PARAM_LONG(flags)
6252 Z_PARAM_LONG(encoding)
6253 Z_PARAM_PATH_OR_NULL(extracertsfilename, extracertsfilename_len)
6254 ZEND_PARSE_PARAMETERS_END();
6255
6256 RETVAL_FALSE;
6257
6258 if (extracertsfilename) {
6259 others = php_openssl_load_all_certs_from_file(
6260 extracertsfilename, extracertsfilename_len, 8);
6261 if (others == NULL) {
6262 goto clean_exit;
6263 }
6264 }
6265
6266 privkey = php_openssl_pkey_from_zval(zprivkey, 0, "", 0, 4);
6267 if (privkey == NULL) {
6268 if (!EG(exception)) {
6269 php_error_docref(NULL, E_WARNING, "Error getting private key");
6270 }
6271 goto clean_exit;
6272 }
6273
6274 cert = php_openssl_x509_from_param(cert_obj, cert_str, 3);
6275 if (cert == NULL) {
6276 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
6277 goto clean_exit;
6278 }
6279
6280 if ((encoding & ENCODING_SMIME) && (flags & CMS_DETACHED)) {
6281 php_error_docref(NULL,
6282 E_WARNING, "Detached signatures not possible with S/MIME encoding");
6283 goto clean_exit;
6284 }
6285
6286 /* a CMS struct will not be complete if either CMS_PARTIAL or CMS_STREAM is set.
6287 * However, CMS_PARTIAL requires a CMS_final call whereas CMS_STREAM requires
6288 * a different write routine below. There may be a more efficient way to do this
6289 * with function pointers, but the readability goes down.
6290 * References: CMS_sign(3SSL), CMS_final(3SSL)
6291 */
6292
6293 if (flags & CMS_PARTIAL && !(flags & CMS_STREAM)) {
6294 need_final=1;
6295 }
6296
6297 infile = php_openssl_bio_new_file(
6298 infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(flags));
6299 if (infile == NULL) {
6300 php_error_docref(NULL, E_WARNING, "Error opening input file %s!", infilename);
6301 goto clean_exit;
6302 }
6303
6304 outfile = php_openssl_bio_new_file(
6305 outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
6306 if (outfile == NULL) {
6307 php_error_docref(NULL, E_WARNING, "Error opening output file %s!", outfilename);
6308 goto clean_exit;
6309 }
6310
6311 cms = CMS_sign(cert, privkey, others, infile, (unsigned int)flags);
6312 if (cms == NULL) {
6313 php_openssl_store_errors();
6314 php_error_docref(NULL, E_WARNING, "Error creating CMS structure!");
6315 goto clean_exit;
6316 }
6317 if (BIO_reset(infile) != 0) {
6318 php_openssl_store_errors();
6319 goto clean_exit;
6320 }
6321
6322 /* tack on extra headers */
6323 if (zheaders && encoding == ENCODING_SMIME) {
6324 int ret;
6325
6326 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) {
6327 zend_string *str = zval_try_get_string(hval);
6328 if (UNEXPECTED(!str)) {
6329 goto clean_exit;
6330 }
6331 if (strindex) {
6332 ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str));
6333 } else {
6334 ret = BIO_printf(outfile, "%s\n", ZSTR_VAL(str));
6335 }
6336 zend_string_release(str);
6337 if (ret < 0) {
6338 php_openssl_store_errors();
6339 }
6340 } ZEND_HASH_FOREACH_END();
6341 }
6342 /* writing the signed data depends on the encoding */
6343 switch (encoding) {
6344 case ENCODING_SMIME:
6345 if (!SMIME_write_CMS(outfile, cms, infile, (int)flags)) {
6346 php_openssl_store_errors();
6347 goto clean_exit;
6348 }
6349 break;
6350 case ENCODING_DER:
6351 if (need_final) {
6352 if (CMS_final(cms, infile, NULL, (unsigned int) flags) != 1) {
6353 php_openssl_store_errors();
6354 goto clean_exit;
6355 }
6356 }
6357 if (i2d_CMS_bio(outfile, cms) != 1) {
6358 php_openssl_store_errors();
6359 goto clean_exit;
6360 }
6361 break;
6362 case ENCODING_PEM:
6363 if (need_final) {
6364 if (CMS_final(cms, infile, NULL, (unsigned int) flags) != 1) {
6365 php_openssl_store_errors();
6366 goto clean_exit;
6367 }
6368 }
6369 if (flags & CMS_STREAM) {
6370 if (PEM_write_bio_CMS_stream(outfile, cms, infile, flags) == 0) {
6371 php_openssl_store_errors();
6372 goto clean_exit;
6373 }
6374 } else {
6375 if (PEM_write_bio_CMS(outfile, cms) == 0) {
6376 php_openssl_store_errors();
6377 goto clean_exit;
6378 }
6379 }
6380 break;
6381 default:
6382 php_error_docref(NULL, E_WARNING, "Unknown OPENSSL encoding");
6383 goto clean_exit;
6384 }
6385 RETVAL_TRUE;
6386
6387 clean_exit:
6388 if (cms) {
6389 CMS_ContentInfo_free(cms);
6390 }
6391 BIO_free(infile);
6392 BIO_free(outfile);
6393 if (others) {
6394 sk_X509_pop_free(others, X509_free);
6395 }
6396 EVP_PKEY_free(privkey);
6397 if (cert && cert_str) {
6398 X509_free(cert);
6399 }
6400 }
6401 /* }}} */
6402
6403 /* {{{ 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 */
6404
PHP_FUNCTION(openssl_cms_decrypt)6405 PHP_FUNCTION(openssl_cms_decrypt)
6406 {
6407 X509 *cert;
6408 zval *recipcert, *recipkey = NULL;
6409 bool free_recipcert;
6410 EVP_PKEY * key = NULL;
6411 zend_long encoding = ENCODING_SMIME;
6412 BIO * in = NULL, * out = NULL, * datain = NULL;
6413 CMS_ContentInfo * cms = NULL;
6414 char * infilename;
6415 size_t infilename_len;
6416 char * outfilename;
6417 size_t outfilename_len;
6418
6419 ZEND_PARSE_PARAMETERS_START(3, 5)
6420 Z_PARAM_PATH(infilename, infilename_len)
6421 Z_PARAM_PATH(outfilename, outfilename_len)
6422 Z_PARAM_ZVAL(recipcert)
6423 Z_PARAM_OPTIONAL
6424 Z_PARAM_ZVAL_OR_NULL(recipkey)
6425 Z_PARAM_LONG(encoding)
6426 ZEND_PARSE_PARAMETERS_END();
6427
6428 RETVAL_FALSE;
6429
6430 cert = php_openssl_x509_from_zval(recipcert, &free_recipcert, 3, false, NULL);
6431 if (cert == NULL) {
6432 php_error_docref(NULL, E_WARNING, "X.509 Certificate cannot be retrieved");
6433 goto clean_exit;
6434 }
6435
6436 key = php_openssl_pkey_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, recipkey ? 4 : 3);
6437 if (key == NULL) {
6438 if (!EG(exception)) {
6439 php_error_docref(NULL, E_WARNING, "Unable to get private key");
6440 }
6441 goto clean_exit;
6442 }
6443
6444 in = php_openssl_bio_new_file(
6445 infilename, infilename_len, 1, PHP_OPENSSL_BIO_MODE_R(CMS_BINARY));
6446 if (in == NULL) {
6447 goto clean_exit;
6448 }
6449
6450 out = php_openssl_bio_new_file(
6451 outfilename, outfilename_len, 2, PHP_OPENSSL_BIO_MODE_W(CMS_BINARY));
6452 if (out == NULL) {
6453 goto clean_exit;
6454 }
6455
6456 switch (encoding) {
6457 case ENCODING_DER:
6458 cms = d2i_CMS_bio(in, NULL);
6459 break;
6460 case ENCODING_PEM:
6461 cms = PEM_read_bio_CMS(in, NULL, 0, NULL);
6462 break;
6463 case ENCODING_SMIME:
6464 cms = SMIME_read_CMS(in, &datain);
6465 break;
6466 default:
6467 zend_argument_value_error(5, "must be an OPENSSL_ENCODING_* constant");
6468 goto clean_exit;
6469 }
6470
6471 if (cms == NULL) {
6472 php_openssl_store_errors();
6473 goto clean_exit;
6474 }
6475 if (CMS_decrypt(cms, key, cert, NULL, out, 0)) {
6476 RETVAL_TRUE;
6477 } else {
6478 php_openssl_store_errors();
6479 }
6480 clean_exit:
6481 if (cms) {
6482 CMS_ContentInfo_free(cms);
6483 }
6484 BIO_free(datain);
6485 BIO_free(in);
6486 BIO_free(out);
6487 if (cert && free_recipcert) {
6488 X509_free(cert);
6489 }
6490 EVP_PKEY_free(key);
6491 }
6492 /* }}} */
6493
6494 /* }}} */
6495
6496
6497
6498 /* {{{ Encrypts data with private key */
PHP_FUNCTION(openssl_private_encrypt)6499 PHP_FUNCTION(openssl_private_encrypt)
6500 {
6501 zval *key, *crypted;
6502 char * data;
6503 size_t data_len;
6504 zend_long padding = RSA_PKCS1_PADDING;
6505
6506 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
6507 RETURN_THROWS();
6508 }
6509
6510 EVP_PKEY *pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3);
6511 if (pkey == NULL) {
6512 if (!EG(exception)) {
6513 php_error_docref(NULL, E_WARNING, "key param is not a valid private key");
6514 }
6515 RETURN_FALSE;
6516 }
6517
6518 size_t out_len = 0;
6519 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
6520 if (!ctx || EVP_PKEY_sign_init(ctx) <= 0 ||
6521 EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
6522 EVP_PKEY_sign(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
6523 php_openssl_store_errors();
6524 RETVAL_FALSE;
6525 goto cleanup;
6526 }
6527
6528 zend_string *out = zend_string_alloc(out_len, 0);
6529 if (EVP_PKEY_sign(ctx, (unsigned char *) ZSTR_VAL(out), &out_len,
6530 (unsigned char *) data, data_len) <= 0) {
6531 zend_string_release(out);
6532 php_openssl_store_errors();
6533 RETVAL_FALSE;
6534 goto cleanup;
6535 }
6536
6537 ZSTR_VAL(out)[out_len] = '\0';
6538 ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, out);
6539 RETVAL_TRUE;
6540
6541 cleanup:
6542 EVP_PKEY_CTX_free(ctx);
6543 EVP_PKEY_free(pkey);
6544 }
6545 /* }}} */
6546
6547 /* {{{ Decrypts data with private key */
PHP_FUNCTION(openssl_private_decrypt)6548 PHP_FUNCTION(openssl_private_decrypt)
6549 {
6550 zval *key, *crypted;
6551 zend_long padding = RSA_PKCS1_PADDING;
6552 char * data;
6553 size_t data_len;
6554
6555 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
6556 RETURN_THROWS();
6557 }
6558
6559 EVP_PKEY *pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3);
6560 if (pkey == NULL) {
6561 if (!EG(exception)) {
6562 php_error_docref(NULL, E_WARNING, "key parameter is not a valid private key");
6563 }
6564 RETURN_FALSE;
6565 }
6566
6567 size_t out_len = 0;
6568 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
6569 if (!ctx || EVP_PKEY_decrypt_init(ctx) <= 0 ||
6570 EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
6571 EVP_PKEY_decrypt(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
6572 php_openssl_store_errors();
6573 RETVAL_FALSE;
6574 goto cleanup;
6575 }
6576
6577 zend_string *out = zend_string_alloc(out_len, 0);
6578 if (EVP_PKEY_decrypt(ctx, (unsigned char *) ZSTR_VAL(out), &out_len,
6579 (unsigned char *) data, data_len) <= 0) {
6580 zend_string_release(out);
6581 php_openssl_store_errors();
6582 RETVAL_FALSE;
6583 goto cleanup;
6584 }
6585
6586 out = zend_string_truncate(out, out_len, 0);
6587 ZSTR_VAL(out)[out_len] = '\0';
6588 ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, out);
6589 RETVAL_TRUE;
6590
6591 cleanup:
6592 EVP_PKEY_CTX_free(ctx);
6593 EVP_PKEY_free(pkey);
6594 }
6595 /* }}} */
6596
6597 /* {{{ Encrypts data with public key */
PHP_FUNCTION(openssl_public_encrypt)6598 PHP_FUNCTION(openssl_public_encrypt)
6599 {
6600 zval *key, *crypted;
6601 zend_long padding = RSA_PKCS1_PADDING;
6602 char * data;
6603 size_t data_len;
6604
6605 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
6606 RETURN_THROWS();
6607 }
6608
6609 EVP_PKEY *pkey = php_openssl_pkey_from_zval(key, 1, NULL, 0, 3);
6610 if (pkey == NULL) {
6611 if (!EG(exception)) {
6612 php_error_docref(NULL, E_WARNING, "key parameter is not a valid public key");
6613 }
6614 RETURN_FALSE;
6615 }
6616
6617 size_t out_len = 0;
6618 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
6619 if (!ctx || EVP_PKEY_encrypt_init(ctx) <= 0 ||
6620 EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
6621 EVP_PKEY_encrypt(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
6622 php_openssl_store_errors();
6623 RETVAL_FALSE;
6624 goto cleanup;
6625 }
6626
6627 zend_string *out = zend_string_alloc(out_len, 0);
6628 if (EVP_PKEY_encrypt(ctx, (unsigned char *) ZSTR_VAL(out), &out_len,
6629 (unsigned char *) data, data_len) <= 0) {
6630 zend_string_release(out);
6631 php_openssl_store_errors();
6632 RETVAL_FALSE;
6633 goto cleanup;
6634 }
6635
6636 ZSTR_VAL(out)[out_len] = '\0';
6637 ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, out);
6638 RETVAL_TRUE;
6639
6640 cleanup:
6641 EVP_PKEY_CTX_free(ctx);
6642 EVP_PKEY_free(pkey);
6643 }
6644 /* }}} */
6645
6646 /* {{{ Decrypts data with public key */
PHP_FUNCTION(openssl_public_decrypt)6647 PHP_FUNCTION(openssl_public_decrypt)
6648 {
6649 zval *key, *crypted;
6650 zend_long padding = RSA_PKCS1_PADDING;
6651 char * data;
6652 size_t data_len;
6653
6654 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
6655 RETURN_THROWS();
6656 }
6657
6658 EVP_PKEY *pkey = php_openssl_pkey_from_zval(key, 1, NULL, 0, 3);
6659 if (pkey == NULL) {
6660 if (!EG(exception)) {
6661 php_error_docref(NULL, E_WARNING, "key parameter is not a valid public key");
6662 }
6663 RETURN_FALSE;
6664 }
6665
6666 size_t out_len = 0;
6667 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
6668 if (!ctx || EVP_PKEY_verify_recover_init(ctx) <= 0 ||
6669 EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
6670 EVP_PKEY_verify_recover(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
6671 php_openssl_store_errors();
6672 RETVAL_FALSE;
6673 goto cleanup;
6674 }
6675
6676 zend_string *out = zend_string_alloc(out_len, 0);
6677 if (EVP_PKEY_verify_recover(ctx, (unsigned char *) ZSTR_VAL(out), &out_len,
6678 (unsigned char *) data, data_len) <= 0) {
6679 zend_string_release(out);
6680 php_openssl_store_errors();
6681 RETVAL_FALSE;
6682 goto cleanup;
6683 }
6684
6685 out = zend_string_truncate(out, out_len, 0);
6686 ZSTR_VAL(out)[out_len] = '\0';
6687 ZEND_TRY_ASSIGN_REF_NEW_STR(crypted, out);
6688 RETVAL_TRUE;
6689
6690 cleanup:
6691 EVP_PKEY_CTX_free(ctx);
6692 EVP_PKEY_free(pkey);
6693 }
6694 /* }}} */
6695
6696 /* {{{ 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)6697 PHP_FUNCTION(openssl_error_string)
6698 {
6699 char buf[256];
6700 unsigned long val;
6701
6702 if (zend_parse_parameters_none() == FAILURE) {
6703 RETURN_THROWS();
6704 }
6705
6706 php_openssl_store_errors();
6707
6708 if (OPENSSL_G(errors) == NULL || OPENSSL_G(errors)->top == OPENSSL_G(errors)->bottom) {
6709 RETURN_FALSE;
6710 }
6711
6712 OPENSSL_G(errors)->bottom = (OPENSSL_G(errors)->bottom + 1) % ERR_NUM_ERRORS;
6713 val = OPENSSL_G(errors)->buffer[OPENSSL_G(errors)->bottom];
6714
6715 if (val) {
6716 ERR_error_string_n(val, buf, 256);
6717 RETURN_STRING(buf);
6718 } else {
6719 RETURN_FALSE;
6720 }
6721 }
6722 /* }}} */
6723
6724 /* {{{ Signs data */
PHP_FUNCTION(openssl_sign)6725 PHP_FUNCTION(openssl_sign)
6726 {
6727 zval *key, *signature;
6728 EVP_PKEY *pkey;
6729 unsigned int siglen;
6730 zend_string *sigbuf;
6731 char * data;
6732 size_t data_len;
6733 EVP_MD_CTX *md_ctx;
6734 zend_string *method_str = NULL;
6735 zend_long method_long = OPENSSL_ALGO_SHA1;
6736 const EVP_MD *mdtype;
6737
6738 ZEND_PARSE_PARAMETERS_START(3, 4)
6739 Z_PARAM_STRING(data, data_len)
6740 Z_PARAM_ZVAL(signature)
6741 Z_PARAM_ZVAL(key)
6742 Z_PARAM_OPTIONAL
6743 Z_PARAM_STR_OR_LONG(method_str, method_long)
6744 ZEND_PARSE_PARAMETERS_END();
6745
6746 pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3);
6747 if (pkey == NULL) {
6748 if (!EG(exception)) {
6749 php_error_docref(NULL, E_WARNING, "Supplied key param cannot be coerced into a private key");
6750 }
6751 RETURN_FALSE;
6752 }
6753
6754 if (method_str) {
6755 mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str));
6756 } else {
6757 mdtype = php_openssl_get_evp_md_from_algo(method_long);
6758 }
6759 if (!mdtype) {
6760 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
6761 RETURN_FALSE;
6762 }
6763
6764 siglen = EVP_PKEY_size(pkey);
6765 sigbuf = zend_string_alloc(siglen, 0);
6766
6767 md_ctx = EVP_MD_CTX_create();
6768 if (md_ctx != NULL &&
6769 EVP_SignInit(md_ctx, mdtype) &&
6770 EVP_SignUpdate(md_ctx, data, data_len) &&
6771 EVP_SignFinal(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, pkey)) {
6772 ZSTR_VAL(sigbuf)[siglen] = '\0';
6773 ZSTR_LEN(sigbuf) = siglen;
6774 ZEND_TRY_ASSIGN_REF_NEW_STR(signature, sigbuf);
6775 RETVAL_TRUE;
6776 } else {
6777 php_openssl_store_errors();
6778 efree(sigbuf);
6779 RETVAL_FALSE;
6780 }
6781 EVP_MD_CTX_destroy(md_ctx);
6782 EVP_PKEY_free(pkey);
6783 }
6784 /* }}} */
6785
6786 /* {{{ Verifys data */
PHP_FUNCTION(openssl_verify)6787 PHP_FUNCTION(openssl_verify)
6788 {
6789 zval *key;
6790 EVP_PKEY *pkey;
6791 int err = 0;
6792 EVP_MD_CTX *md_ctx;
6793 const EVP_MD *mdtype;
6794 char * data;
6795 size_t data_len;
6796 char * signature;
6797 size_t signature_len;
6798 zend_string *method_str = NULL;
6799 zend_long method_long = OPENSSL_ALGO_SHA1;
6800
6801 ZEND_PARSE_PARAMETERS_START(3, 4)
6802 Z_PARAM_STRING(data, data_len)
6803 Z_PARAM_STRING(signature, signature_len)
6804 Z_PARAM_ZVAL(key)
6805 Z_PARAM_OPTIONAL
6806 Z_PARAM_STR_OR_LONG(method_str, method_long)
6807 ZEND_PARSE_PARAMETERS_END();
6808
6809 PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature, 2);
6810
6811 if (method_str) {
6812 mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str));
6813 } else {
6814 mdtype = php_openssl_get_evp_md_from_algo(method_long);
6815 }
6816 if (!mdtype) {
6817 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
6818 RETURN_FALSE;
6819 }
6820
6821 pkey = php_openssl_pkey_from_zval(key, 1, NULL, 0, 3);
6822 if (pkey == NULL) {
6823 if (!EG(exception)) {
6824 php_error_docref(NULL, E_WARNING, "Supplied key param cannot be coerced into a public key");
6825 }
6826 RETURN_FALSE;
6827 }
6828
6829 md_ctx = EVP_MD_CTX_create();
6830 if (md_ctx == NULL ||
6831 !EVP_VerifyInit (md_ctx, mdtype) ||
6832 !EVP_VerifyUpdate (md_ctx, data, data_len) ||
6833 (err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey)) < 0) {
6834 php_openssl_store_errors();
6835 }
6836 EVP_MD_CTX_destroy(md_ctx);
6837 EVP_PKEY_free(pkey);
6838 RETURN_LONG(err);
6839 }
6840 /* }}} */
6841
6842 /* {{{ Seals data */
PHP_FUNCTION(openssl_seal)6843 PHP_FUNCTION(openssl_seal)
6844 {
6845 zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL;
6846 HashTable *pubkeysht;
6847 EVP_PKEY **pkeys;
6848 int i, len1, len2, *eksl, nkeys, iv_len;
6849 unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks;
6850 char * data;
6851 size_t data_len;
6852 char *method;
6853 size_t method_len;
6854 const EVP_CIPHER *cipher;
6855 EVP_CIPHER_CTX *ctx;
6856
6857 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z", &data, &data_len,
6858 &sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) {
6859 RETURN_THROWS();
6860 }
6861
6862 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
6863
6864 pubkeysht = Z_ARRVAL_P(pubkeys);
6865 nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
6866 if (!nkeys) {
6867 zend_argument_value_error(4, "cannot be empty");
6868 RETURN_THROWS();
6869 }
6870
6871 cipher = EVP_get_cipherbyname(method);
6872 if (!cipher) {
6873 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
6874 RETURN_FALSE;
6875 }
6876
6877 iv_len = EVP_CIPHER_iv_length(cipher);
6878 if (!iv && iv_len > 0) {
6879 zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm");
6880 RETURN_THROWS();
6881 }
6882
6883 pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
6884 eksl = safe_emalloc(nkeys, sizeof(*eksl), 0);
6885 eks = safe_emalloc(nkeys, sizeof(*eks), 0);
6886 memset(eks, 0, sizeof(*eks) * nkeys);
6887 memset(pkeys, 0, sizeof(*pkeys) * nkeys);
6888
6889 /* get the public keys we are using to seal this data */
6890 i = 0;
6891 ZEND_HASH_FOREACH_VAL(pubkeysht, pubkey) {
6892 pkeys[i] = php_openssl_pkey_from_zval(pubkey, 1, NULL, 0, 4);
6893 if (pkeys[i] == NULL) {
6894 if (!EG(exception)) {
6895 php_error_docref(NULL, E_WARNING, "Not a public key (%dth member of pubkeys)", i+1);
6896 }
6897 RETVAL_FALSE;
6898 goto clean_exit;
6899 }
6900 eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
6901 i++;
6902 } ZEND_HASH_FOREACH_END();
6903
6904 ctx = EVP_CIPHER_CTX_new();
6905 if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) {
6906 EVP_CIPHER_CTX_free(ctx);
6907 php_openssl_store_errors();
6908 RETVAL_FALSE;
6909 goto clean_exit;
6910 }
6911
6912 /* allocate one byte extra to make room for \0 */
6913 buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(ctx));
6914 EVP_CIPHER_CTX_reset(ctx);
6915
6916 if (EVP_SealInit(ctx, cipher, eks, eksl, &iv_buf[0], pkeys, nkeys) <= 0 ||
6917 !EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
6918 !EVP_SealFinal(ctx, buf + len1, &len2)) {
6919 efree(buf);
6920 EVP_CIPHER_CTX_free(ctx);
6921 php_openssl_store_errors();
6922 RETVAL_FALSE;
6923 goto clean_exit;
6924 }
6925
6926 if (len1 + len2 > 0) {
6927 ZEND_TRY_ASSIGN_REF_NEW_STR(sealdata, zend_string_init((char*)buf, len1 + len2, 0));
6928 efree(buf);
6929
6930 ekeys = zend_try_array_init(ekeys);
6931 if (!ekeys) {
6932 EVP_CIPHER_CTX_free(ctx);
6933 goto clean_exit;
6934 }
6935
6936 for (i=0; i<nkeys; i++) {
6937 eks[i][eksl[i]] = '\0';
6938 add_next_index_stringl(ekeys, (const char*)eks[i], eksl[i]);
6939 efree(eks[i]);
6940 eks[i] = NULL;
6941 }
6942
6943 if (iv) {
6944 iv_buf[iv_len] = '\0';
6945 ZEND_TRY_ASSIGN_REF_NEW_STR(iv, zend_string_init((char*)iv_buf, iv_len, 0));
6946 }
6947 } else {
6948 efree(buf);
6949 }
6950 RETVAL_LONG(len1 + len2);
6951 EVP_CIPHER_CTX_free(ctx);
6952
6953 clean_exit:
6954 for (i=0; i<nkeys; i++) {
6955 if (pkeys[i] != NULL) {
6956 EVP_PKEY_free(pkeys[i]);
6957 }
6958 if (eks[i]) {
6959 efree(eks[i]);
6960 }
6961 }
6962 efree(eks);
6963 efree(eksl);
6964 efree(pkeys);
6965 }
6966 /* }}} */
6967
6968 /* {{{ Opens data */
PHP_FUNCTION(openssl_open)6969 PHP_FUNCTION(openssl_open)
6970 {
6971 zval *privkey, *opendata;
6972 EVP_PKEY *pkey;
6973 int len1, len2, cipher_iv_len;
6974 unsigned char *buf, *iv_buf;
6975 EVP_CIPHER_CTX *ctx;
6976 char * data;
6977 size_t data_len;
6978 char * ekey;
6979 size_t ekey_len;
6980 char *method, *iv = NULL;
6981 size_t method_len, iv_len = 0;
6982 const EVP_CIPHER *cipher;
6983
6984 if (zend_parse_parameters(ZEND_NUM_ARGS(), "szszs|s!", &data, &data_len, &opendata,
6985 &ekey, &ekey_len, &privkey, &method, &method_len, &iv, &iv_len) == FAILURE) {
6986 RETURN_THROWS();
6987 }
6988
6989 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
6990 PHP_OPENSSL_CHECK_SIZE_T_TO_INT(ekey_len, ekey, 3);
6991 pkey = php_openssl_pkey_from_zval(privkey, 0, "", 0, 4);
6992
6993 if (pkey == NULL) {
6994 if (!EG(exception)) {
6995 php_error_docref(NULL, E_WARNING, "Unable to coerce parameter 4 into a private key");
6996 }
6997 RETURN_FALSE;
6998 }
6999
7000 cipher = EVP_get_cipherbyname(method);
7001 if (!cipher) {
7002 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7003 RETURN_FALSE;
7004 }
7005
7006 cipher_iv_len = EVP_CIPHER_iv_length(cipher);
7007 if (cipher_iv_len > 0) {
7008 if (!iv) {
7009 zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm");
7010 RETURN_THROWS();
7011 }
7012 if ((size_t)cipher_iv_len != iv_len) {
7013 php_error_docref(NULL, E_WARNING, "IV length is invalid");
7014 RETURN_FALSE;
7015 }
7016 iv_buf = (unsigned char *)iv;
7017 } else {
7018 iv_buf = NULL;
7019 }
7020
7021 buf = emalloc(data_len + 1);
7022
7023 ctx = EVP_CIPHER_CTX_new();
7024 if (ctx != NULL && EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) &&
7025 EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) &&
7026 EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) {
7027 buf[len1 + len2] = '\0';
7028 ZEND_TRY_ASSIGN_REF_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
7029 RETVAL_TRUE;
7030 } else {
7031 php_openssl_store_errors();
7032 RETVAL_FALSE;
7033 }
7034
7035 efree(buf);
7036 EVP_PKEY_free(pkey);
7037 EVP_CIPHER_CTX_free(ctx);
7038 }
7039 /* }}} */
7040
php_openssl_add_method_or_alias(const OBJ_NAME * name,void * arg)7041 static void php_openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */
7042 {
7043 add_next_index_string((zval*)arg, (char*)name->name);
7044 }
7045 /* }}} */
7046
php_openssl_add_method(const OBJ_NAME * name,void * arg)7047 static void php_openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */
7048 {
7049 if (name->alias == 0) {
7050 add_next_index_string((zval*)arg, (char*)name->name);
7051 }
7052 }
7053 /* }}} */
7054
7055 /* {{{ Return array of available digest algorithms */
PHP_FUNCTION(openssl_get_md_methods)7056 PHP_FUNCTION(openssl_get_md_methods)
7057 {
7058 bool aliases = 0;
7059
7060 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
7061 RETURN_THROWS();
7062 }
7063 array_init(return_value);
7064 OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
7065 aliases ? php_openssl_add_method_or_alias: php_openssl_add_method,
7066 return_value);
7067 }
7068 /* }}} */
7069
7070 #if PHP_OPENSSL_API_VERSION >= 0x30000
php_openssl_add_cipher_name(const char * name,void * arg)7071 static void php_openssl_add_cipher_name(const char *name, void *arg)
7072 {
7073 size_t len = strlen(name);
7074 zend_string *str = zend_string_alloc(len, 0);
7075 zend_str_tolower_copy(ZSTR_VAL(str), name, len);
7076 add_next_index_str((zval*)arg, str);
7077 }
7078
php_openssl_add_cipher_or_alias(EVP_CIPHER * cipher,void * arg)7079 static void php_openssl_add_cipher_or_alias(EVP_CIPHER *cipher, void *arg)
7080 {
7081 EVP_CIPHER_names_do_all(cipher, php_openssl_add_cipher_name, arg);
7082 }
7083
php_openssl_add_cipher(EVP_CIPHER * cipher,void * arg)7084 static void php_openssl_add_cipher(EVP_CIPHER *cipher, void *arg)
7085 {
7086 php_openssl_add_cipher_name(EVP_CIPHER_get0_name(cipher), arg);
7087 }
7088
php_openssl_compare_func(Bucket * a,Bucket * b)7089 static int php_openssl_compare_func(Bucket *a, Bucket *b)
7090 {
7091 return string_compare_function(&a->val, &b->val);
7092 }
7093 #endif
7094
7095 /* {{{ Return array of available cipher algorithms */
PHP_FUNCTION(openssl_get_cipher_methods)7096 PHP_FUNCTION(openssl_get_cipher_methods)
7097 {
7098 bool aliases = 0;
7099
7100 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
7101 RETURN_THROWS();
7102 }
7103 array_init(return_value);
7104 #if PHP_OPENSSL_API_VERSION >= 0x30000
7105 EVP_CIPHER_do_all_provided(NULL,
7106 aliases ? php_openssl_add_cipher_or_alias : php_openssl_add_cipher,
7107 return_value);
7108 zend_hash_sort(Z_ARRVAL_P(return_value), php_openssl_compare_func, 1);
7109 #else
7110 OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
7111 aliases ? php_openssl_add_method_or_alias : php_openssl_add_method,
7112 return_value);
7113 #endif
7114 }
7115 /* }}} */
7116
7117 /* {{{ Return array of available elliptic curves */
7118 #ifdef HAVE_EVP_PKEY_EC
PHP_FUNCTION(openssl_get_curve_names)7119 PHP_FUNCTION(openssl_get_curve_names)
7120 {
7121 EC_builtin_curve *curves = NULL;
7122 const char *sname;
7123 size_t i;
7124 size_t len = EC_get_builtin_curves(NULL, 0);
7125
7126 if (zend_parse_parameters_none() == FAILURE) {
7127 RETURN_THROWS();
7128 }
7129
7130 curves = emalloc(sizeof(EC_builtin_curve) * len);
7131 if (!EC_get_builtin_curves(curves, len)) {
7132 RETURN_FALSE;
7133 }
7134
7135 array_init(return_value);
7136 for (i = 0; i < len; i++) {
7137 sname = OBJ_nid2sn(curves[i].nid);
7138 if (sname != NULL) {
7139 add_next_index_string(return_value, sname);
7140 }
7141 }
7142 efree(curves);
7143 }
7144 #endif
7145 /* }}} */
7146
7147 /* {{{ Computes digest hash value for given data using given method, returns raw or binhex encoded string */
PHP_FUNCTION(openssl_digest)7148 PHP_FUNCTION(openssl_digest)
7149 {
7150 bool raw_output = 0;
7151 char *data, *method;
7152 size_t data_len, method_len;
7153 const EVP_MD *mdtype;
7154 EVP_MD_CTX *md_ctx;
7155 unsigned int siglen;
7156 zend_string *sigbuf;
7157
7158 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
7159 RETURN_THROWS();
7160 }
7161 mdtype = EVP_get_digestbyname(method);
7162 if (!mdtype) {
7163 php_error_docref(NULL, E_WARNING, "Unknown digest algorithm");
7164 RETURN_FALSE;
7165 }
7166
7167 siglen = EVP_MD_size(mdtype);
7168 sigbuf = zend_string_alloc(siglen, 0);
7169
7170 md_ctx = EVP_MD_CTX_create();
7171 if (EVP_DigestInit(md_ctx, mdtype) &&
7172 EVP_DigestUpdate(md_ctx, (unsigned char *)data, data_len) &&
7173 EVP_DigestFinal (md_ctx, (unsigned char *)ZSTR_VAL(sigbuf), &siglen)) {
7174 if (raw_output) {
7175 ZSTR_VAL(sigbuf)[siglen] = '\0';
7176 ZSTR_LEN(sigbuf) = siglen;
7177 RETVAL_STR(sigbuf);
7178 } else {
7179 int digest_str_len = siglen * 2;
7180 zend_string *digest_str = zend_string_alloc(digest_str_len, 0);
7181
7182 make_digest_ex(ZSTR_VAL(digest_str), (unsigned char*)ZSTR_VAL(sigbuf), siglen);
7183 ZSTR_VAL(digest_str)[digest_str_len] = '\0';
7184 zend_string_release_ex(sigbuf, 0);
7185 RETVAL_NEW_STR(digest_str);
7186 }
7187 } else {
7188 php_openssl_store_errors();
7189 zend_string_release_ex(sigbuf, 0);
7190 RETVAL_FALSE;
7191 }
7192
7193 EVP_MD_CTX_destroy(md_ctx);
7194 }
7195 /* }}} */
7196
7197 /* Cipher mode info */
7198 struct php_openssl_cipher_mode {
7199 bool is_aead;
7200 bool is_single_run_aead;
7201 bool set_tag_length_always;
7202 bool set_tag_length_when_encrypting;
7203 int aead_get_tag_flag;
7204 int aead_set_tag_flag;
7205 int aead_ivlen_flag;
7206 };
7207
7208 #if PHP_OPENSSL_API_VERSION >= 0x10100
php_openssl_set_aead_flags(struct php_openssl_cipher_mode * mode)7209 static inline void php_openssl_set_aead_flags(struct php_openssl_cipher_mode *mode) {
7210 mode->is_aead = true;
7211 mode->aead_get_tag_flag = EVP_CTRL_AEAD_GET_TAG;
7212 mode->aead_set_tag_flag = EVP_CTRL_AEAD_SET_TAG;
7213 mode->aead_ivlen_flag = EVP_CTRL_AEAD_SET_IVLEN;
7214 }
7215 #endif
7216
php_openssl_load_cipher_mode(struct php_openssl_cipher_mode * mode,const EVP_CIPHER * cipher_type)7217 static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type)
7218 {
7219 int cipher_mode = EVP_CIPHER_mode(cipher_type);
7220 memset(mode, 0, sizeof(struct php_openssl_cipher_mode));
7221 switch (cipher_mode) {
7222 #if PHP_OPENSSL_API_VERSION >= 0x10100
7223 /* Since OpenSSL 1.1, all AEAD ciphers use a common framework. We check for
7224 * EVP_CIPH_OCB_MODE, because LibreSSL does not support it. */
7225 case EVP_CIPH_GCM_MODE:
7226 case EVP_CIPH_CCM_MODE:
7227 # ifdef EVP_CIPH_OCB_MODE
7228 case EVP_CIPH_OCB_MODE:
7229 /* For OCB mode, explicitly set the tag length even when decrypting,
7230 * see https://github.com/openssl/openssl/issues/8331. */
7231 mode->set_tag_length_always = cipher_mode == EVP_CIPH_OCB_MODE;
7232 # endif
7233 php_openssl_set_aead_flags(mode);
7234 mode->set_tag_length_when_encrypting = cipher_mode == EVP_CIPH_CCM_MODE;
7235 mode->is_single_run_aead = cipher_mode == EVP_CIPH_CCM_MODE;
7236 break;
7237 # ifdef NID_chacha20_poly1305
7238 default:
7239 if (EVP_CIPHER_nid(cipher_type) == NID_chacha20_poly1305) {
7240 php_openssl_set_aead_flags(mode);
7241 }
7242 break;
7243
7244 # endif
7245 #else
7246 # ifdef EVP_CIPH_GCM_MODE
7247 case EVP_CIPH_GCM_MODE:
7248 mode->is_aead = 1;
7249 mode->aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG;
7250 mode->aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG;
7251 mode->aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN;
7252 break;
7253 # endif
7254 # ifdef EVP_CIPH_CCM_MODE
7255 case EVP_CIPH_CCM_MODE:
7256 mode->is_aead = 1;
7257 mode->is_single_run_aead = 1;
7258 mode->set_tag_length_when_encrypting = 1;
7259 mode->aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG;
7260 mode->aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG;
7261 mode->aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN;
7262 break;
7263 # endif
7264 #endif
7265 }
7266 }
7267
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)7268 static int php_openssl_validate_iv(const char **piv, size_t *piv_len, size_t iv_required_len,
7269 bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */
7270 {
7271 char *iv_new;
7272
7273 if (mode->is_aead) {
7274 if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_ivlen_flag, *piv_len, NULL) != 1) {
7275 php_error_docref(NULL, E_WARNING, "Setting of IV length for AEAD mode failed");
7276 return FAILURE;
7277 }
7278 return SUCCESS;
7279 }
7280
7281 /* Best case scenario, user behaved */
7282 if (*piv_len == iv_required_len) {
7283 return SUCCESS;
7284 }
7285
7286 iv_new = ecalloc(1, iv_required_len + 1);
7287
7288 if (*piv_len == 0) {
7289 /* BC behavior */
7290 *piv_len = iv_required_len;
7291 *piv = iv_new;
7292 *free_iv = 1;
7293 return SUCCESS;
7294
7295 }
7296
7297 if (*piv_len < iv_required_len) {
7298 php_error_docref(NULL, E_WARNING,
7299 "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0",
7300 *piv_len, iv_required_len);
7301 memcpy(iv_new, *piv, *piv_len);
7302 *piv_len = iv_required_len;
7303 *piv = iv_new;
7304 *free_iv = 1;
7305 return SUCCESS;
7306 }
7307
7308 php_error_docref(NULL, E_WARNING,
7309 "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating",
7310 *piv_len, iv_required_len);
7311 memcpy(iv_new, *piv, iv_required_len);
7312 *piv_len = iv_required_len;
7313 *piv = iv_new;
7314 *free_iv = 1;
7315 return SUCCESS;
7316
7317 }
7318 /* }}} */
7319
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)7320 static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
7321 EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
7322 const char **ppassword, size_t *ppassword_len, bool *free_password,
7323 const char **piv, size_t *piv_len, bool *free_iv,
7324 const char *tag, int tag_len, zend_long options, int enc) /* {{{ */
7325 {
7326 unsigned char *key;
7327 int key_len, password_len;
7328 size_t max_iv_len;
7329
7330 *free_password = 0;
7331
7332 max_iv_len = EVP_CIPHER_iv_length(cipher_type);
7333 if (enc && *piv_len == 0 && max_iv_len > 0 && !mode->is_aead) {
7334 php_error_docref(NULL, E_WARNING,
7335 "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
7336 }
7337
7338 if (!EVP_CipherInit_ex(cipher_ctx, cipher_type, NULL, NULL, NULL, enc)) {
7339 php_openssl_store_errors();
7340 return FAILURE;
7341 }
7342 if (php_openssl_validate_iv(piv, piv_len, max_iv_len, free_iv, cipher_ctx, mode) == FAILURE) {
7343 return FAILURE;
7344 }
7345 if (mode->set_tag_length_always || (enc && mode->set_tag_length_when_encrypting)) {
7346 if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL)) {
7347 php_error_docref(NULL, E_WARNING, "Setting tag length for AEAD cipher failed");
7348 return FAILURE;
7349 }
7350 }
7351 if (!enc && tag && tag_len > 0) {
7352 if (!mode->is_aead) {
7353 php_error_docref(NULL, E_WARNING, "The tag cannot be used because the cipher algorithm does not support AEAD");
7354 } else if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, (unsigned char *) tag)) {
7355 php_error_docref(NULL, E_WARNING, "Setting tag for AEAD cipher decryption failed");
7356 return FAILURE;
7357 }
7358 }
7359 /* check and set key */
7360 password_len = (int) *ppassword_len;
7361 key_len = EVP_CIPHER_key_length(cipher_type);
7362 if (key_len > password_len) {
7363 if ((OPENSSL_DONT_ZERO_PAD_KEY & options) && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
7364 php_openssl_store_errors();
7365 php_error_docref(NULL, E_WARNING, "Key length cannot be set for the cipher algorithm");
7366 return FAILURE;
7367 }
7368 key = emalloc(key_len);
7369 memset(key, 0, key_len);
7370 memcpy(key, *ppassword, password_len);
7371 *ppassword = (char *) key;
7372 *ppassword_len = key_len;
7373 *free_password = 1;
7374 } else {
7375 if (password_len > key_len && !EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len)) {
7376 php_openssl_store_errors();
7377 }
7378 key = (unsigned char*)*ppassword;
7379 }
7380
7381 if (!EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)*piv, enc)) {
7382 php_openssl_store_errors();
7383 return FAILURE;
7384 }
7385 if (options & OPENSSL_ZERO_PADDING) {
7386 EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
7387 }
7388
7389 return SUCCESS;
7390 }
7391 /* }}} */
7392
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)7393 static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
7394 EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
7395 zend_string **poutbuf, int *poutlen, const char *data, size_t data_len,
7396 const char *aad, size_t aad_len, int enc) /* {{{ */
7397 {
7398 int i = 0;
7399
7400 if (mode->is_single_run_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
7401 php_openssl_store_errors();
7402 php_error_docref(NULL, E_WARNING, "Setting of data length failed");
7403 return FAILURE;
7404 }
7405
7406 if (mode->is_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, (const unsigned char *) aad, (int) aad_len)) {
7407 php_openssl_store_errors();
7408 php_error_docref(NULL, E_WARNING, "Setting of additional application data failed");
7409 return FAILURE;
7410 }
7411
7412 *poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
7413
7414 if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
7415 &i, (const unsigned char *)data, (int)data_len)) {
7416 /* we don't show warning when we fail but if we ever do, then it should look like this:
7417 if (mode->is_single_run_aead && !enc) {
7418 php_error_docref(NULL, E_WARNING, "Tag verifycation failed");
7419 } else {
7420 php_error_docref(NULL, E_WARNING, enc ? "Encryption failed" : "Decryption failed");
7421 }
7422 */
7423 php_openssl_store_errors();
7424 zend_string_release_ex(*poutbuf, 0);
7425 return FAILURE;
7426 }
7427
7428 *poutlen = i;
7429
7430 return SUCCESS;
7431 }
7432 /* }}} */
7433
7434
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)7435 PHP_OPENSSL_API zend_string* php_openssl_encrypt(
7436 const char *data, size_t data_len,
7437 const char *method, size_t method_len,
7438 const char *password, size_t password_len,
7439 zend_long options,
7440 const char *iv, size_t iv_len,
7441 zval *tag, zend_long tag_len,
7442 const char *aad, size_t aad_len)
7443 {
7444 const EVP_CIPHER *cipher_type;
7445 EVP_CIPHER_CTX *cipher_ctx;
7446 struct php_openssl_cipher_mode mode;
7447 int i = 0, outlen;
7448 bool free_iv = 0, free_password = 0;
7449 zend_string *outbuf = NULL;
7450
7451 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(data_len, data);
7452 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(password_len, password);
7453 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(aad_len, aad);
7454 PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(tag_len, tag_len);
7455
7456
7457 cipher_type = EVP_get_cipherbyname(method);
7458 if (!cipher_type) {
7459 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7460 return NULL;
7461 }
7462
7463 cipher_ctx = EVP_CIPHER_CTX_new();
7464 if (!cipher_ctx) {
7465 php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
7466 return NULL;
7467 }
7468
7469 php_openssl_load_cipher_mode(&mode, cipher_type);
7470
7471 if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
7472 &password, &password_len, &free_password,
7473 &iv, &iv_len, &free_iv, NULL, tag_len, options, 1) == FAILURE ||
7474 php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
7475 data, data_len, aad, aad_len, 1) == FAILURE) {
7476 outbuf = NULL;
7477 } else if (EVP_EncryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
7478 outlen += i;
7479 if (options & OPENSSL_RAW_DATA) {
7480 ZSTR_VAL(outbuf)[outlen] = '\0';
7481 ZSTR_LEN(outbuf) = outlen;
7482 } else {
7483 zend_string *base64_str;
7484
7485 base64_str = php_base64_encode((unsigned char*)ZSTR_VAL(outbuf), outlen);
7486 zend_string_release_ex(outbuf, 0);
7487 outbuf = base64_str;
7488 }
7489 if (mode.is_aead && tag) {
7490 zend_string *tag_str = zend_string_alloc(tag_len, 0);
7491
7492 if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode.aead_get_tag_flag, tag_len, ZSTR_VAL(tag_str)) == 1) {
7493 ZSTR_VAL(tag_str)[tag_len] = '\0';
7494 ZSTR_LEN(tag_str) = tag_len;
7495 ZEND_TRY_ASSIGN_REF_NEW_STR(tag, tag_str);
7496 } else {
7497 php_error_docref(NULL, E_WARNING, "Retrieving verification tag failed");
7498 zend_string_release_ex(tag_str, 0);
7499 zend_string_release_ex(outbuf, 0);
7500 outbuf = NULL;
7501 }
7502 } else if (tag) {
7503 ZEND_TRY_ASSIGN_REF_NULL(tag);
7504 } else if (mode.is_aead) {
7505 php_error_docref(NULL, E_WARNING, "A tag should be provided when using AEAD mode");
7506 zend_string_release_ex(outbuf, 0);
7507 outbuf = NULL;
7508 }
7509 } else {
7510 php_openssl_store_errors();
7511 zend_string_release_ex(outbuf, 0);
7512 outbuf = NULL;
7513 }
7514
7515 if (free_password) {
7516 efree((void *) password);
7517 }
7518 if (free_iv) {
7519 efree((void *) iv);
7520 }
7521 EVP_CIPHER_CTX_reset(cipher_ctx);
7522 EVP_CIPHER_CTX_free(cipher_ctx);
7523 return outbuf;
7524 }
7525
7526 /* {{{ Encrypts given data with given method and key, returns raw or base64 encoded string */
PHP_FUNCTION(openssl_encrypt)7527 PHP_FUNCTION(openssl_encrypt)
7528 {
7529 zend_long options = 0, tag_len = 16;
7530 char *data, *method, *password, *iv = "", *aad = "";
7531 size_t data_len, method_len, password_len, iv_len = 0, aad_len = 0;
7532 zend_string *ret;
7533 zval *tag = NULL;
7534
7535 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lszsl", &data, &data_len, &method, &method_len,
7536 &password, &password_len, &options, &iv, &iv_len, &tag, &aad, &aad_len, &tag_len) == FAILURE) {
7537 RETURN_THROWS();
7538 }
7539
7540 if ((ret = php_openssl_encrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
7541 RETVAL_STR(ret);
7542 } else {
7543 RETVAL_FALSE;
7544 }
7545 }
7546 /* }}} */
7547
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)7548 PHP_OPENSSL_API zend_string* php_openssl_decrypt(
7549 const char *data, size_t data_len,
7550 const char *method, size_t method_len,
7551 const char *password, size_t password_len,
7552 zend_long options,
7553 const char *iv, size_t iv_len,
7554 const char *tag, zend_long tag_len,
7555 const char *aad, size_t aad_len)
7556 {
7557 const EVP_CIPHER *cipher_type;
7558 EVP_CIPHER_CTX *cipher_ctx;
7559 struct php_openssl_cipher_mode mode;
7560 int i = 0, outlen;
7561 zend_string *base64_str = NULL;
7562 bool free_iv = 0, free_password = 0;
7563 zend_string *outbuf = NULL;
7564
7565 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(data_len, data);
7566 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(password_len, password);
7567 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(aad_len, aad);
7568 PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(tag_len, tag);
7569
7570
7571 cipher_type = EVP_get_cipherbyname(method);
7572 if (!cipher_type) {
7573 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7574 return NULL;
7575 }
7576
7577 cipher_ctx = EVP_CIPHER_CTX_new();
7578 if (!cipher_ctx) {
7579 php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
7580 return NULL;
7581 }
7582
7583 php_openssl_load_cipher_mode(&mode, cipher_type);
7584
7585 if (!(options & OPENSSL_RAW_DATA)) {
7586 base64_str = php_base64_decode((unsigned char*)data, data_len);
7587 if (!base64_str) {
7588 php_error_docref(NULL, E_WARNING, "Failed to base64 decode the input");
7589 EVP_CIPHER_CTX_free(cipher_ctx);
7590 return NULL;
7591 }
7592 data_len = ZSTR_LEN(base64_str);
7593 data = ZSTR_VAL(base64_str);
7594 }
7595
7596 if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode,
7597 &password, &password_len, &free_password,
7598 &iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE ||
7599 php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen,
7600 data, data_len, aad, aad_len, 0) == FAILURE) {
7601 outbuf = NULL;
7602 } else if (mode.is_single_run_aead ||
7603 EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) {
7604 outlen += i;
7605 ZSTR_VAL(outbuf)[outlen] = '\0';
7606 ZSTR_LEN(outbuf) = outlen;
7607 } else {
7608 php_openssl_store_errors();
7609 zend_string_release_ex(outbuf, 0);
7610 outbuf = NULL;
7611 }
7612
7613 if (free_password) {
7614 efree((void *) password);
7615 }
7616 if (free_iv) {
7617 efree((void *) iv);
7618 }
7619 if (base64_str) {
7620 zend_string_release_ex(base64_str, 0);
7621 }
7622 EVP_CIPHER_CTX_reset(cipher_ctx);
7623 EVP_CIPHER_CTX_free(cipher_ctx);
7624 return outbuf;
7625 }
7626
7627 /* {{{ Takes raw or base64 encoded string and decrypts it using given method and key */
PHP_FUNCTION(openssl_decrypt)7628 PHP_FUNCTION(openssl_decrypt)
7629 {
7630 zend_long options = 0;
7631 char *data, *method, *password, *iv = "", *tag = NULL, *aad = "";
7632 size_t data_len, method_len, password_len, iv_len = 0, tag_len = 0, aad_len = 0;
7633 zend_string *ret;
7634
7635 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lss!s", &data, &data_len, &method, &method_len,
7636 &password, &password_len, &options, &iv, &iv_len, &tag, &tag_len, &aad, &aad_len) == FAILURE) {
7637 RETURN_THROWS();
7638 }
7639
7640 if (!method_len) {
7641 zend_argument_value_error(2, "cannot be empty");
7642 RETURN_THROWS();
7643 }
7644
7645 if ((ret = php_openssl_decrypt(data, data_len, method, method_len, password, password_len, options, iv, iv_len, tag, tag_len, aad, aad_len))) {
7646 RETVAL_STR(ret);
7647 } else {
7648 RETVAL_FALSE;
7649 }
7650 }
7651 /* }}} */
7652
php_openssl_get_evp_cipher_by_name(const char * method)7653 static inline const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method)
7654 {
7655 const EVP_CIPHER *cipher_type;
7656
7657 cipher_type = EVP_get_cipherbyname(method);
7658 if (!cipher_type) {
7659 php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
7660 return NULL;
7661 }
7662
7663 return cipher_type;
7664 }
7665
php_openssl_cipher_iv_length(const char * method)7666 PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method)
7667 {
7668 const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method);
7669
7670 return cipher_type == NULL ? -1 : EVP_CIPHER_iv_length(cipher_type);
7671 }
7672
PHP_FUNCTION(openssl_cipher_iv_length)7673 PHP_FUNCTION(openssl_cipher_iv_length)
7674 {
7675 zend_string *method;
7676 zend_long ret;
7677
7678 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &method) == FAILURE) {
7679 RETURN_THROWS();
7680 }
7681
7682 if (ZSTR_LEN(method) == 0) {
7683 zend_argument_value_error(1, "cannot be empty");
7684 RETURN_THROWS();
7685 }
7686
7687 /* Warning is emitted in php_openssl_cipher_iv_length */
7688 if ((ret = php_openssl_cipher_iv_length(ZSTR_VAL(method))) == -1) {
7689 RETURN_FALSE;
7690 }
7691
7692 RETURN_LONG(ret);
7693 }
7694
php_openssl_cipher_key_length(const char * method)7695 PHP_OPENSSL_API zend_long php_openssl_cipher_key_length(const char *method)
7696 {
7697 const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method);
7698
7699 return cipher_type == NULL ? -1 : EVP_CIPHER_key_length(cipher_type);
7700 }
7701
PHP_FUNCTION(openssl_cipher_key_length)7702 PHP_FUNCTION(openssl_cipher_key_length)
7703 {
7704 zend_string *method;
7705 zend_long ret;
7706
7707 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &method) == FAILURE) {
7708 RETURN_THROWS();
7709 }
7710
7711 if (ZSTR_LEN(method) == 0) {
7712 zend_argument_value_error(1, "cannot be empty");
7713 RETURN_THROWS();
7714 }
7715
7716 /* Warning is emitted in php_openssl_cipher_key_length */
7717 if ((ret = php_openssl_cipher_key_length(ZSTR_VAL(method))) == -1) {
7718 RETURN_FALSE;
7719 }
7720
7721 RETURN_LONG(ret);
7722 }
7723
php_openssl_random_pseudo_bytes(zend_long buffer_length)7724 PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_length)
7725 {
7726 zend_string *buffer = NULL;
7727 if (buffer_length <= 0) {
7728 zend_argument_value_error(1, "must be greater than 0");
7729 return NULL;
7730 }
7731 if (ZEND_LONG_INT_OVFL(buffer_length)) {
7732 zend_argument_value_error(1, "must be less than or equal to %d", INT_MAX);
7733 return NULL;
7734 }
7735 buffer = zend_string_alloc(buffer_length, 0);
7736
7737 PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(buffer_length, length);
7738 PHP_OPENSSL_RAND_ADD_TIME();
7739 if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), (int)buffer_length) <= 0) {
7740 zend_string_release_ex(buffer, 0);
7741 zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
7742 return NULL;
7743 } else {
7744 php_openssl_store_errors();
7745 }
7746
7747 return buffer;
7748 }
7749
7750 /* {{{ Returns a string of the length specified filled with random pseudo bytes */
PHP_FUNCTION(openssl_random_pseudo_bytes)7751 PHP_FUNCTION(openssl_random_pseudo_bytes)
7752 {
7753 zend_string *buffer = NULL;
7754 zend_long buffer_length;
7755 zval *zstrong_result_returned = NULL;
7756
7757 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) {
7758 RETURN_THROWS();
7759 }
7760
7761 if (zstrong_result_returned) {
7762 ZEND_TRY_ASSIGN_REF_FALSE(zstrong_result_returned);
7763 }
7764
7765 if ((buffer = php_openssl_random_pseudo_bytes(buffer_length))) {
7766 ZSTR_VAL(buffer)[buffer_length] = 0;
7767 RETVAL_NEW_STR(buffer);
7768 }
7769
7770 if (zstrong_result_returned) {
7771 ZEND_TRY_ASSIGN_REF_TRUE(zstrong_result_returned);
7772 }
7773 }
7774 /* }}} */
7775