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