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