1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Authors: Stig Venaas <venaas@php.net> | 16 | Wez Furlong <wez@thebrainroom.com> | 17 | Sascha Kettler <kettler@gmx.net> | 18 | Pierre-Alain Joye <pierre@php.net> | 19 | Marc Delling <delling@silpion.de> (PKCS12 functions) | 20 +----------------------------------------------------------------------+ 21 */ 22 23/* $Id$ */ 24 25#ifdef HAVE_CONFIG_H 26#include "config.h" 27#endif 28 29#include "php.h" 30#include "php_openssl.h" 31 32/* PHP Includes */ 33#include "ext/standard/file.h" 34#include "ext/standard/info.h" 35#include "ext/standard/php_fopen_wrappers.h" 36#include "ext/standard/md5.h" 37#include "ext/standard/base64.h" 38 39/* OpenSSL includes */ 40#include <openssl/evp.h> 41#include <openssl/x509.h> 42#include <openssl/x509v3.h> 43#include <openssl/crypto.h> 44#include <openssl/pem.h> 45#include <openssl/err.h> 46#include <openssl/conf.h> 47#include <openssl/rand.h> 48#include <openssl/ssl.h> 49#include <openssl/pkcs12.h> 50 51/* Common */ 52#include <time.h> 53 54#ifdef NETWARE 55#define timezone _timezone /* timezone is called _timezone in LibC */ 56#endif 57 58#define DEFAULT_KEY_LENGTH 512 59#define MIN_KEY_LENGTH 384 60 61#define OPENSSL_ALGO_SHA1 1 62#define OPENSSL_ALGO_MD5 2 63#define OPENSSL_ALGO_MD4 3 64#ifdef HAVE_OPENSSL_MD2_H 65#define OPENSSL_ALGO_MD2 4 66#endif 67#define OPENSSL_ALGO_DSS1 5 68#if OPENSSL_VERSION_NUMBER >= 0x0090708fL 69#define OPENSSL_ALGO_SHA224 6 70#define OPENSSL_ALGO_SHA256 7 71#define OPENSSL_ALGO_SHA384 8 72#define OPENSSL_ALGO_SHA512 9 73#define OPENSSL_ALGO_RMD160 10 74#endif 75#define DEBUG_SMIME 0 76 77/* FIXME: Use the openssl constants instead of 78 * enum. It is now impossible to match real values 79 * against php constants. Also sorry to break the 80 * enum principles here, BC... 81 */ 82enum php_openssl_key_type { 83 OPENSSL_KEYTYPE_RSA, 84 OPENSSL_KEYTYPE_DSA, 85 OPENSSL_KEYTYPE_DH, 86 OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA, 87#ifdef EVP_PKEY_EC 88 OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1 89#endif 90}; 91 92enum php_openssl_cipher_type { 93 PHP_OPENSSL_CIPHER_RC2_40, 94 PHP_OPENSSL_CIPHER_RC2_128, 95 PHP_OPENSSL_CIPHER_RC2_64, 96 PHP_OPENSSL_CIPHER_DES, 97 PHP_OPENSSL_CIPHER_3DES, 98 PHP_OPENSSL_CIPHER_AES_128_CBC, 99 PHP_OPENSSL_CIPHER_AES_192_CBC, 100 PHP_OPENSSL_CIPHER_AES_256_CBC, 101 102 PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_RC2_40 103}; 104 105PHP_FUNCTION(openssl_get_md_methods); 106PHP_FUNCTION(openssl_get_cipher_methods); 107 108PHP_FUNCTION(openssl_digest); 109PHP_FUNCTION(openssl_encrypt); 110PHP_FUNCTION(openssl_decrypt); 111PHP_FUNCTION(openssl_cipher_iv_length); 112 113PHP_FUNCTION(openssl_dh_compute_key); 114PHP_FUNCTION(openssl_random_pseudo_bytes); 115 116/* {{{ arginfo */ 117ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 0, 2) 118 ZEND_ARG_INFO(0, x509) 119 ZEND_ARG_INFO(0, outfilename) 120 ZEND_ARG_INFO(0, notext) 121ZEND_END_ARG_INFO() 122 123ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export, 0, 0, 2) 124 ZEND_ARG_INFO(0, x509) 125 ZEND_ARG_INFO(1, out) 126 ZEND_ARG_INFO(0, notext) 127ZEND_END_ARG_INFO() 128 129ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_check_private_key, 0) 130 ZEND_ARG_INFO(0, cert) 131 ZEND_ARG_INFO(0, key) 132ZEND_END_ARG_INFO() 133 134ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_parse, 0) 135 ZEND_ARG_INFO(0, x509) 136 ZEND_ARG_INFO(0, shortname) 137ZEND_END_ARG_INFO() 138 139ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_checkpurpose, 0, 0, 3) 140 ZEND_ARG_INFO(0, x509cert) 141 ZEND_ARG_INFO(0, purpose) 142 ZEND_ARG_INFO(0, cainfo) /* array */ 143 ZEND_ARG_INFO(0, untrustedfile) 144ZEND_END_ARG_INFO() 145 146ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_read, 0) 147 ZEND_ARG_INFO(0, cert) 148ZEND_END_ARG_INFO() 149 150ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_free, 0) 151 ZEND_ARG_INFO(0, x509) 152ZEND_END_ARG_INFO() 153 154ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs12_export_to_file, 0, 0, 4) 155 ZEND_ARG_INFO(0, x509) 156 ZEND_ARG_INFO(0, filename) 157 ZEND_ARG_INFO(0, priv_key) 158 ZEND_ARG_INFO(0, pass) 159 ZEND_ARG_INFO(0, args) /* array */ 160ZEND_END_ARG_INFO() 161 162ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_export, 0) 163 ZEND_ARG_INFO(0, x509) 164 ZEND_ARG_INFO(1, out) 165 ZEND_ARG_INFO(0, priv_key) 166 ZEND_ARG_INFO(0, pass) 167 ZEND_ARG_INFO(0, args) /* array */ 168ZEND_END_ARG_INFO() 169 170ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_read, 0) 171 ZEND_ARG_INFO(0, PKCS12) 172 ZEND_ARG_INFO(1, certs) /* array */ 173 ZEND_ARG_INFO(0, pass) 174ZEND_END_ARG_INFO() 175 176ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export_to_file, 0, 0, 2) 177 ZEND_ARG_INFO(0, csr) 178 ZEND_ARG_INFO(0, outfilename) 179 ZEND_ARG_INFO(0, notext) 180ZEND_END_ARG_INFO() 181 182ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export, 0, 0, 2) 183 ZEND_ARG_INFO(0, csr) 184 ZEND_ARG_INFO(1, out) 185 ZEND_ARG_INFO(0, notext) 186ZEND_END_ARG_INFO() 187 188ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_sign, 0, 0, 4) 189 ZEND_ARG_INFO(0, csr) 190 ZEND_ARG_INFO(0, x509) 191 ZEND_ARG_INFO(0, priv_key) 192 ZEND_ARG_INFO(0, days) 193 ZEND_ARG_INFO(0, config_args) /* array */ 194 ZEND_ARG_INFO(0, serial) 195ZEND_END_ARG_INFO() 196 197ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_new, 0, 0, 2) 198 ZEND_ARG_INFO(0, dn) /* array */ 199 ZEND_ARG_INFO(1, privkey) 200 ZEND_ARG_INFO(0, configargs) 201 ZEND_ARG_INFO(0, extraattribs) 202ZEND_END_ARG_INFO() 203 204ZEND_BEGIN_ARG_INFO(arginfo_openssl_csr_get_subject, 0) 205 ZEND_ARG_INFO(0, csr) 206ZEND_END_ARG_INFO() 207 208ZEND_BEGIN_ARG_INFO(arginfo_openssl_csr_get_public_key, 0) 209 ZEND_ARG_INFO(0, csr) 210ZEND_END_ARG_INFO() 211 212ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_new, 0, 0, 0) 213 ZEND_ARG_INFO(0, configargs) /* array */ 214ZEND_END_ARG_INFO() 215 216ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export_to_file, 0, 0, 2) 217 ZEND_ARG_INFO(0, key) 218 ZEND_ARG_INFO(0, outfilename) 219 ZEND_ARG_INFO(0, passphrase) 220 ZEND_ARG_INFO(0, config_args) /* array */ 221ZEND_END_ARG_INFO() 222 223ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export, 0, 0, 2) 224 ZEND_ARG_INFO(0, key) 225 ZEND_ARG_INFO(1, out) 226 ZEND_ARG_INFO(0, passphrase) 227 ZEND_ARG_INFO(0, config_args) /* array */ 228ZEND_END_ARG_INFO() 229 230ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_public, 0) 231 ZEND_ARG_INFO(0, cert) 232ZEND_END_ARG_INFO() 233 234ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_free, 0) 235 ZEND_ARG_INFO(0, key) 236ZEND_END_ARG_INFO() 237 238ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_get_private, 0, 0, 1) 239 ZEND_ARG_INFO(0, key) 240 ZEND_ARG_INFO(0, passphrase) 241ZEND_END_ARG_INFO() 242 243ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_details, 0) 244 ZEND_ARG_INFO(0, key) 245ZEND_END_ARG_INFO() 246 247#if OPENSSL_VERSION_NUMBER >= 0x10000000L 248ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pbkdf2, 0, 0, 4) 249 ZEND_ARG_INFO(0, password) 250 ZEND_ARG_INFO(0, salt) 251 ZEND_ARG_INFO(0, key_length) 252 ZEND_ARG_INFO(0, iterations) 253 ZEND_ARG_INFO(0, digest_algorithm) 254ZEND_END_ARG_INFO() 255#endif 256 257ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2) 258 ZEND_ARG_INFO(0, filename) 259 ZEND_ARG_INFO(0, flags) 260 ZEND_ARG_INFO(0, signerscerts) 261 ZEND_ARG_INFO(0, cainfo) /* array */ 262 ZEND_ARG_INFO(0, extracerts) 263 ZEND_ARG_INFO(0, content) 264ZEND_END_ARG_INFO() 265 266ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_encrypt, 0, 0, 4) 267 ZEND_ARG_INFO(0, infile) 268 ZEND_ARG_INFO(0, outfile) 269 ZEND_ARG_INFO(0, recipcerts) 270 ZEND_ARG_INFO(0, headers) /* array */ 271 ZEND_ARG_INFO(0, flags) 272 ZEND_ARG_INFO(0, cipher) 273ZEND_END_ARG_INFO() 274 275ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_sign, 0, 0, 5) 276 ZEND_ARG_INFO(0, infile) 277 ZEND_ARG_INFO(0, outfile) 278 ZEND_ARG_INFO(0, signcert) 279 ZEND_ARG_INFO(0, signkey) 280 ZEND_ARG_INFO(0, headers) /* array */ 281 ZEND_ARG_INFO(0, flags) 282 ZEND_ARG_INFO(0, extracertsfilename) 283ZEND_END_ARG_INFO() 284 285ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_decrypt, 0, 0, 3) 286 ZEND_ARG_INFO(0, infilename) 287 ZEND_ARG_INFO(0, outfilename) 288 ZEND_ARG_INFO(0, recipcert) 289 ZEND_ARG_INFO(0, recipkey) 290ZEND_END_ARG_INFO() 291 292ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_encrypt, 0, 0, 3) 293 ZEND_ARG_INFO(0, data) 294 ZEND_ARG_INFO(1, crypted) 295 ZEND_ARG_INFO(0, key) 296 ZEND_ARG_INFO(0, padding) 297ZEND_END_ARG_INFO() 298 299ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_decrypt, 0, 0, 3) 300 ZEND_ARG_INFO(0, data) 301 ZEND_ARG_INFO(1, crypted) 302 ZEND_ARG_INFO(0, key) 303 ZEND_ARG_INFO(0, padding) 304ZEND_END_ARG_INFO() 305 306ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_encrypt, 0, 0, 3) 307 ZEND_ARG_INFO(0, data) 308 ZEND_ARG_INFO(1, crypted) 309 ZEND_ARG_INFO(0, key) 310 ZEND_ARG_INFO(0, padding) 311ZEND_END_ARG_INFO() 312 313ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_decrypt, 0, 0, 3) 314 ZEND_ARG_INFO(0, data) 315 ZEND_ARG_INFO(1, crypted) 316 ZEND_ARG_INFO(0, key) 317 ZEND_ARG_INFO(0, padding) 318ZEND_END_ARG_INFO() 319 320ZEND_BEGIN_ARG_INFO(arginfo_openssl_error_string, 0) 321ZEND_END_ARG_INFO() 322 323ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_sign, 0, 0, 3) 324 ZEND_ARG_INFO(0, data) 325 ZEND_ARG_INFO(1, signature) 326 ZEND_ARG_INFO(0, key) 327 ZEND_ARG_INFO(0, method) 328ZEND_END_ARG_INFO() 329 330ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_verify, 0, 0, 3) 331 ZEND_ARG_INFO(0, data) 332 ZEND_ARG_INFO(0, signature) 333 ZEND_ARG_INFO(0, key) 334 ZEND_ARG_INFO(0, method) 335ZEND_END_ARG_INFO() 336 337ZEND_BEGIN_ARG_INFO(arginfo_openssl_seal, 0) 338 ZEND_ARG_INFO(0, data) 339 ZEND_ARG_INFO(1, sealdata) 340 ZEND_ARG_INFO(1, ekeys) /* arary */ 341 ZEND_ARG_INFO(0, pubkeys) /* array */ 342ZEND_END_ARG_INFO() 343 344ZEND_BEGIN_ARG_INFO(arginfo_openssl_open, 0) 345 ZEND_ARG_INFO(0, data) 346 ZEND_ARG_INFO(1, opendata) 347 ZEND_ARG_INFO(0, ekey) 348 ZEND_ARG_INFO(0, privkey) 349ZEND_END_ARG_INFO() 350 351ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_md_methods, 0, 0, 0) 352 ZEND_ARG_INFO(0, aliases) 353ZEND_END_ARG_INFO() 354 355ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_cipher_methods, 0, 0, 0) 356 ZEND_ARG_INFO(0, aliases) 357ZEND_END_ARG_INFO() 358 359ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_digest, 0, 0, 2) 360 ZEND_ARG_INFO(0, data) 361 ZEND_ARG_INFO(0, method) 362 ZEND_ARG_INFO(0, raw_output) 363ZEND_END_ARG_INFO() 364 365ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_encrypt, 0, 0, 3) 366 ZEND_ARG_INFO(0, data) 367 ZEND_ARG_INFO(0, method) 368 ZEND_ARG_INFO(0, password) 369 ZEND_ARG_INFO(0, options) 370 ZEND_ARG_INFO(0, iv) 371ZEND_END_ARG_INFO() 372 373ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3) 374 ZEND_ARG_INFO(0, data) 375 ZEND_ARG_INFO(0, method) 376 ZEND_ARG_INFO(0, password) 377 ZEND_ARG_INFO(0, options) 378 ZEND_ARG_INFO(0, iv) 379ZEND_END_ARG_INFO() 380 381ZEND_BEGIN_ARG_INFO(arginfo_openssl_cipher_iv_length, 0) 382 ZEND_ARG_INFO(0, method) 383ZEND_END_ARG_INFO() 384 385ZEND_BEGIN_ARG_INFO(arginfo_openssl_dh_compute_key, 0) 386 ZEND_ARG_INFO(0, pub_key) 387 ZEND_ARG_INFO(0, dh_key) 388ZEND_END_ARG_INFO() 389 390ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_random_pseudo_bytes, 0, 0, 1) 391 ZEND_ARG_INFO(0, length) 392 ZEND_ARG_INFO(1, result_is_strong) 393ZEND_END_ARG_INFO() 394/* }}} */ 395 396/* {{{ openssl_functions[] 397 */ 398const zend_function_entry openssl_functions[] = { 399/* public/private key functions */ 400 PHP_FE(openssl_pkey_free, arginfo_openssl_pkey_free) 401 PHP_FE(openssl_pkey_new, arginfo_openssl_pkey_new) 402 PHP_FE(openssl_pkey_export, arginfo_openssl_pkey_export) 403 PHP_FE(openssl_pkey_export_to_file, arginfo_openssl_pkey_export_to_file) 404 PHP_FE(openssl_pkey_get_private, arginfo_openssl_pkey_get_private) 405 PHP_FE(openssl_pkey_get_public, arginfo_openssl_pkey_get_public) 406 PHP_FE(openssl_pkey_get_details, arginfo_openssl_pkey_get_details) 407 408 PHP_FALIAS(openssl_free_key, openssl_pkey_free, arginfo_openssl_pkey_free) 409 PHP_FALIAS(openssl_get_privatekey, openssl_pkey_get_private, arginfo_openssl_pkey_get_private) 410 PHP_FALIAS(openssl_get_publickey, openssl_pkey_get_public, arginfo_openssl_pkey_get_public) 411 412/* x.509 cert funcs */ 413 PHP_FE(openssl_x509_read, arginfo_openssl_x509_read) 414 PHP_FE(openssl_x509_free, arginfo_openssl_x509_free) 415 PHP_FE(openssl_x509_parse, arginfo_openssl_x509_parse) 416 PHP_FE(openssl_x509_checkpurpose, arginfo_openssl_x509_checkpurpose) 417 PHP_FE(openssl_x509_check_private_key, arginfo_openssl_x509_check_private_key) 418 PHP_FE(openssl_x509_export, arginfo_openssl_x509_export) 419 PHP_FE(openssl_x509_export_to_file, arginfo_openssl_x509_export_to_file) 420 421/* PKCS12 funcs */ 422 PHP_FE(openssl_pkcs12_export, arginfo_openssl_pkcs12_export) 423 PHP_FE(openssl_pkcs12_export_to_file, arginfo_openssl_pkcs12_export_to_file) 424 PHP_FE(openssl_pkcs12_read, arginfo_openssl_pkcs12_read) 425 426/* CSR funcs */ 427 PHP_FE(openssl_csr_new, arginfo_openssl_csr_new) 428 PHP_FE(openssl_csr_export, arginfo_openssl_csr_export) 429 PHP_FE(openssl_csr_export_to_file, arginfo_openssl_csr_export_to_file) 430 PHP_FE(openssl_csr_sign, arginfo_openssl_csr_sign) 431 PHP_FE(openssl_csr_get_subject, arginfo_openssl_csr_get_subject) 432 PHP_FE(openssl_csr_get_public_key, arginfo_openssl_csr_get_public_key) 433 434 PHP_FE(openssl_digest, arginfo_openssl_digest) 435 PHP_FE(openssl_encrypt, arginfo_openssl_encrypt) 436 PHP_FE(openssl_decrypt, arginfo_openssl_decrypt) 437 PHP_FE(openssl_cipher_iv_length, arginfo_openssl_cipher_iv_length) 438 PHP_FE(openssl_sign, arginfo_openssl_sign) 439 PHP_FE(openssl_verify, arginfo_openssl_verify) 440 PHP_FE(openssl_seal, arginfo_openssl_seal) 441 PHP_FE(openssl_open, arginfo_openssl_open) 442 443#if OPENSSL_VERSION_NUMBER >= 0x10000000L 444 PHP_FE(openssl_pbkdf2, arginfo_openssl_pbkdf2) 445#endif 446 447/* for S/MIME handling */ 448 PHP_FE(openssl_pkcs7_verify, arginfo_openssl_pkcs7_verify) 449 PHP_FE(openssl_pkcs7_decrypt, arginfo_openssl_pkcs7_decrypt) 450 PHP_FE(openssl_pkcs7_sign, arginfo_openssl_pkcs7_sign) 451 PHP_FE(openssl_pkcs7_encrypt, arginfo_openssl_pkcs7_encrypt) 452 453 PHP_FE(openssl_private_encrypt, arginfo_openssl_private_encrypt) 454 PHP_FE(openssl_private_decrypt, arginfo_openssl_private_decrypt) 455 PHP_FE(openssl_public_encrypt, arginfo_openssl_public_encrypt) 456 PHP_FE(openssl_public_decrypt, arginfo_openssl_public_decrypt) 457 458 PHP_FE(openssl_get_md_methods, arginfo_openssl_get_md_methods) 459 PHP_FE(openssl_get_cipher_methods, arginfo_openssl_get_cipher_methods) 460 461 PHP_FE(openssl_dh_compute_key, arginfo_openssl_dh_compute_key) 462 463 PHP_FE(openssl_random_pseudo_bytes, arginfo_openssl_random_pseudo_bytes) 464 PHP_FE(openssl_error_string, arginfo_openssl_error_string) 465 PHP_FE_END 466}; 467/* }}} */ 468 469/* {{{ openssl_module_entry 470 */ 471zend_module_entry openssl_module_entry = { 472 STANDARD_MODULE_HEADER, 473 "openssl", 474 openssl_functions, 475 PHP_MINIT(openssl), 476 PHP_MSHUTDOWN(openssl), 477 NULL, 478 NULL, 479 PHP_MINFO(openssl), 480 NO_VERSION_YET, 481 STANDARD_MODULE_PROPERTIES 482}; 483/* }}} */ 484 485#ifdef COMPILE_DL_OPENSSL 486ZEND_GET_MODULE(openssl) 487#endif 488 489static int le_key; 490static int le_x509; 491static int le_csr; 492static int ssl_stream_data_index; 493 494int php_openssl_get_x509_list_id(void) /* {{{ */ 495{ 496 return le_x509; 497} 498/* }}} */ 499 500/* {{{ resource destructors */ 501static void php_pkey_free(zend_rsrc_list_entry *rsrc TSRMLS_DC) 502{ 503 EVP_PKEY *pkey = (EVP_PKEY *)rsrc->ptr; 504 505 assert(pkey != NULL); 506 507 EVP_PKEY_free(pkey); 508} 509 510static void php_x509_free(zend_rsrc_list_entry *rsrc TSRMLS_DC) 511{ 512 X509 *x509 = (X509 *)rsrc->ptr; 513 X509_free(x509); 514} 515 516static void php_csr_free(zend_rsrc_list_entry *rsrc TSRMLS_DC) 517{ 518 X509_REQ * csr = (X509_REQ*)rsrc->ptr; 519 X509_REQ_free(csr); 520} 521/* }}} */ 522 523/* {{{ openssl open_basedir check */ 524inline static int php_openssl_open_base_dir_chk(char *filename TSRMLS_DC) 525{ 526 if (php_check_open_basedir(filename TSRMLS_CC)) { 527 return -1; 528 } 529 530 return 0; 531} 532/* }}} */ 533 534/* openssl -> PHP "bridging" */ 535/* true global; readonly after module startup */ 536static char default_ssl_conf_filename[MAXPATHLEN]; 537 538struct php_x509_request { /* {{{ */ 539#if OPENSSL_VERSION_NUMBER >= 0x10000002L 540 LHASH_OF(CONF_VALUE) * global_config; /* Global SSL config */ 541 LHASH_OF(CONF_VALUE) * req_config; /* SSL config for this request */ 542#else 543 LHASH * global_config; /* Global SSL config */ 544 LHASH * req_config; /* SSL config for this request */ 545#endif 546 const EVP_MD * md_alg; 547 const EVP_MD * digest; 548 char * section_name, 549 * config_filename, 550 * digest_name, 551 * extensions_section, 552 * request_extensions_section; 553 int priv_key_bits; 554 int priv_key_type; 555 556 int priv_key_encrypt; 557 558 EVP_PKEY * priv_key; 559 560 const EVP_CIPHER * priv_key_encrypt_cipher; 561}; 562/* }}} */ 563 564static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC); 565static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC); 566static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC); 567static X509_STORE * setup_verify(zval * calist TSRMLS_DC); 568static STACK_OF(X509) * load_all_certs_from_file(char *certfile); 569static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC); 570static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC); 571 572static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname TSRMLS_DC) /* {{{ */ 573{ 574 zval *subitem, *subentries; 575 int i, j = -1, last = -1, obj_cnt = 0; 576 char *sname; 577 int nid; 578 X509_NAME_ENTRY * ne; 579 ASN1_STRING * str = NULL; 580 ASN1_OBJECT * obj; 581 582 if (key != NULL) { 583 MAKE_STD_ZVAL(subitem); 584 array_init(subitem); 585 } else { 586 subitem = val; 587 } 588 589 for (i = 0; i < X509_NAME_entry_count(name); i++) { 590 unsigned char *to_add; 591 int to_add_len; 592 593 594 ne = X509_NAME_get_entry(name, i); 595 obj = X509_NAME_ENTRY_get_object(ne); 596 nid = OBJ_obj2nid(obj); 597 obj_cnt = 0; 598 599 if (shortname) { 600 sname = (char *) OBJ_nid2sn(nid); 601 } else { 602 sname = (char *) OBJ_nid2ln(nid); 603 } 604 605 MAKE_STD_ZVAL(subentries); 606 array_init(subentries); 607 608 last = -1; 609 for (;;) { 610 j = X509_NAME_get_index_by_OBJ(name, obj, last); 611 if (j < 0) { 612 if (last != -1) break; 613 } else { 614 obj_cnt++; 615 ne = X509_NAME_get_entry(name, j); 616 str = X509_NAME_ENTRY_get_data(ne); 617 if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) { 618 to_add_len = ASN1_STRING_to_UTF8(&to_add, str); 619 if (to_add_len != -1) { 620 add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1); 621 } 622 } else { 623 to_add = ASN1_STRING_data(str); 624 to_add_len = ASN1_STRING_length(str); 625 add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1); 626 } 627 } 628 last = j; 629 } 630 i = last; 631 632 if (obj_cnt > 1) { 633 add_assoc_zval_ex(subitem, sname, strlen(sname) + 1, subentries); 634 } else { 635 zval_dtor(subentries); 636 FREE_ZVAL(subentries); 637 if (obj_cnt && str && to_add_len > -1) { 638 add_assoc_stringl(subitem, sname, (char *)to_add, to_add_len, 1); 639 } 640 } 641 } 642 if (key != NULL) { 643 zend_hash_update(HASH_OF(val), key, strlen(key) + 1, (void *)&subitem, sizeof(subitem), NULL); 644 } 645} 646/* }}} */ 647 648static void add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) /* {{{ */ 649{ 650 add_assoc_stringl(val, key, (char *)str->data, str->length, 1); 651} 652/* }}} */ 653 654static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC) /* {{{ */ 655{ 656/* 657 This is how the time string is formatted: 658 659 snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100, 660 ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec); 661*/ 662 663 time_t ret; 664 struct tm thetime; 665 char * strbuf; 666 char * thestr; 667 long gmadjust = 0; 668 669 if (timestr->length < 13) { 670 php_error_docref(NULL TSRMLS_CC, E_WARNING, "extension author too lazy to parse %s correctly", timestr->data); 671 return (time_t)-1; 672 } 673 674 strbuf = estrdup((char *)timestr->data); 675 676 memset(&thetime, 0, sizeof(thetime)); 677 678 /* we work backwards so that we can use atoi more easily */ 679 680 thestr = strbuf + timestr->length - 3; 681 682 thetime.tm_sec = atoi(thestr); 683 *thestr = '\0'; 684 thestr -= 2; 685 thetime.tm_min = atoi(thestr); 686 *thestr = '\0'; 687 thestr -= 2; 688 thetime.tm_hour = atoi(thestr); 689 *thestr = '\0'; 690 thestr -= 2; 691 thetime.tm_mday = atoi(thestr); 692 *thestr = '\0'; 693 thestr -= 2; 694 thetime.tm_mon = atoi(thestr)-1; 695 *thestr = '\0'; 696 thestr -= 2; 697 thetime.tm_year = atoi(thestr); 698 699 if (thetime.tm_year < 68) { 700 thetime.tm_year += 100; 701 } 702 703 thetime.tm_isdst = -1; 704 ret = mktime(&thetime); 705 706#if HAVE_TM_GMTOFF 707 gmadjust = thetime.tm_gmtoff; 708#else 709 /* 710 ** If correcting for daylight savings time, we set the adjustment to 711 ** the value of timezone - 3600 seconds. Otherwise, we need to overcorrect and 712 ** set the adjustment to the main timezone + 3600 seconds. 713 */ 714 gmadjust = -(thetime.tm_isdst ? (long)timezone - 3600 : (long)timezone + 3600); 715#endif 716 ret += gmadjust; 717 718 efree(strbuf); 719 720 return ret; 721} 722/* }}} */ 723 724#if OPENSSL_VERSION_NUMBER >= 0x10000002L 725static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH_OF(CONF_VALUE) * config TSRMLS_DC) /* {{{ */ 726#else 727static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH * config TSRMLS_DC) 728#endif 729{ 730 X509V3_CTX ctx; 731 732 X509V3_set_ctx_test(&ctx); 733 X509V3_set_conf_lhash(&ctx, config); 734 if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL)) { 735 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading %s section %s of %s", 736 section_label, 737 section, 738 config_filename); 739 return FAILURE; 740 } 741 return SUCCESS; 742} 743/* }}} */ 744 745static int add_oid_section(struct php_x509_request * req TSRMLS_DC) /* {{{ */ 746{ 747 char * str; 748 STACK_OF(CONF_VALUE) * sktmp; 749 CONF_VALUE * cnf; 750 int i; 751 752 str = CONF_get_string(req->req_config, NULL, "oid_section"); 753 if (str == NULL) { 754 return SUCCESS; 755 } 756 sktmp = CONF_get_section(req->req_config, str); 757 if (sktmp == NULL) { 758 php_error_docref(NULL TSRMLS_CC, E_WARNING, "problem loading oid section %s", str); 759 return FAILURE; 760 } 761 for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) { 762 cnf = sk_CONF_VALUE_value(sktmp, i); 763 if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) { 764 php_error_docref(NULL TSRMLS_CC, E_WARNING, "problem creating object %s=%s", cnf->name, cnf->value); 765 return FAILURE; 766 } 767 } 768 return SUCCESS; 769} 770/* }}} */ 771 772#define PHP_SSL_REQ_INIT(req) memset(req, 0, sizeof(*req)) 773#define PHP_SSL_REQ_DISPOSE(req) php_openssl_dispose_config(req TSRMLS_CC) 774#define PHP_SSL_REQ_PARSE(req, zval) php_openssl_parse_config(req, zval TSRMLS_CC) 775 776#define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \ 777 req->config_filename, req->var, req->req_config TSRMLS_CC) == FAILURE) return FAILURE 778 779#define SET_OPTIONAL_STRING_ARG(key, varname, defval) \ 780 if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS) \ 781 varname = Z_STRVAL_PP(item); \ 782 else \ 783 varname = defval 784 785#define SET_OPTIONAL_LONG_ARG(key, varname, defval) \ 786 if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS) \ 787 varname = Z_LVAL_PP(item); \ 788 else \ 789 varname = defval 790 791static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo); 792 793 794static int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args TSRMLS_DC) /* {{{ */ 795{ 796 char * str; 797 zval ** item; 798 799 SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename); 800 SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req"); 801 req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL); 802 req->req_config = CONF_load(NULL, req->config_filename, NULL); 803 804 if (req->req_config == NULL) { 805 return FAILURE; 806 } 807 808 /* read in the oids */ 809 str = CONF_get_string(req->req_config, NULL, "oid_file"); 810 if (str && !php_openssl_open_base_dir_chk(str TSRMLS_CC)) { 811 BIO *oid_bio = BIO_new_file(str, "r"); 812 if (oid_bio) { 813 OBJ_create_objects(oid_bio); 814 BIO_free(oid_bio); 815 } 816 } 817 if (add_oid_section(req TSRMLS_CC) == FAILURE) { 818 return FAILURE; 819 } 820 SET_OPTIONAL_STRING_ARG("digest_alg", req->digest_name, 821 CONF_get_string(req->req_config, req->section_name, "default_md")); 822 SET_OPTIONAL_STRING_ARG("x509_extensions", req->extensions_section, 823 CONF_get_string(req->req_config, req->section_name, "x509_extensions")); 824 SET_OPTIONAL_STRING_ARG("req_extensions", req->request_extensions_section, 825 CONF_get_string(req->req_config, req->section_name, "req_extensions")); 826 SET_OPTIONAL_LONG_ARG("private_key_bits", req->priv_key_bits, 827 CONF_get_number(req->req_config, req->section_name, "default_bits")); 828 829 SET_OPTIONAL_LONG_ARG("private_key_type", req->priv_key_type, OPENSSL_KEYTYPE_DEFAULT); 830 831 if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), "encrypt_key", sizeof("encrypt_key"), (void**)&item) == SUCCESS) { 832 req->priv_key_encrypt = Z_BVAL_PP(item); 833 } else { 834 str = CONF_get_string(req->req_config, req->section_name, "encrypt_rsa_key"); 835 if (str == NULL) { 836 str = CONF_get_string(req->req_config, req->section_name, "encrypt_key"); 837 } 838 if (str && strcmp(str, "no") == 0) { 839 req->priv_key_encrypt = 0; 840 } else { 841 req->priv_key_encrypt = 1; 842 } 843 } 844 845 if (req->priv_key_encrypt && optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), "encrypt_key_cipher", sizeof("encrypt_key_cipher"), (void**)&item) == SUCCESS) { 846 long cipher_algo = Z_LVAL_PP(item); 847 const EVP_CIPHER* cipher = php_openssl_get_evp_cipher_from_algo(cipher_algo); 848 if (cipher == NULL) { 849 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm for private key."); 850 return FAILURE; 851 } else { 852 req->priv_key_encrypt_cipher = cipher; 853 } 854 } else { 855 req->priv_key_encrypt_cipher = NULL; 856 } 857 858 859 860 /* digest alg */ 861 if (req->digest_name == NULL) { 862 req->digest_name = CONF_get_string(req->req_config, req->section_name, "default_md"); 863 } 864 if (req->digest_name) { 865 req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name); 866 } 867 if (req->md_alg == NULL) { 868 req->md_alg = req->digest = EVP_md5(); 869 } 870 871 PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section); 872 873 /* set the string mask */ 874 str = CONF_get_string(req->req_config, req->section_name, "string_mask"); 875 if (str && !ASN1_STRING_set_default_mask_asc(str)) { 876 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid global string mask setting %s", str); 877 return FAILURE; 878 } 879 880 PHP_SSL_CONFIG_SYNTAX_CHECK(request_extensions_section); 881 882 return SUCCESS; 883} 884/* }}} */ 885 886static void php_openssl_dispose_config(struct php_x509_request * req TSRMLS_DC) /* {{{ */ 887{ 888 if (req->priv_key) { 889 EVP_PKEY_free(req->priv_key); 890 req->priv_key = NULL; 891 } 892 if (req->global_config) { 893 CONF_free(req->global_config); 894 req->global_config = NULL; 895 } 896 if (req->req_config) { 897 CONF_free(req->req_config); 898 req->req_config = NULL; 899 } 900} 901/* }}} */ 902 903static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded TSRMLS_DC) /* {{{ */ 904{ 905 char buffer[MAXPATHLEN]; 906 907 *egdsocket = 0; 908 *seeded = 0; 909 910 if (file == NULL) { 911 file = RAND_file_name(buffer, sizeof(buffer)); 912 } else if (RAND_egd(file) > 0) { 913 /* if the given filename is an EGD socket, don't 914 * write anything back to it */ 915 *egdsocket = 1; 916 return SUCCESS; 917 } 918 if (file == NULL || !RAND_load_file(file, -1)) { 919 if (RAND_status() == 0) { 920 php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to load random state; not enough random data!"); 921 return FAILURE; 922 } 923 return FAILURE; 924 } 925 *seeded = 1; 926 return SUCCESS; 927} 928/* }}} */ 929 930static int php_openssl_write_rand_file(const char * file, int egdsocket, int seeded) /* {{{ */ 931{ 932 char buffer[MAXPATHLEN]; 933 934 TSRMLS_FETCH(); 935 936 if (egdsocket || !seeded) { 937 /* if we did not manage to read the seed file, we should not write 938 * a low-entropy seed file back */ 939 return FAILURE; 940 } 941 if (file == NULL) { 942 file = RAND_file_name(buffer, sizeof(buffer)); 943 } 944 if (file == NULL || !RAND_write_file(file)) { 945 php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to write random state"); 946 return FAILURE; 947 } 948 return SUCCESS; 949} 950/* }}} */ 951 952static EVP_MD * php_openssl_get_evp_md_from_algo(long algo) { /* {{{ */ 953 EVP_MD *mdtype; 954 955 switch (algo) { 956 case OPENSSL_ALGO_SHA1: 957 mdtype = (EVP_MD *) EVP_sha1(); 958 break; 959 case OPENSSL_ALGO_MD5: 960 mdtype = (EVP_MD *) EVP_md5(); 961 break; 962 case OPENSSL_ALGO_MD4: 963 mdtype = (EVP_MD *) EVP_md4(); 964 break; 965#ifdef HAVE_OPENSSL_MD2_H 966 case OPENSSL_ALGO_MD2: 967 mdtype = (EVP_MD *) EVP_md2(); 968 break; 969#endif 970 case OPENSSL_ALGO_DSS1: 971 mdtype = (EVP_MD *) EVP_dss1(); 972 break; 973#if OPENSSL_VERSION_NUMBER >= 0x0090708fL 974 case OPENSSL_ALGO_SHA224: 975 mdtype = (EVP_MD *) EVP_sha224(); 976 break; 977 case OPENSSL_ALGO_SHA256: 978 mdtype = (EVP_MD *) EVP_sha256(); 979 break; 980 case OPENSSL_ALGO_SHA384: 981 mdtype = (EVP_MD *) EVP_sha384(); 982 break; 983 case OPENSSL_ALGO_SHA512: 984 mdtype = (EVP_MD *) EVP_sha512(); 985 break; 986 case OPENSSL_ALGO_RMD160: 987 mdtype = (EVP_MD *) EVP_ripemd160(); 988 break; 989#endif 990 default: 991 return NULL; 992 break; 993 } 994 return mdtype; 995} 996/* }}} */ 997 998static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo) { /* {{{ */ 999 switch (algo) { 1000#ifndef OPENSSL_NO_RC2 1001 case PHP_OPENSSL_CIPHER_RC2_40: 1002 return EVP_rc2_40_cbc(); 1003 break; 1004 case PHP_OPENSSL_CIPHER_RC2_64: 1005 return EVP_rc2_64_cbc(); 1006 break; 1007 case PHP_OPENSSL_CIPHER_RC2_128: 1008 return EVP_rc2_cbc(); 1009 break; 1010#endif 1011 1012#ifndef OPENSSL_NO_DES 1013 case PHP_OPENSSL_CIPHER_DES: 1014 return EVP_des_cbc(); 1015 break; 1016 case PHP_OPENSSL_CIPHER_3DES: 1017 return EVP_des_ede3_cbc(); 1018 break; 1019#endif 1020 1021#ifndef OPENSSL_NO_AES 1022 case PHP_OPENSSL_CIPHER_AES_128_CBC: 1023 return EVP_aes_128_cbc(); 1024 break; 1025 case PHP_OPENSSL_CIPHER_AES_192_CBC: 1026 return EVP_aes_192_cbc(); 1027 break; 1028 case PHP_OPENSSL_CIPHER_AES_256_CBC: 1029 return EVP_aes_256_cbc(); 1030 break; 1031#endif 1032 1033 1034 default: 1035 return NULL; 1036 break; 1037 } 1038} 1039/* }}} */ 1040 1041/* {{{ PHP_MINIT_FUNCTION 1042 */ 1043PHP_MINIT_FUNCTION(openssl) 1044{ 1045 char * config_filename; 1046 1047 le_key = zend_register_list_destructors_ex(php_pkey_free, NULL, "OpenSSL key", module_number); 1048 le_x509 = zend_register_list_destructors_ex(php_x509_free, NULL, "OpenSSL X.509", module_number); 1049 le_csr = zend_register_list_destructors_ex(php_csr_free, NULL, "OpenSSL X.509 CSR", module_number); 1050 1051 SSL_library_init(); 1052 OpenSSL_add_all_ciphers(); 1053 OpenSSL_add_all_digests(); 1054 OpenSSL_add_all_algorithms(); 1055 1056 SSL_load_error_strings(); 1057 1058 /* register a resource id number with OpenSSL so that we can map SSL -> stream structures in 1059 * OpenSSL callbacks */ 1060 ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL); 1061 1062 REGISTER_STRING_CONSTANT("OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT, CONST_CS|CONST_PERSISTENT); 1063 REGISTER_LONG_CONSTANT("OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER, CONST_CS|CONST_PERSISTENT); 1064 1065 /* purposes for cert purpose checking */ 1066 REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_CLIENT", X509_PURPOSE_SSL_CLIENT, CONST_CS|CONST_PERSISTENT); 1067 REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_SERVER", X509_PURPOSE_SSL_SERVER, CONST_CS|CONST_PERSISTENT); 1068 REGISTER_LONG_CONSTANT("X509_PURPOSE_NS_SSL_SERVER", X509_PURPOSE_NS_SSL_SERVER, CONST_CS|CONST_PERSISTENT); 1069 REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_SIGN", X509_PURPOSE_SMIME_SIGN, CONST_CS|CONST_PERSISTENT); 1070 REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_ENCRYPT", X509_PURPOSE_SMIME_ENCRYPT, CONST_CS|CONST_PERSISTENT); 1071 REGISTER_LONG_CONSTANT("X509_PURPOSE_CRL_SIGN", X509_PURPOSE_CRL_SIGN, CONST_CS|CONST_PERSISTENT); 1072#ifdef X509_PURPOSE_ANY 1073 REGISTER_LONG_CONSTANT("X509_PURPOSE_ANY", X509_PURPOSE_ANY, CONST_CS|CONST_PERSISTENT); 1074#endif 1075 1076 /* signature algorithm constants */ 1077 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA1", OPENSSL_ALGO_SHA1, CONST_CS|CONST_PERSISTENT); 1078 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD5", OPENSSL_ALGO_MD5, CONST_CS|CONST_PERSISTENT); 1079 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD4", OPENSSL_ALGO_MD4, CONST_CS|CONST_PERSISTENT); 1080#ifdef HAVE_OPENSSL_MD2_H 1081 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD2", OPENSSL_ALGO_MD2, CONST_CS|CONST_PERSISTENT); 1082#endif 1083 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT); 1084#if OPENSSL_VERSION_NUMBER >= 0x0090708fL 1085 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA224", OPENSSL_ALGO_SHA224, CONST_CS|CONST_PERSISTENT); 1086 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA256", OPENSSL_ALGO_SHA256, CONST_CS|CONST_PERSISTENT); 1087 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA384", OPENSSL_ALGO_SHA384, CONST_CS|CONST_PERSISTENT); 1088 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA512", OPENSSL_ALGO_SHA512, CONST_CS|CONST_PERSISTENT); 1089 REGISTER_LONG_CONSTANT("OPENSSL_ALGO_RMD160", OPENSSL_ALGO_RMD160, CONST_CS|CONST_PERSISTENT); 1090#endif 1091 1092 /* flags for S/MIME */ 1093 REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT); 1094 REGISTER_LONG_CONSTANT("PKCS7_TEXT", PKCS7_TEXT, CONST_CS|CONST_PERSISTENT); 1095 REGISTER_LONG_CONSTANT("PKCS7_NOINTERN", PKCS7_NOINTERN, CONST_CS|CONST_PERSISTENT); 1096 REGISTER_LONG_CONSTANT("PKCS7_NOVERIFY", PKCS7_NOVERIFY, CONST_CS|CONST_PERSISTENT); 1097 REGISTER_LONG_CONSTANT("PKCS7_NOCHAIN", PKCS7_NOCHAIN, CONST_CS|CONST_PERSISTENT); 1098 REGISTER_LONG_CONSTANT("PKCS7_NOCERTS", PKCS7_NOCERTS, CONST_CS|CONST_PERSISTENT); 1099 REGISTER_LONG_CONSTANT("PKCS7_NOATTR", PKCS7_NOATTR, CONST_CS|CONST_PERSISTENT); 1100 REGISTER_LONG_CONSTANT("PKCS7_BINARY", PKCS7_BINARY, CONST_CS|CONST_PERSISTENT); 1101 REGISTER_LONG_CONSTANT("PKCS7_NOSIGS", PKCS7_NOSIGS, CONST_CS|CONST_PERSISTENT); 1102 1103 REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_PADDING", RSA_PKCS1_PADDING, CONST_CS|CONST_PERSISTENT); 1104 REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING", RSA_SSLV23_PADDING, CONST_CS|CONST_PERSISTENT); 1105 REGISTER_LONG_CONSTANT("OPENSSL_NO_PADDING", RSA_NO_PADDING, CONST_CS|CONST_PERSISTENT); 1106 REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING, CONST_CS|CONST_PERSISTENT); 1107 1108 /* Ciphers */ 1109#ifndef OPENSSL_NO_RC2 1110 REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_40", PHP_OPENSSL_CIPHER_RC2_40, CONST_CS|CONST_PERSISTENT); 1111 REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_128", PHP_OPENSSL_CIPHER_RC2_128, CONST_CS|CONST_PERSISTENT); 1112 REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_64", PHP_OPENSSL_CIPHER_RC2_64, CONST_CS|CONST_PERSISTENT); 1113#endif 1114#ifndef OPENSSL_NO_DES 1115 REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_DES", PHP_OPENSSL_CIPHER_DES, CONST_CS|CONST_PERSISTENT); 1116 REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_3DES", PHP_OPENSSL_CIPHER_3DES, CONST_CS|CONST_PERSISTENT); 1117#endif 1118#ifndef OPENSSL_NO_AES 1119 REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_128_CBC", PHP_OPENSSL_CIPHER_AES_128_CBC, CONST_CS|CONST_PERSISTENT); 1120 REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_192_CBC", PHP_OPENSSL_CIPHER_AES_192_CBC, CONST_CS|CONST_PERSISTENT); 1121 REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_256_CBC", PHP_OPENSSL_CIPHER_AES_256_CBC, CONST_CS|CONST_PERSISTENT); 1122#endif 1123 1124 /* Values for key types */ 1125 REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_RSA", OPENSSL_KEYTYPE_RSA, CONST_CS|CONST_PERSISTENT); 1126#ifndef NO_DSA 1127 REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DSA", OPENSSL_KEYTYPE_DSA, CONST_CS|CONST_PERSISTENT); 1128#endif 1129 REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH", OPENSSL_KEYTYPE_DH, CONST_CS|CONST_PERSISTENT); 1130#ifdef EVP_PKEY_EC 1131 REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_EC", OPENSSL_KEYTYPE_EC, CONST_CS|CONST_PERSISTENT); 1132#endif 1133 1134 REGISTER_LONG_CONSTANT("OPENSSL_RAW_DATA", OPENSSL_RAW_DATA, CONST_CS|CONST_PERSISTENT); 1135 REGISTER_LONG_CONSTANT("OPENSSL_ZERO_PADDING", OPENSSL_ZERO_PADDING, CONST_CS|CONST_PERSISTENT); 1136 1137#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) 1138 /* SNI support included in OpenSSL >= 0.9.8j */ 1139 REGISTER_LONG_CONSTANT("OPENSSL_TLSEXT_SERVER_NAME", 1, CONST_CS|CONST_PERSISTENT); 1140#endif 1141 1142 /* Determine default SSL configuration file */ 1143 config_filename = getenv("OPENSSL_CONF"); 1144 if (config_filename == NULL) { 1145 config_filename = getenv("SSLEAY_CONF"); 1146 } 1147 1148 /* default to 'openssl.cnf' if no environment variable is set */ 1149 if (config_filename == NULL) { 1150 snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename), "%s/%s", 1151 X509_get_default_cert_area(), 1152 "openssl.cnf"); 1153 } else { 1154 strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename)); 1155 } 1156 1157 php_stream_xport_register("ssl", php_openssl_ssl_socket_factory TSRMLS_CC); 1158 php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory TSRMLS_CC); 1159#ifndef OPENSSL_NO_SSL2 1160 php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory TSRMLS_CC); 1161#endif 1162 php_stream_xport_register("tls", php_openssl_ssl_socket_factory TSRMLS_CC); 1163 1164 /* override the default tcp socket provider */ 1165 php_stream_xport_register("tcp", php_openssl_ssl_socket_factory TSRMLS_CC); 1166 1167 php_register_url_stream_wrapper("https", &php_stream_http_wrapper TSRMLS_CC); 1168 php_register_url_stream_wrapper("ftps", &php_stream_ftp_wrapper TSRMLS_CC); 1169 1170 return SUCCESS; 1171} 1172/* }}} */ 1173 1174/* {{{ PHP_MINFO_FUNCTION 1175 */ 1176PHP_MINFO_FUNCTION(openssl) 1177{ 1178 php_info_print_table_start(); 1179 php_info_print_table_row(2, "OpenSSL support", "enabled"); 1180 php_info_print_table_row(2, "OpenSSL Library Version", SSLeay_version(SSLEAY_VERSION)); 1181 php_info_print_table_row(2, "OpenSSL Header Version", OPENSSL_VERSION_TEXT); 1182 php_info_print_table_end(); 1183} 1184/* }}} */ 1185 1186/* {{{ PHP_MSHUTDOWN_FUNCTION 1187 */ 1188PHP_MSHUTDOWN_FUNCTION(openssl) 1189{ 1190 EVP_cleanup(); 1191 1192 php_unregister_url_stream_wrapper("https" TSRMLS_CC); 1193 php_unregister_url_stream_wrapper("ftps" TSRMLS_CC); 1194 1195 php_stream_xport_unregister("ssl" TSRMLS_CC); 1196#ifndef OPENSSL_NO_SSL2 1197 php_stream_xport_unregister("sslv2" TSRMLS_CC); 1198#endif 1199 php_stream_xport_unregister("sslv3" TSRMLS_CC); 1200 php_stream_xport_unregister("tls" TSRMLS_CC); 1201 1202 /* reinstate the default tcp handler */ 1203 php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC); 1204 1205 return SUCCESS; 1206} 1207/* }}} */ 1208 1209/* {{{ x509 cert functions */ 1210 1211/* {{{ php_openssl_x509_from_zval 1212 Given a zval, coerce it into an X509 object. 1213 The zval can be: 1214 . X509 resource created using openssl_read_x509() 1215 . if it starts with file:// then it will be interpreted as the path to that cert 1216 . it will be interpreted as the cert data 1217 If you supply makeresource, the result will be registered as an x509 resource and 1218 it's value returned in makeresource. 1219*/ 1220static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC) 1221{ 1222 X509 *cert = NULL; 1223 1224 if (resourceval) { 1225 *resourceval = -1; 1226 } 1227 if (Z_TYPE_PP(val) == IS_RESOURCE) { 1228 /* is it an x509 resource ? */ 1229 void * what; 1230 int type; 1231 1232 what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509", &type, 1, le_x509); 1233 if (!what) { 1234 return NULL; 1235 } 1236 /* this is so callers can decide if they should free the X509 */ 1237 if (resourceval) { 1238 *resourceval = Z_LVAL_PP(val); 1239 } 1240 if (type == le_x509) { 1241 return (X509*)what; 1242 } 1243 /* other types could be used here - eg: file pointers and read in the data from them */ 1244 1245 return NULL; 1246 } 1247 1248 if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) { 1249 return NULL; 1250 } 1251 1252 /* force it to be a string and check if it refers to a file */ 1253 convert_to_string_ex(val); 1254 1255 if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) { 1256 /* read cert from the named file */ 1257 BIO *in; 1258 1259 if (php_openssl_open_base_dir_chk(Z_STRVAL_PP(val) + (sizeof("file://") - 1) TSRMLS_CC)) { 1260 return NULL; 1261 } 1262 1263 in = BIO_new_file(Z_STRVAL_PP(val) + (sizeof("file://") - 1), "r"); 1264 if (in == NULL) { 1265 return NULL; 1266 } 1267 cert = PEM_read_bio_X509(in, NULL, NULL, NULL); 1268 BIO_free(in); 1269 } else { 1270 BIO *in; 1271 1272 in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); 1273 if (in == NULL) { 1274 return NULL; 1275 } 1276#ifdef TYPEDEF_D2I_OF 1277 cert = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL); 1278#else 1279 cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL); 1280#endif 1281 BIO_free(in); 1282 } 1283 1284 if (cert && makeresource && resourceval) { 1285 *resourceval = zend_list_insert(cert, le_x509 TSRMLS_CC); 1286 } 1287 return cert; 1288} 1289 1290/* }}} */ 1291 1292/* {{{ proto bool openssl_x509_export_to_file(mixed x509, string outfilename [, bool notext = true]) 1293 Exports a CERT to file or a var */ 1294PHP_FUNCTION(openssl_x509_export_to_file) 1295{ 1296 X509 * cert; 1297 zval ** zcert; 1298 zend_bool notext = 1; 1299 BIO * bio_out; 1300 long certresource; 1301 char * filename; 1302 int filename_len; 1303 1304 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zp|b", &zcert, &filename, &filename_len, ¬ext) == FAILURE) { 1305 return; 1306 } 1307 RETVAL_FALSE; 1308 1309 cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); 1310 if (cert == NULL) { 1311 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1"); 1312 return; 1313 } 1314 1315 if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { 1316 return; 1317 } 1318 1319 bio_out = BIO_new_file(filename, "w"); 1320 if (bio_out) { 1321 if (!notext) { 1322 X509_print(bio_out, cert); 1323 } 1324 PEM_write_bio_X509(bio_out, cert); 1325 1326 RETVAL_TRUE; 1327 } else { 1328 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename); 1329 } 1330 if (certresource == -1 && cert) { 1331 X509_free(cert); 1332 } 1333 BIO_free(bio_out); 1334} 1335/* }}} */ 1336 1337/* {{{ proto bool openssl_x509_export(mixed x509, string &out [, bool notext = true]) 1338 Exports a CERT to file or a var */ 1339PHP_FUNCTION(openssl_x509_export) 1340{ 1341 X509 * cert; 1342 zval ** zcert, *zout; 1343 zend_bool notext = 1; 1344 BIO * bio_out; 1345 long certresource; 1346 1347 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|b", &zcert, &zout, ¬ext) == FAILURE) { 1348 return; 1349 } 1350 RETVAL_FALSE; 1351 1352 cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); 1353 if (cert == NULL) { 1354 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1"); 1355 return; 1356 } 1357 1358 bio_out = BIO_new(BIO_s_mem()); 1359 if (!notext) { 1360 X509_print(bio_out, cert); 1361 } 1362 if (PEM_write_bio_X509(bio_out, cert)) { 1363 BUF_MEM *bio_buf; 1364 1365 zval_dtor(zout); 1366 BIO_get_mem_ptr(bio_out, &bio_buf); 1367 ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1); 1368 1369 RETVAL_TRUE; 1370 } 1371 1372 if (certresource == -1 && cert) { 1373 X509_free(cert); 1374 } 1375 BIO_free(bio_out); 1376} 1377/* }}} */ 1378 1379/* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key) 1380 Checks if a private key corresponds to a CERT */ 1381PHP_FUNCTION(openssl_x509_check_private_key) 1382{ 1383 zval ** zcert, **zkey; 1384 X509 * cert = NULL; 1385 EVP_PKEY * key = NULL; 1386 long certresource = -1, keyresource = -1; 1387 1388 RETVAL_FALSE; 1389 1390 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ", &zcert, &zkey) == FAILURE) { 1391 return; 1392 } 1393 cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); 1394 if (cert == NULL) { 1395 RETURN_FALSE; 1396 } 1397 key = php_openssl_evp_from_zval(zkey, 0, "", 1, &keyresource TSRMLS_CC); 1398 if (key) { 1399 RETVAL_BOOL(X509_check_private_key(cert, key)); 1400 } 1401 1402 if (keyresource == -1 && key) { 1403 EVP_PKEY_free(key); 1404 } 1405 if (certresource == -1 && cert) { 1406 X509_free(cert); 1407 } 1408} 1409/* }}} */ 1410 1411/* {{{ proto array openssl_x509_parse(mixed x509 [, bool shortnames=true]) 1412 Returns an array of the fields/values of the CERT */ 1413PHP_FUNCTION(openssl_x509_parse) 1414{ 1415 zval ** zcert; 1416 X509 * cert = NULL; 1417 long certresource = -1; 1418 int i; 1419 zend_bool useshortnames = 1; 1420 char * tmpstr; 1421 zval * subitem; 1422 X509_EXTENSION *extension; 1423 char *extname; 1424 BIO *bio_out; 1425 BUF_MEM *bio_buf; 1426 char buf[256]; 1427 1428 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcert, &useshortnames) == FAILURE) { 1429 return; 1430 } 1431 cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); 1432 if (cert == NULL) { 1433 RETURN_FALSE; 1434 } 1435 array_init(return_value); 1436 1437 if (cert->name) { 1438 add_assoc_string(return_value, "name", cert->name, 1); 1439 } 1440/* add_assoc_bool(return_value, "valid", cert->valid); */ 1441 1442 add_assoc_name_entry(return_value, "subject", X509_get_subject_name(cert), useshortnames TSRMLS_CC); 1443 /* hash as used in CA directories to lookup cert by subject name */ 1444 { 1445 char buf[32]; 1446 snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert)); 1447 add_assoc_string(return_value, "hash", buf, 1); 1448 } 1449 1450 add_assoc_name_entry(return_value, "issuer", X509_get_issuer_name(cert), useshortnames TSRMLS_CC); 1451 add_assoc_long(return_value, "version", X509_get_version(cert)); 1452 1453 add_assoc_string(return_value, "serialNumber", i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(cert)), 1); 1454 1455 add_assoc_asn1_string(return_value, "validFrom", X509_get_notBefore(cert)); 1456 add_assoc_asn1_string(return_value, "validTo", X509_get_notAfter(cert)); 1457 1458 add_assoc_long(return_value, "validFrom_time_t", asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC)); 1459 add_assoc_long(return_value, "validTo_time_t", asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC)); 1460 1461 tmpstr = (char *)X509_alias_get0(cert, NULL); 1462 if (tmpstr) { 1463 add_assoc_string(return_value, "alias", tmpstr, 1); 1464 } 1465/* 1466 add_assoc_long(return_value, "signaturetypeLONG", X509_get_signature_type(cert)); 1467 add_assoc_string(return_value, "signaturetype", OBJ_nid2sn(X509_get_signature_type(cert)), 1); 1468 add_assoc_string(return_value, "signaturetypeLN", OBJ_nid2ln(X509_get_signature_type(cert)), 1); 1469*/ 1470 MAKE_STD_ZVAL(subitem); 1471 array_init(subitem); 1472 1473 /* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines 1474 in x509v3.h */ 1475 for (i = 0; i < X509_PURPOSE_get_count(); i++) { 1476 int id, purpset; 1477 char * pname; 1478 X509_PURPOSE * purp; 1479 zval * subsub; 1480 1481 MAKE_STD_ZVAL(subsub); 1482 array_init(subsub); 1483 1484 purp = X509_PURPOSE_get0(i); 1485 id = X509_PURPOSE_get_id(purp); 1486 1487 purpset = X509_check_purpose(cert, id, 0); 1488 add_index_bool(subsub, 0, purpset); 1489 1490 purpset = X509_check_purpose(cert, id, 1); 1491 add_index_bool(subsub, 1, purpset); 1492 1493 pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp); 1494 add_index_string(subsub, 2, pname, 1); 1495 1496 /* NOTE: if purpset > 1 then it's a warning - we should mention it ? */ 1497 1498 add_index_zval(subitem, id, subsub); 1499 } 1500 add_assoc_zval(return_value, "purposes", subitem); 1501 1502 MAKE_STD_ZVAL(subitem); 1503 array_init(subitem); 1504 1505 1506 for (i = 0; i < X509_get_ext_count(cert); i++) { 1507 extension = X509_get_ext(cert, i); 1508 if (OBJ_obj2nid(X509_EXTENSION_get_object(extension)) != NID_undef) { 1509 extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension))); 1510 } else { 1511 OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1); 1512 extname = buf; 1513 } 1514 bio_out = BIO_new(BIO_s_mem()); 1515 if (X509V3_EXT_print(bio_out, extension, 0, 0)) { 1516 BIO_get_mem_ptr(bio_out, &bio_buf); 1517 add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1); 1518 } else { 1519 add_assoc_asn1_string(subitem, extname, X509_EXTENSION_get_data(extension)); 1520 } 1521 BIO_free(bio_out); 1522 } 1523 add_assoc_zval(return_value, "extensions", subitem); 1524 1525 if (certresource == -1 && cert) { 1526 X509_free(cert); 1527 } 1528} 1529/* }}} */ 1530 1531/* {{{ load_all_certs_from_file */ 1532static STACK_OF(X509) * load_all_certs_from_file(char *certfile) 1533{ 1534 STACK_OF(X509_INFO) *sk=NULL; 1535 STACK_OF(X509) *stack=NULL, *ret=NULL; 1536 BIO *in=NULL; 1537 X509_INFO *xi; 1538 TSRMLS_FETCH(); 1539 1540 if(!(stack = sk_X509_new_null())) { 1541 php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure"); 1542 goto end; 1543 } 1544 1545 if (php_openssl_open_base_dir_chk(certfile TSRMLS_CC)) { 1546 sk_X509_free(stack); 1547 goto end; 1548 } 1549 1550 if(!(in=BIO_new_file(certfile, "r"))) { 1551 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening the file, %s", certfile); 1552 sk_X509_free(stack); 1553 goto end; 1554 } 1555 1556 /* This loads from a file, a stack of x509/crl/pkey sets */ 1557 if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) { 1558 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error reading the file, %s", certfile); 1559 sk_X509_free(stack); 1560 goto end; 1561 } 1562 1563 /* scan over it and pull out the certs */ 1564 while (sk_X509_INFO_num(sk)) { 1565 xi=sk_X509_INFO_shift(sk); 1566 if (xi->x509 != NULL) { 1567 sk_X509_push(stack,xi->x509); 1568 xi->x509=NULL; 1569 } 1570 X509_INFO_free(xi); 1571 } 1572 if(!sk_X509_num(stack)) { 1573 php_error_docref(NULL TSRMLS_CC, E_WARNING, "no certificates in file, %s", certfile); 1574 sk_X509_free(stack); 1575 goto end; 1576 } 1577 ret=stack; 1578end: 1579 BIO_free(in); 1580 sk_X509_INFO_free(sk); 1581 1582 return ret; 1583} 1584/* }}} */ 1585 1586/* {{{ check_cert */ 1587static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose) 1588{ 1589 int ret=0; 1590 X509_STORE_CTX *csc; 1591 TSRMLS_FETCH(); 1592 1593 csc = X509_STORE_CTX_new(); 1594 if (csc == NULL) { 1595 php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure"); 1596 return 0; 1597 } 1598 X509_STORE_CTX_init(csc, ctx, x, untrustedchain); 1599 if(purpose >= 0) { 1600 X509_STORE_CTX_set_purpose(csc, purpose); 1601 } 1602 ret = X509_verify_cert(csc); 1603 X509_STORE_CTX_free(csc); 1604 1605 return ret; 1606} 1607/* }}} */ 1608 1609/* {{{ proto int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo [, string untrustedfile]) 1610 Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs */ 1611PHP_FUNCTION(openssl_x509_checkpurpose) 1612{ 1613 zval ** zcert, * zcainfo = NULL; 1614 X509_STORE * cainfo = NULL; 1615 X509 * cert = NULL; 1616 long certresource = -1; 1617 STACK_OF(X509) * untrustedchain = NULL; 1618 long purpose; 1619 char * untrusted = NULL; 1620 int untrusted_len = 0, ret; 1621 1622 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zl|a!s", &zcert, &purpose, &zcainfo, &untrusted, &untrusted_len) == FAILURE) { 1623 return; 1624 } 1625 1626 RETVAL_LONG(-1); 1627 1628 if (untrusted) { 1629 untrustedchain = load_all_certs_from_file(untrusted); 1630 if (untrustedchain == NULL) { 1631 goto clean_exit; 1632 } 1633 } 1634 1635 cainfo = setup_verify(zcainfo TSRMLS_CC); 1636 if (cainfo == NULL) { 1637 goto clean_exit; 1638 } 1639 cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); 1640 if (cert == NULL) { 1641 goto clean_exit; 1642 } 1643 1644 ret = check_cert(cainfo, cert, untrustedchain, purpose); 1645 if (ret != 0 && ret != 1) { 1646 RETVAL_LONG(ret); 1647 } else { 1648 RETVAL_BOOL(ret); 1649 } 1650 1651clean_exit: 1652 if (certresource == 1 && cert) { 1653 X509_free(cert); 1654 } 1655 if (cainfo) { 1656 X509_STORE_free(cainfo); 1657 } 1658 if (untrustedchain) { 1659 sk_X509_pop_free(untrustedchain, X509_free); 1660 } 1661} 1662/* }}} */ 1663 1664/* {{{ setup_verify 1665 * calist is an array containing file and directory names. create a 1666 * certificate store and add those certs to it for use in verification. 1667*/ 1668static X509_STORE * setup_verify(zval * calist TSRMLS_DC) 1669{ 1670 X509_STORE *store; 1671 X509_LOOKUP * dir_lookup, * file_lookup; 1672 HashPosition pos; 1673 int ndirs = 0, nfiles = 0; 1674 1675 store = X509_STORE_new(); 1676 1677 if (store == NULL) { 1678 return NULL; 1679 } 1680 1681 if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) { 1682 zend_hash_internal_pointer_reset_ex(HASH_OF(calist), &pos); 1683 for (;; zend_hash_move_forward_ex(HASH_OF(calist), &pos)) { 1684 zval ** item; 1685 struct stat sb; 1686 1687 if (zend_hash_get_current_data_ex(HASH_OF(calist), (void**)&item, &pos) == FAILURE) { 1688 break; 1689 } 1690 convert_to_string_ex(item); 1691 1692 if (VCWD_STAT(Z_STRVAL_PP(item), &sb) == -1) { 1693 php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to stat %s", Z_STRVAL_PP(item)); 1694 continue; 1695 } 1696 1697 if ((sb.st_mode & S_IFREG) == S_IFREG) { 1698 file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); 1699 if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) { 1700 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading file %s", Z_STRVAL_PP(item)); 1701 } else { 1702 nfiles++; 1703 } 1704 file_lookup = NULL; 1705 } else { 1706 dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); 1707 if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) { 1708 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading directory %s", Z_STRVAL_PP(item)); 1709 } else { 1710 ndirs++; 1711 } 1712 dir_lookup = NULL; 1713 } 1714 } 1715 } 1716 if (nfiles == 0) { 1717 file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); 1718 if (file_lookup) { 1719 X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT); 1720 } 1721 } 1722 if (ndirs == 0) { 1723 dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); 1724 if (dir_lookup) { 1725 X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT); 1726 } 1727 } 1728 return store; 1729} 1730/* }}} */ 1731 1732/* {{{ proto resource openssl_x509_read(mixed cert) 1733 Reads X.509 certificates */ 1734PHP_FUNCTION(openssl_x509_read) 1735{ 1736 zval **cert; 1737 X509 *x509; 1738 1739 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) { 1740 return; 1741 } 1742 Z_TYPE_P(return_value) = IS_RESOURCE; 1743 x509 = php_openssl_x509_from_zval(cert, 1, &Z_LVAL_P(return_value) TSRMLS_CC); 1744 1745 if (x509 == NULL) { 1746 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied parameter cannot be coerced into an X509 certificate!"); 1747 RETURN_FALSE; 1748 } 1749} 1750/* }}} */ 1751 1752/* {{{ proto void openssl_x509_free(resource x509) 1753 Frees X.509 certificates */ 1754PHP_FUNCTION(openssl_x509_free) 1755{ 1756 zval *x509; 1757 X509 *cert; 1758 1759 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &x509) == FAILURE) { 1760 return; 1761 } 1762 ZEND_FETCH_RESOURCE(cert, X509 *, &x509, -1, "OpenSSL X.509", le_x509); 1763 zend_list_delete(Z_LVAL_P(x509)); 1764} 1765/* }}} */ 1766 1767/* }}} */ 1768 1769/* Pop all X509 from Stack and free them, free the stack afterwards */ 1770static void php_sk_X509_free(STACK_OF(X509) * sk) /* {{{ */ 1771{ 1772 for (;;) { 1773 X509* x = sk_X509_pop(sk); 1774 if (!x) break; 1775 X509_free(x); 1776 } 1777 sk_X509_free(sk); 1778} 1779/* }}} */ 1780 1781static STACK_OF(X509) * php_array_to_X509_sk(zval ** zcerts TSRMLS_DC) /* {{{ */ 1782{ 1783 HashPosition hpos; 1784 zval ** zcertval; 1785 STACK_OF(X509) * sk = NULL; 1786 X509 * cert; 1787 long certresource; 1788 1789 sk = sk_X509_new_null(); 1790 1791 /* get certs */ 1792 if (Z_TYPE_PP(zcerts) == IS_ARRAY) { 1793 zend_hash_internal_pointer_reset_ex(HASH_OF(*zcerts), &hpos); 1794 while(zend_hash_get_current_data_ex(HASH_OF(*zcerts), (void**)&zcertval, &hpos) == SUCCESS) { 1795 1796 cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC); 1797 if (cert == NULL) { 1798 goto clean_exit; 1799 } 1800 1801 if (certresource != -1) { 1802 cert = X509_dup(cert); 1803 1804 if (cert == NULL) { 1805 goto clean_exit; 1806 } 1807 1808 } 1809 sk_X509_push(sk, cert); 1810 1811 zend_hash_move_forward_ex(HASH_OF(*zcerts), &hpos); 1812 } 1813 } else { 1814 /* a single certificate */ 1815 cert = php_openssl_x509_from_zval(zcerts, 0, &certresource TSRMLS_CC); 1816 1817 if (cert == NULL) { 1818 goto clean_exit; 1819 } 1820 1821 if (certresource != -1) { 1822 cert = X509_dup(cert); 1823 if (cert == NULL) { 1824 goto clean_exit; 1825 } 1826 } 1827 sk_X509_push(sk, cert); 1828 } 1829 1830 clean_exit: 1831 return sk; 1832} 1833/* }}} */ 1834 1835/* {{{ proto bool openssl_pkcs12_export_to_file(mixed x509, string filename, mixed priv_key, string pass[, array args]) 1836 Creates and exports a PKCS to file */ 1837PHP_FUNCTION(openssl_pkcs12_export_to_file) 1838{ 1839 X509 * cert = NULL; 1840 BIO * bio_out = NULL; 1841 PKCS12 * p12 = NULL; 1842 char * filename; 1843 char * friendly_name = NULL; 1844 int filename_len; 1845 char * pass; 1846 int pass_len; 1847 zval **zcert = NULL, *zpkey = NULL, *args = NULL; 1848 EVP_PKEY *priv_key = NULL; 1849 long certresource, keyresource; 1850 zval ** item; 1851 STACK_OF(X509) *ca = NULL; 1852 1853 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zpzs|a", &zcert, &filename, &filename_len, &zpkey, &pass, &pass_len, &args) == FAILURE) 1854 return; 1855 1856 RETVAL_FALSE; 1857 1858 cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); 1859 if (cert == NULL) { 1860 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1"); 1861 return; 1862 } 1863 priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC); 1864 if (priv_key == NULL) { 1865 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3"); 1866 goto cleanup; 1867 } 1868 if (cert && !X509_check_private_key(cert, priv_key)) { 1869 php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert"); 1870 goto cleanup; 1871 } 1872 if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { 1873 goto cleanup; 1874 } 1875 1876 /* parse extra config from args array, promote this to an extra function */ 1877 if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS) 1878 friendly_name = Z_STRVAL_PP(item); 1879 /* certpbe (default RC2-40) 1880 keypbe (default 3DES) 1881 friendly_caname 1882 */ 1883 1884 if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS) 1885 ca = php_array_to_X509_sk(item TSRMLS_CC); 1886 /* end parse extra config */ 1887 1888 /*PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ca, 1889 int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/ 1890 1891 p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0); 1892 1893 bio_out = BIO_new_file(filename, "w"); 1894 if (bio_out) { 1895 1896 i2d_PKCS12_bio(bio_out, p12); 1897 1898 RETVAL_TRUE; 1899 } else { 1900 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename); 1901 } 1902 1903 BIO_free(bio_out); 1904 PKCS12_free(p12); 1905 php_sk_X509_free(ca); 1906 1907cleanup: 1908 1909 if (keyresource == -1 && priv_key) { 1910 EVP_PKEY_free(priv_key); 1911 } 1912 if (certresource == -1 && cert) { 1913 X509_free(cert); 1914 } 1915} 1916/* }}} */ 1917 1918/* {{{ proto bool openssl_pkcs12_export(mixed x509, string &out, mixed priv_key, string pass[, array args]) 1919 Creates and exports a PKCS12 to a var */ 1920PHP_FUNCTION(openssl_pkcs12_export) 1921{ 1922 X509 * cert = NULL; 1923 BIO * bio_out; 1924 PKCS12 * p12 = NULL; 1925 zval * zcert = NULL, *zout = NULL, *zpkey, *args = NULL; 1926 EVP_PKEY *priv_key = NULL; 1927 long certresource, keyresource; 1928 char * pass; 1929 int pass_len; 1930 char * friendly_name = NULL; 1931 zval ** item; 1932 STACK_OF(X509) *ca = NULL; 1933 1934 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzzs|a", &zcert, &zout, &zpkey, &pass, &pass_len, &args) == FAILURE) 1935 return; 1936 1937 RETVAL_FALSE; 1938 1939 cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC); 1940 if (cert == NULL) { 1941 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1"); 1942 return; 1943 } 1944 priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC); 1945 if (priv_key == NULL) { 1946 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3"); 1947 goto cleanup; 1948 } 1949 if (cert && !X509_check_private_key(cert, priv_key)) { 1950 php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert"); 1951 goto cleanup; 1952 } 1953 1954 /* parse extra config from args array, promote this to an extra function */ 1955 if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS) 1956 friendly_name = Z_STRVAL_PP(item); 1957 1958 if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS) 1959 ca = php_array_to_X509_sk(item TSRMLS_CC); 1960 /* end parse extra config */ 1961 1962 p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0); 1963 1964 bio_out = BIO_new(BIO_s_mem()); 1965 if (i2d_PKCS12_bio(bio_out, p12)) { 1966 BUF_MEM *bio_buf; 1967 1968 zval_dtor(zout); 1969 BIO_get_mem_ptr(bio_out, &bio_buf); 1970 ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1); 1971 1972 RETVAL_TRUE; 1973 } 1974 1975 BIO_free(bio_out); 1976 PKCS12_free(p12); 1977 php_sk_X509_free(ca); 1978 1979cleanup: 1980 1981 if (keyresource == -1 && priv_key) { 1982 EVP_PKEY_free(priv_key); 1983 } 1984 if (certresource == -1 && cert) { 1985 X509_free(cert); 1986 } 1987} 1988/* }}} */ 1989 1990/* {{{ proto bool openssl_pkcs12_read(string PKCS12, array &certs, string pass) 1991 Parses a PKCS12 to an array */ 1992PHP_FUNCTION(openssl_pkcs12_read) 1993{ 1994 zval *zout = NULL, *zextracerts, *zcert, *zpkey; 1995 char *pass, *zp12; 1996 int pass_len, zp12_len; 1997 PKCS12 * p12 = NULL; 1998 EVP_PKEY * pkey = NULL; 1999 X509 * cert = NULL; 2000 STACK_OF(X509) * ca = NULL; 2001 BIO * bio_in = NULL; 2002 int i; 2003 2004 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szs", &zp12, &zp12_len, &zout, &pass, &pass_len) == FAILURE) 2005 return; 2006 2007 RETVAL_FALSE; 2008 2009 bio_in = BIO_new(BIO_s_mem()); 2010 2011 if(!BIO_write(bio_in, zp12, zp12_len)) 2012 goto cleanup; 2013 2014 if(d2i_PKCS12_bio(bio_in, &p12)) { 2015 if(PKCS12_parse(p12, pass, &pkey, &cert, &ca)) { 2016 BIO * bio_out; 2017 2018 zval_dtor(zout); 2019 array_init(zout); 2020 2021 bio_out = BIO_new(BIO_s_mem()); 2022 if (PEM_write_bio_X509(bio_out, cert)) { 2023 BUF_MEM *bio_buf; 2024 BIO_get_mem_ptr(bio_out, &bio_buf); 2025 MAKE_STD_ZVAL(zcert); 2026 ZVAL_STRINGL(zcert, bio_buf->data, bio_buf->length, 1); 2027 add_assoc_zval(zout, "cert", zcert); 2028 } 2029 BIO_free(bio_out); 2030 2031 bio_out = BIO_new(BIO_s_mem()); 2032 if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) { 2033 BUF_MEM *bio_buf; 2034 BIO_get_mem_ptr(bio_out, &bio_buf); 2035 MAKE_STD_ZVAL(zpkey); 2036 ZVAL_STRINGL(zpkey, bio_buf->data, bio_buf->length, 1); 2037 add_assoc_zval(zout, "pkey", zpkey); 2038 } 2039 BIO_free(bio_out); 2040 2041 MAKE_STD_ZVAL(zextracerts); 2042 array_init(zextracerts); 2043 2044 for (i=0;;i++) { 2045 zval * zextracert; 2046 X509* aCA = sk_X509_pop(ca); 2047 if (!aCA) break; 2048 2049 bio_out = BIO_new(BIO_s_mem()); 2050 if (PEM_write_bio_X509(bio_out, aCA)) { 2051 BUF_MEM *bio_buf; 2052 BIO_get_mem_ptr(bio_out, &bio_buf); 2053 MAKE_STD_ZVAL(zextracert); 2054 ZVAL_STRINGL(zextracert, bio_buf->data, bio_buf->length, 1); 2055 add_index_zval(zextracerts, i, zextracert); 2056 2057 } 2058 BIO_free(bio_out); 2059 2060 X509_free(aCA); 2061 } 2062 if(ca) { 2063 sk_X509_free(ca); 2064 add_assoc_zval(zout, "extracerts", zextracerts); 2065 } else { 2066 zval_dtor(zextracerts); 2067 } 2068 2069 RETVAL_TRUE; 2070 2071 PKCS12_free(p12); 2072 } 2073 } 2074 2075 cleanup: 2076 if (bio_in) { 2077 BIO_free(bio_in); 2078 } 2079 if (pkey) { 2080 EVP_PKEY_free(pkey); 2081 } 2082 if (cert) { 2083 X509_free(cert); 2084 } 2085} 2086/* }}} */ 2087 2088/* {{{ x509 CSR functions */ 2089 2090/* {{{ php_openssl_make_REQ */ 2091static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs TSRMLS_DC) 2092{ 2093 STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL; 2094 char * str, *dn_sect, *attr_sect; 2095 2096 dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name"); 2097 if (dn_sect == NULL) { 2098 return FAILURE; 2099 } 2100 dn_sk = CONF_get_section(req->req_config, dn_sect); 2101 if (dn_sk == NULL) { 2102 return FAILURE; 2103 } 2104 attr_sect = CONF_get_string(req->req_config, req->section_name, "attributes"); 2105 if (attr_sect == NULL) { 2106 attr_sk = NULL; 2107 } else { 2108 attr_sk = CONF_get_section(req->req_config, attr_sect); 2109 if (attr_sk == NULL) { 2110 return FAILURE; 2111 } 2112 } 2113 /* setup the version number: version 1 */ 2114 if (X509_REQ_set_version(csr, 0L)) { 2115 int i, nid; 2116 char * type; 2117 CONF_VALUE * v; 2118 X509_NAME * subj; 2119 HashPosition hpos; 2120 zval ** item; 2121 2122 subj = X509_REQ_get_subject_name(csr); 2123 /* apply values from the dn hash */ 2124 zend_hash_internal_pointer_reset_ex(HASH_OF(dn), &hpos); 2125 while(zend_hash_get_current_data_ex(HASH_OF(dn), (void**)&item, &hpos) == SUCCESS) { 2126 char * strindex = NULL; 2127 uint strindexlen = 0; 2128 ulong intindex; 2129 2130 zend_hash_get_current_key_ex(HASH_OF(dn), &strindex, &strindexlen, &intindex, 0, &hpos); 2131 2132 convert_to_string_ex(item); 2133 2134 if (strindex) { 2135 int nid; 2136 2137 nid = OBJ_txt2nid(strindex); 2138 if (nid != NID_undef) { 2139 if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, 2140 (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0)) 2141 { 2142 php_error_docref(NULL TSRMLS_CC, E_WARNING, 2143 "dn: add_entry_by_NID %d -> %s (failed; check error" 2144 " queue and value of string_mask OpenSSL option " 2145 "if illegal characters are reported)", 2146 nid, Z_STRVAL_PP(item)); 2147 return FAILURE; 2148 } 2149 } else { 2150 php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex); 2151 } 2152 } 2153 zend_hash_move_forward_ex(HASH_OF(dn), &hpos); 2154 } 2155 2156 /* Finally apply defaults from config file */ 2157 for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) { 2158 int len; 2159 char buffer[200 + 1]; /*200 + \0 !*/ 2160 2161 v = sk_CONF_VALUE_value(dn_sk, i); 2162 type = v->name; 2163 2164 len = strlen(type); 2165 if (len < sizeof("_default")) { 2166 continue; 2167 } 2168 len -= sizeof("_default") - 1; 2169 if (strcmp("_default", type + len) != 0) { 2170 continue; 2171 } 2172 if (len > 200) { 2173 len = 200; 2174 } 2175 memcpy(buffer, type, len); 2176 buffer[len] = '\0'; 2177 type = buffer; 2178 2179 /* Skip past any leading X. X: X, etc to allow for multiple 2180 * instances */ 2181 for (str = type; *str; str++) { 2182 if (*str == ':' || *str == ',' || *str == '.') { 2183 str++; 2184 if (*str) { 2185 type = str; 2186 } 2187 break; 2188 } 2189 } 2190 /* if it is already set, skip this */ 2191 nid = OBJ_txt2nid(type); 2192 if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) { 2193 continue; 2194 } 2195 if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) { 2196 php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value); 2197 return FAILURE; 2198 } 2199 if (!X509_NAME_entry_count(subj)) { 2200 php_error_docref(NULL TSRMLS_CC, E_WARNING, "no objects specified in config file"); 2201 return FAILURE; 2202 } 2203 } 2204 if (attribs) { 2205 zend_hash_internal_pointer_reset_ex(HASH_OF(attribs), &hpos); 2206 while(zend_hash_get_current_data_ex(HASH_OF(attribs), (void**)&item, &hpos) == SUCCESS) { 2207 char *strindex = NULL; 2208 uint strindexlen; 2209 ulong intindex; 2210 2211 zend_hash_get_current_key_ex(HASH_OF(attribs), &strindex, &strindexlen, &intindex, 0, &hpos); 2212 convert_to_string_ex(item); 2213 2214 if (strindex) { 2215 int nid; 2216 2217 nid = OBJ_txt2nid(strindex); 2218 if (nid != NID_undef) { 2219 if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0)) { 2220 php_error_docref(NULL TSRMLS_CC, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item)); 2221 return FAILURE; 2222 } 2223 } else { 2224 php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex); 2225 } 2226 } 2227 zend_hash_move_forward_ex(HASH_OF(attribs), &hpos); 2228 } 2229 for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) { 2230 v = sk_CONF_VALUE_value(attr_sk, i); 2231 /* if it is already set, skip this */ 2232 nid = OBJ_txt2nid(v->name); 2233 if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) { 2234 continue; 2235 } 2236 if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) { 2237 php_error_docref(NULL TSRMLS_CC, E_WARNING, 2238 "add1_attr_by_txt %s -> %s (failed; check error queue " 2239 "and value of string_mask OpenSSL option if illegal " 2240 "characters are reported)", 2241 v->name, v->value); 2242 return FAILURE; 2243 } 2244 } 2245 } 2246 } 2247 2248 X509_REQ_set_pubkey(csr, req->priv_key); 2249 return SUCCESS; 2250} 2251/* }}} */ 2252 2253/* {{{ php_openssl_csr_from_zval */ 2254static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC) 2255{ 2256 X509_REQ * csr = NULL; 2257 char * filename = NULL; 2258 BIO * in; 2259 2260 if (resourceval) { 2261 *resourceval = -1; 2262 } 2263 if (Z_TYPE_PP(val) == IS_RESOURCE) { 2264 void * what; 2265 int type; 2266 2267 what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509 CSR", &type, 1, le_csr); 2268 if (what) { 2269 if (resourceval) { 2270 *resourceval = Z_LVAL_PP(val); 2271 } 2272 return (X509_REQ*)what; 2273 } 2274 return NULL; 2275 } else if (Z_TYPE_PP(val) != IS_STRING) { 2276 return NULL; 2277 } 2278 2279 if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) { 2280 filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1); 2281 } 2282 if (filename) { 2283 if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { 2284 return NULL; 2285 } 2286 in = BIO_new_file(filename, "r"); 2287 } else { 2288 in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); 2289 } 2290 csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL); 2291 BIO_free(in); 2292 2293 return csr; 2294} 2295/* }}} */ 2296 2297/* {{{ proto bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true]) 2298 Exports a CSR to file */ 2299PHP_FUNCTION(openssl_csr_export_to_file) 2300{ 2301 X509_REQ * csr; 2302 zval * zcsr = NULL; 2303 zend_bool notext = 1; 2304 char * filename = NULL; int filename_len; 2305 BIO * bio_out; 2306 long csr_resource; 2307 2308 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp|b", &zcsr, &filename, &filename_len, ¬ext) == FAILURE) { 2309 return; 2310 } 2311 RETVAL_FALSE; 2312 2313 csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC); 2314 if (csr == NULL) { 2315 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1"); 2316 return; 2317 } 2318 2319 if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { 2320 return; 2321 } 2322 2323 bio_out = BIO_new_file(filename, "w"); 2324 if (bio_out) { 2325 if (!notext) { 2326 X509_REQ_print(bio_out, csr); 2327 } 2328 PEM_write_bio_X509_REQ(bio_out, csr); 2329 RETVAL_TRUE; 2330 } else { 2331 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename); 2332 } 2333 2334 if (csr_resource == -1 && csr) { 2335 X509_REQ_free(csr); 2336 } 2337 BIO_free(bio_out); 2338} 2339/* }}} */ 2340 2341/* {{{ proto bool openssl_csr_export(resource csr, string &out [, bool notext=true]) 2342 Exports a CSR to file or a var */ 2343PHP_FUNCTION(openssl_csr_export) 2344{ 2345 X509_REQ * csr; 2346 zval * zcsr = NULL, *zout=NULL; 2347 zend_bool notext = 1; 2348 BIO * bio_out; 2349 2350 long csr_resource; 2351 2352 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|b", &zcsr, &zout, ¬ext) == FAILURE) { 2353 return; 2354 } 2355 RETVAL_FALSE; 2356 2357 csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC); 2358 if (csr == NULL) { 2359 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1"); 2360 return; 2361 } 2362 2363 /* export to a var */ 2364 2365 bio_out = BIO_new(BIO_s_mem()); 2366 if (!notext) { 2367 X509_REQ_print(bio_out, csr); 2368 } 2369 2370 if (PEM_write_bio_X509_REQ(bio_out, csr)) { 2371 BUF_MEM *bio_buf; 2372 2373 BIO_get_mem_ptr(bio_out, &bio_buf); 2374 zval_dtor(zout); 2375 ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1); 2376 2377 RETVAL_TRUE; 2378 } 2379 2380 if (csr_resource == -1 && csr) { 2381 X509_REQ_free(csr); 2382 } 2383 BIO_free(bio_out); 2384} 2385/* }}} */ 2386 2387/* {{{ proto resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]]) 2388 Signs a cert with another CERT */ 2389PHP_FUNCTION(openssl_csr_sign) 2390{ 2391 zval ** zcert = NULL, **zcsr, **zpkey, *args = NULL; 2392 long num_days; 2393 long serial = 0L; 2394 X509 * cert = NULL, *new_cert = NULL; 2395 X509_REQ * csr; 2396 EVP_PKEY * key = NULL, *priv_key = NULL; 2397 long csr_resource, certresource = 0, keyresource = -1; 2398 int i; 2399 struct php_x509_request req; 2400 2401 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ!Zl|a!l", &zcsr, &zcert, &zpkey, &num_days, &args, &serial) == FAILURE) 2402 return; 2403 2404 RETVAL_FALSE; 2405 PHP_SSL_REQ_INIT(&req); 2406 2407 csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC); 2408 if (csr == NULL) { 2409 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1"); 2410 return; 2411 } 2412 if (zcert) { 2413 cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); 2414 if (cert == NULL) { 2415 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 2"); 2416 goto cleanup; 2417 } 2418 } 2419 priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 1, &keyresource TSRMLS_CC); 2420 if (priv_key == NULL) { 2421 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3"); 2422 goto cleanup; 2423 } 2424 if (cert && !X509_check_private_key(cert, priv_key)) { 2425 php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to signing cert"); 2426 goto cleanup; 2427 } 2428 2429 if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) { 2430 goto cleanup; 2431 } 2432 /* Check that the request matches the signature */ 2433 key = X509_REQ_get_pubkey(csr); 2434 if (key == NULL) { 2435 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error unpacking public key"); 2436 goto cleanup; 2437 } 2438 i = X509_REQ_verify(csr, key); 2439 2440 if (i < 0) { 2441 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature verification problems"); 2442 goto cleanup; 2443 } 2444 else if (i == 0) { 2445 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature did not match the certificate request"); 2446 goto cleanup; 2447 } 2448 2449 /* Now we can get on with it */ 2450 2451 new_cert = X509_new(); 2452 if (new_cert == NULL) { 2453 php_error_docref(NULL TSRMLS_CC, E_WARNING, "No memory"); 2454 goto cleanup; 2455 } 2456 /* Version 3 cert */ 2457 if (!X509_set_version(new_cert, 2)) 2458 goto cleanup; 2459 2460 ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial); 2461 2462 X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr)); 2463 2464 if (cert == NULL) { 2465 cert = new_cert; 2466 } 2467 if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) { 2468 goto cleanup; 2469 } 2470 X509_gmtime_adj(X509_get_notBefore(new_cert), 0); 2471 X509_gmtime_adj(X509_get_notAfter(new_cert), (long)60*60*24*num_days); 2472 i = X509_set_pubkey(new_cert, key); 2473 if (!i) { 2474 goto cleanup; 2475 } 2476 if (req.extensions_section) { 2477 X509V3_CTX ctx; 2478 2479 X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0); 2480 X509V3_set_conf_lhash(&ctx, req.req_config); 2481 if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) { 2482 goto cleanup; 2483 } 2484 } 2485 2486 /* Now sign it */ 2487 if (!X509_sign(new_cert, priv_key, req.digest)) { 2488 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to sign it"); 2489 goto cleanup; 2490 } 2491 2492 /* Succeeded; lets return the cert */ 2493 RETVAL_RESOURCE(zend_list_insert(new_cert, le_x509 TSRMLS_CC)); 2494 new_cert = NULL; 2495 2496cleanup: 2497 2498 if (cert == new_cert) { 2499 cert = NULL; 2500 } 2501 PHP_SSL_REQ_DISPOSE(&req); 2502 2503 if (keyresource == -1 && priv_key) { 2504 EVP_PKEY_free(priv_key); 2505 } 2506 if (key) { 2507 EVP_PKEY_free(key); 2508 } 2509 if (csr_resource == -1 && csr) { 2510 X509_REQ_free(csr); 2511 } 2512 if (certresource == -1 && cert) { 2513 X509_free(cert); 2514 } 2515 if (new_cert) { 2516 X509_free(new_cert); 2517 } 2518} 2519/* }}} */ 2520 2521/* {{{ proto bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]]) 2522 Generates a privkey and CSR */ 2523PHP_FUNCTION(openssl_csr_new) 2524{ 2525 struct php_x509_request req; 2526 zval * args = NULL, * dn, *attribs = NULL; 2527 zval * out_pkey; 2528 X509_REQ * csr = NULL; 2529 int we_made_the_key = 1; 2530 long key_resource; 2531 2532 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) { 2533 return; 2534 } 2535 RETVAL_FALSE; 2536 2537 PHP_SSL_REQ_INIT(&req); 2538 2539 if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) { 2540 /* Generate or use a private key */ 2541 if (Z_TYPE_P(out_pkey) != IS_NULL) { 2542 req.priv_key = php_openssl_evp_from_zval(&out_pkey, 0, NULL, 0, &key_resource TSRMLS_CC); 2543 if (req.priv_key != NULL) { 2544 we_made_the_key = 0; 2545 } 2546 } 2547 if (req.priv_key == NULL) { 2548 php_openssl_generate_private_key(&req TSRMLS_CC); 2549 } 2550 if (req.priv_key == NULL) { 2551 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to generate a private key"); 2552 } else { 2553 csr = X509_REQ_new(); 2554 if (csr) { 2555 if (php_openssl_make_REQ(&req, csr, dn, attribs TSRMLS_CC) == SUCCESS) { 2556 X509V3_CTX ext_ctx; 2557 2558 X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0); 2559 X509V3_set_conf_lhash(&ext_ctx, req.req_config); 2560 2561 /* Add extensions */ 2562 if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config, 2563 &ext_ctx, req.request_extensions_section, csr)) 2564 { 2565 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading extension section %s", req.request_extensions_section); 2566 } else { 2567 RETVAL_TRUE; 2568 2569 if (X509_REQ_sign(csr, req.priv_key, req.digest)) { 2570 RETVAL_RESOURCE(zend_list_insert(csr, le_csr TSRMLS_CC)); 2571 csr = NULL; 2572 } else { 2573 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error signing request"); 2574 } 2575 2576 if (we_made_the_key) { 2577 /* and a resource for the private key */ 2578 zval_dtor(out_pkey); 2579 ZVAL_RESOURCE(out_pkey, zend_list_insert(req.priv_key, le_key TSRMLS_CC)); 2580 req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */ 2581 } else if (key_resource != -1) { 2582 req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */ 2583 } 2584 } 2585 } 2586 else { 2587 if (!we_made_the_key) { 2588 /* if we have not made the key we are not supposed to zap it by calling dispose! */ 2589 req.priv_key = NULL; 2590 } 2591 } 2592 } 2593 } 2594 } 2595 if (csr) { 2596 X509_REQ_free(csr); 2597 } 2598 PHP_SSL_REQ_DISPOSE(&req); 2599} 2600/* }}} */ 2601 2602/* {{{ proto mixed openssl_csr_get_subject(mixed csr) 2603 Returns the subject of a CERT or FALSE on error */ 2604PHP_FUNCTION(openssl_csr_get_subject) 2605{ 2606 zval ** zcsr; 2607 zend_bool use_shortnames = 1; 2608 long csr_resource; 2609 X509_NAME * subject; 2610 X509_REQ * csr; 2611 2612 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) { 2613 return; 2614 } 2615 2616 csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC); 2617 2618 if (csr == NULL) { 2619 RETURN_FALSE; 2620 } 2621 2622 subject = X509_REQ_get_subject_name(csr); 2623 2624 array_init(return_value); 2625 add_assoc_name_entry(return_value, NULL, subject, use_shortnames TSRMLS_CC); 2626 return; 2627} 2628/* }}} */ 2629 2630/* {{{ proto mixed openssl_csr_get_public_key(mixed csr) 2631 Returns the subject of a CERT or FALSE on error */ 2632PHP_FUNCTION(openssl_csr_get_public_key) 2633{ 2634 zval ** zcsr; 2635 zend_bool use_shortnames = 1; 2636 long csr_resource; 2637 2638 X509_REQ * csr; 2639 EVP_PKEY *tpubkey; 2640 2641 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) { 2642 return; 2643 } 2644 2645 csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC); 2646 2647 if (csr == NULL) { 2648 RETURN_FALSE; 2649 } 2650 2651 tpubkey=X509_REQ_get_pubkey(csr); 2652 RETVAL_RESOURCE(zend_list_insert(tpubkey, le_key TSRMLS_CC)); 2653 return; 2654} 2655/* }}} */ 2656 2657/* }}} */ 2658 2659/* {{{ EVP Public/Private key functions */ 2660 2661/* {{{ php_openssl_evp_from_zval 2662 Given a zval, coerce it into a EVP_PKEY object. 2663 It can be: 2664 1. private key resource from openssl_get_privatekey() 2665 2. X509 resource -> public key will be extracted from it 2666 3. if it starts with file:// interpreted as path to key file 2667 4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey() 2668 5. an array(0 => [items 2..4], 1 => passphrase) 2669 6. if val is a string (possibly starting with file:///) and it is not an X509 certificate, then interpret as public key 2670 NOTE: If you are requesting a private key but have not specified a passphrase, you should use an 2671 empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in 2672 the Apache error log! 2673*/ 2674static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC) 2675{ 2676 EVP_PKEY * key = NULL; 2677 X509 * cert = NULL; 2678 int free_cert = 0; 2679 long cert_res = -1; 2680 char * filename = NULL; 2681 zval tmp; 2682 2683 Z_TYPE(tmp) = IS_NULL; 2684 2685#define TMP_CLEAN \ 2686 if (Z_TYPE(tmp) == IS_STRING) {\ 2687 zval_dtor(&tmp); \ 2688 } \ 2689 return NULL; 2690 2691 if (resourceval) { 2692 *resourceval = -1; 2693 } 2694 if (Z_TYPE_PP(val) == IS_ARRAY) { 2695 zval ** zphrase; 2696 2697 /* get passphrase */ 2698 2699 if (zend_hash_index_find(HASH_OF(*val), 1, (void **)&zphrase) == FAILURE) { 2700 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)"); 2701 return NULL; 2702 } 2703 2704 if (Z_TYPE_PP(zphrase) == IS_STRING) { 2705 passphrase = Z_STRVAL_PP(zphrase); 2706 } else { 2707 tmp = **zphrase; 2708 zval_copy_ctor(&tmp); 2709 convert_to_string(&tmp); 2710 passphrase = Z_STRVAL(tmp); 2711 } 2712 2713 /* now set val to be the key param and continue */ 2714 if (zend_hash_index_find(HASH_OF(*val), 0, (void **)&val) == FAILURE) { 2715 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)"); 2716 TMP_CLEAN; 2717 } 2718 } 2719 2720 if (Z_TYPE_PP(val) == IS_RESOURCE) { 2721 void * what; 2722 int type; 2723 2724 what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key); 2725 if (!what) { 2726 TMP_CLEAN; 2727 } 2728 if (resourceval) { 2729 *resourceval = Z_LVAL_PP(val); 2730 } 2731 if (type == le_x509) { 2732 /* extract key from cert, depending on public_key param */ 2733 cert = (X509*)what; 2734 free_cert = 0; 2735 } else if (type == le_key) { 2736 int is_priv; 2737 2738 is_priv = php_openssl_is_private_key((EVP_PKEY*)what TSRMLS_CC); 2739 2740 /* check whether it is actually a private key if requested */ 2741 if (!public_key && !is_priv) { 2742 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param is a public key"); 2743 TMP_CLEAN; 2744 } 2745 2746 if (public_key && is_priv) { 2747 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Don't know how to get public key from this private key"); 2748 TMP_CLEAN; 2749 } else { 2750 if (Z_TYPE(tmp) == IS_STRING) { 2751 zval_dtor(&tmp); 2752 } 2753 /* got the key - return it */ 2754 return (EVP_PKEY*)what; 2755 } 2756 } else { 2757 /* other types could be used here - eg: file pointers and read in the data from them */ 2758 TMP_CLEAN; 2759 } 2760 } else { 2761 /* force it to be a string and check if it refers to a file */ 2762 /* passing non string values leaks, object uses toString, it returns NULL 2763 * See bug38255.phpt 2764 */ 2765 if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) { 2766 TMP_CLEAN; 2767 } 2768 convert_to_string_ex(val); 2769 2770 if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) { 2771 filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1); 2772 } 2773 /* it's an X509 file/cert of some kind, and we need to extract the data from that */ 2774 if (public_key) { 2775 cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC); 2776 free_cert = (cert_res == -1); 2777 /* actual extraction done later */ 2778 if (!cert) { 2779 /* not a X509 certificate, try to retrieve public key */ 2780 BIO* in; 2781 if (filename) { 2782 in = BIO_new_file(filename, "r"); 2783 } else { 2784 in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); 2785 } 2786 if (in == NULL) { 2787 TMP_CLEAN; 2788 } 2789 key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL); 2790 BIO_free(in); 2791 } 2792 } else { 2793 /* we want the private key */ 2794 BIO *in; 2795 2796 if (filename) { 2797 if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { 2798 TMP_CLEAN; 2799 } 2800 in = BIO_new_file(filename, "r"); 2801 } else { 2802 in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); 2803 } 2804 2805 if (in == NULL) { 2806 TMP_CLEAN; 2807 } 2808 key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase); 2809 BIO_free(in); 2810 } 2811 } 2812 2813 if (public_key && cert && key == NULL) { 2814 /* extract public key from X509 cert */ 2815 key = (EVP_PKEY *) X509_get_pubkey(cert); 2816 } 2817 2818 if (free_cert && cert) { 2819 X509_free(cert); 2820 } 2821 if (key && makeresource && resourceval) { 2822 *resourceval = ZEND_REGISTER_RESOURCE(NULL, key, le_key); 2823 } 2824 if (Z_TYPE(tmp) == IS_STRING) { 2825 zval_dtor(&tmp); 2826 } 2827 return key; 2828} 2829/* }}} */ 2830 2831/* {{{ php_openssl_generate_private_key */ 2832static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC) 2833{ 2834 char * randfile = NULL; 2835 int egdsocket, seeded; 2836 EVP_PKEY * return_val = NULL; 2837 2838 if (req->priv_key_bits < MIN_KEY_LENGTH) { 2839 php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key length is too short; it needs to be at least %d bits, not %d", 2840 MIN_KEY_LENGTH, req->priv_key_bits); 2841 return NULL; 2842 } 2843 2844 randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE"); 2845 php_openssl_load_rand_file(randfile, &egdsocket, &seeded TSRMLS_CC); 2846 2847 if ((req->priv_key = EVP_PKEY_new()) != NULL) { 2848 switch(req->priv_key_type) { 2849 case OPENSSL_KEYTYPE_RSA: 2850 if (EVP_PKEY_assign_RSA(req->priv_key, RSA_generate_key(req->priv_key_bits, 0x10001, NULL, NULL))) { 2851 return_val = req->priv_key; 2852 } 2853 break; 2854#if !defined(NO_DSA) && defined(HAVE_DSA_DEFAULT_METHOD) 2855 case OPENSSL_KEYTYPE_DSA: 2856 { 2857 DSA *dsapar = DSA_generate_parameters(req->priv_key_bits, NULL, 0, NULL, NULL, NULL, NULL); 2858 if (dsapar) { 2859 DSA_set_method(dsapar, DSA_get_default_method()); 2860 if (DSA_generate_key(dsapar)) { 2861 if (EVP_PKEY_assign_DSA(req->priv_key, dsapar)) { 2862 return_val = req->priv_key; 2863 } 2864 } else { 2865 DSA_free(dsapar); 2866 } 2867 } 2868 } 2869 break; 2870#endif 2871#if !defined(NO_DH) 2872 case OPENSSL_KEYTYPE_DH: 2873 { 2874 DH *dhpar = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL); 2875 int codes = 0; 2876 2877 if (dhpar) { 2878 DH_set_method(dhpar, DH_get_default_method()); 2879 if (DH_check(dhpar, &codes) && codes == 0 && DH_generate_key(dhpar)) { 2880 if (EVP_PKEY_assign_DH(req->priv_key, dhpar)) { 2881 return_val = req->priv_key; 2882 } 2883 } else { 2884 DH_free(dhpar); 2885 } 2886 } 2887 } 2888 break; 2889#endif 2890 default: 2891 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported private key type"); 2892 } 2893 } 2894 2895 php_openssl_write_rand_file(randfile, egdsocket, seeded); 2896 2897 if (return_val == NULL) { 2898 EVP_PKEY_free(req->priv_key); 2899 req->priv_key = NULL; 2900 return NULL; 2901 } 2902 2903 return return_val; 2904} 2905/* }}} */ 2906 2907/* {{{ php_openssl_is_private_key 2908 Check whether the supplied key is a private key by checking if the secret prime factors are set */ 2909static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC) 2910{ 2911 assert(pkey != NULL); 2912 2913 switch (pkey->type) { 2914#ifndef NO_RSA 2915 case EVP_PKEY_RSA: 2916 case EVP_PKEY_RSA2: 2917 assert(pkey->pkey.rsa != NULL); 2918 if (pkey->pkey.rsa != NULL && (NULL == pkey->pkey.rsa->p || NULL == pkey->pkey.rsa->q)) { 2919 return 0; 2920 } 2921 break; 2922#endif 2923#ifndef NO_DSA 2924 case EVP_PKEY_DSA: 2925 case EVP_PKEY_DSA1: 2926 case EVP_PKEY_DSA2: 2927 case EVP_PKEY_DSA3: 2928 case EVP_PKEY_DSA4: 2929 assert(pkey->pkey.dsa != NULL); 2930 2931 if (NULL == pkey->pkey.dsa->p || NULL == pkey->pkey.dsa->q || NULL == pkey->pkey.dsa->priv_key){ 2932 return 0; 2933 } 2934 break; 2935#endif 2936#ifndef NO_DH 2937 case EVP_PKEY_DH: 2938 assert(pkey->pkey.dh != NULL); 2939 2940 if (NULL == pkey->pkey.dh->p || NULL == pkey->pkey.dh->priv_key) { 2941 return 0; 2942 } 2943 break; 2944#endif 2945 default: 2946 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); 2947 break; 2948 } 2949 return 1; 2950} 2951/* }}} */ 2952 2953#define OPENSSL_PKEY_GET_BN(_type, _name) do { \ 2954 if (pkey->pkey._type->_name != NULL) { \ 2955 int len = BN_num_bytes(pkey->pkey._type->_name); \ 2956 char *str = emalloc(len + 1); \ 2957 BN_bn2bin(pkey->pkey._type->_name, (unsigned char*)str); \ 2958 str[len] = 0; \ 2959 add_assoc_stringl(_type, #_name, str, len, 0); \ 2960 } \ 2961 } while (0) 2962 2963#define OPENSSL_PKEY_SET_BN(_ht, _type, _name) do { \ 2964 zval **bn; \ 2965 if (zend_hash_find(_ht, #_name, sizeof(#_name), (void**)&bn) == SUCCESS && \ 2966 Z_TYPE_PP(bn) == IS_STRING) { \ 2967 _type->_name = BN_bin2bn( \ 2968 (unsigned char*)Z_STRVAL_PP(bn), \ 2969 Z_STRLEN_PP(bn), NULL); \ 2970 } \ 2971 } while (0); 2972 2973 2974/* {{{ proto resource openssl_pkey_new([array configargs]) 2975 Generates a new private key */ 2976PHP_FUNCTION(openssl_pkey_new) 2977{ 2978 struct php_x509_request req; 2979 zval * args = NULL; 2980 zval **data; 2981 2982 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &args) == FAILURE) { 2983 return; 2984 } 2985 RETVAL_FALSE; 2986 2987 if (args && Z_TYPE_P(args) == IS_ARRAY) { 2988 EVP_PKEY *pkey; 2989 2990 if (zend_hash_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa"), (void**)&data) == SUCCESS && 2991 Z_TYPE_PP(data) == IS_ARRAY) { 2992 pkey = EVP_PKEY_new(); 2993 if (pkey) { 2994 RSA *rsa = RSA_new(); 2995 if (rsa) { 2996 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, n); 2997 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, e); 2998 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, d); 2999 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, p); 3000 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, q); 3001 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmp1); 3002 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmq1); 3003 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, iqmp); 3004 if (rsa->n && rsa->d) { 3005 if (EVP_PKEY_assign_RSA(pkey, rsa)) { 3006 RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC)); 3007 } 3008 } 3009 RSA_free(rsa); 3010 } 3011 EVP_PKEY_free(pkey); 3012 } 3013 RETURN_FALSE; 3014 } else if (zend_hash_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa"), (void**)&data) == SUCCESS && 3015 Z_TYPE_PP(data) == IS_ARRAY) { 3016 pkey = EVP_PKEY_new(); 3017 if (pkey) { 3018 DSA *dsa = DSA_new(); 3019 if (dsa) { 3020 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, p); 3021 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, q); 3022 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, g); 3023 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, priv_key); 3024 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, pub_key); 3025 if (dsa->p && dsa->q && dsa->g) { 3026 if (!dsa->priv_key && !dsa->pub_key) { 3027 DSA_generate_key(dsa); 3028 } 3029 if (EVP_PKEY_assign_DSA(pkey, dsa)) { 3030 RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC)); 3031 } 3032 } 3033 DSA_free(dsa); 3034 } 3035 EVP_PKEY_free(pkey); 3036 } 3037 RETURN_FALSE; 3038 } else if (zend_hash_find(Z_ARRVAL_P(args), "dh", sizeof("dh"), (void**)&data) == SUCCESS && 3039 Z_TYPE_PP(data) == IS_ARRAY) { 3040 pkey = EVP_PKEY_new(); 3041 if (pkey) { 3042 DH *dh = DH_new(); 3043 if (dh) { 3044 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, p); 3045 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, g); 3046 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, priv_key); 3047 OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, pub_key); 3048 if (dh->p && dh->g) { 3049 if (!dh->pub_key) { 3050 DH_generate_key(dh); 3051 } 3052 if (EVP_PKEY_assign_DH(pkey, dh)) { 3053 RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC)); 3054 } 3055 } 3056 DH_free(dh); 3057 } 3058 EVP_PKEY_free(pkey); 3059 } 3060 RETURN_FALSE; 3061 } 3062 } 3063 3064 PHP_SSL_REQ_INIT(&req); 3065 3066 if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) 3067 { 3068 if (php_openssl_generate_private_key(&req TSRMLS_CC)) { 3069 /* pass back a key resource */ 3070 RETVAL_RESOURCE(zend_list_insert(req.priv_key, le_key TSRMLS_CC)); 3071 /* make sure the cleanup code doesn't zap it! */ 3072 req.priv_key = NULL; 3073 } 3074 } 3075 PHP_SSL_REQ_DISPOSE(&req); 3076} 3077/* }}} */ 3078 3079/* {{{ proto bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args) 3080 Gets an exportable representation of a key into a file */ 3081PHP_FUNCTION(openssl_pkey_export_to_file) 3082{ 3083 struct php_x509_request req; 3084 zval ** zpkey, * args = NULL; 3085 char * passphrase = NULL; int passphrase_len = 0; 3086 char * filename = NULL; int filename_len = 0; 3087 long key_resource = -1; 3088 EVP_PKEY * key; 3089 BIO * bio_out = NULL; 3090 const EVP_CIPHER * cipher; 3091 3092 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zp|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) { 3093 return; 3094 } 3095 RETVAL_FALSE; 3096 3097 key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC); 3098 3099 if (key == NULL) { 3100 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1"); 3101 RETURN_FALSE; 3102 } 3103 3104 if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { 3105 RETURN_FALSE; 3106 } 3107 3108 PHP_SSL_REQ_INIT(&req); 3109 3110 if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) { 3111 bio_out = BIO_new_file(filename, "w"); 3112 3113 if (passphrase && req.priv_key_encrypt) { 3114 if (req.priv_key_encrypt_cipher) { 3115 cipher = req.priv_key_encrypt_cipher; 3116 } else { 3117 cipher = (EVP_CIPHER *) EVP_des_ede3_cbc(); 3118 } 3119 } else { 3120 cipher = NULL; 3121 } 3122 if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) { 3123 /* Success! 3124 * If returning the output as a string, do so now */ 3125 RETVAL_TRUE; 3126 } 3127 } 3128 PHP_SSL_REQ_DISPOSE(&req); 3129 3130 if (key_resource == -1 && key) { 3131 EVP_PKEY_free(key); 3132 } 3133 if (bio_out) { 3134 BIO_free(bio_out); 3135 } 3136} 3137/* }}} */ 3138 3139/* {{{ proto bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]]) 3140 Gets an exportable representation of a key into a string or file */ 3141PHP_FUNCTION(openssl_pkey_export) 3142{ 3143 struct php_x509_request req; 3144 zval ** zpkey, * args = NULL, *out; 3145 char * passphrase = NULL; int passphrase_len = 0; 3146 long key_resource = -1; 3147 EVP_PKEY * key; 3148 BIO * bio_out = NULL; 3149 const EVP_CIPHER * cipher; 3150 3151 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) { 3152 return; 3153 } 3154 RETVAL_FALSE; 3155 3156 key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC); 3157 3158 if (key == NULL) { 3159 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1"); 3160 RETURN_FALSE; 3161 } 3162 3163 PHP_SSL_REQ_INIT(&req); 3164 3165 if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) { 3166 bio_out = BIO_new(BIO_s_mem()); 3167 3168 if (passphrase && req.priv_key_encrypt) { 3169 if (req.priv_key_encrypt_cipher) { 3170 cipher = req.priv_key_encrypt_cipher; 3171 } else { 3172 cipher = (EVP_CIPHER *) EVP_des_ede3_cbc(); 3173 } 3174 } else { 3175 cipher = NULL; 3176 } 3177 if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) { 3178 /* Success! 3179 * If returning the output as a string, do so now */ 3180 3181 char * bio_mem_ptr; 3182 long bio_mem_len; 3183 RETVAL_TRUE; 3184 3185 bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr); 3186 zval_dtor(out); 3187 ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len, 1); 3188 } 3189 } 3190 PHP_SSL_REQ_DISPOSE(&req); 3191 3192 if (key_resource == -1 && key) { 3193 EVP_PKEY_free(key); 3194 } 3195 if (bio_out) { 3196 BIO_free(bio_out); 3197 } 3198} 3199/* }}} */ 3200 3201/* {{{ proto int openssl_pkey_get_public(mixed cert) 3202 Gets public key from X.509 certificate */ 3203PHP_FUNCTION(openssl_pkey_get_public) 3204{ 3205 zval **cert; 3206 EVP_PKEY *pkey; 3207 3208 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) { 3209 return; 3210 } 3211 Z_TYPE_P(return_value) = IS_RESOURCE; 3212 pkey = php_openssl_evp_from_zval(cert, 1, NULL, 1, &Z_LVAL_P(return_value) TSRMLS_CC); 3213 3214 if (pkey == NULL) { 3215 RETURN_FALSE; 3216 } 3217 zend_list_addref(Z_LVAL_P(return_value)); 3218} 3219/* }}} */ 3220 3221/* {{{ proto void openssl_pkey_free(int key) 3222 Frees a key */ 3223PHP_FUNCTION(openssl_pkey_free) 3224{ 3225 zval *key; 3226 EVP_PKEY *pkey; 3227 3228 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) { 3229 return; 3230 } 3231 ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key); 3232 zend_list_delete(Z_LVAL_P(key)); 3233} 3234/* }}} */ 3235 3236/* {{{ proto int openssl_pkey_get_private(string key [, string passphrase]) 3237 Gets private keys */ 3238PHP_FUNCTION(openssl_pkey_get_private) 3239{ 3240 zval **cert; 3241 EVP_PKEY *pkey; 3242 char * passphrase = ""; 3243 int passphrase_len = sizeof("")-1; 3244 3245 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|s", &cert, &passphrase, &passphrase_len) == FAILURE) { 3246 return; 3247 } 3248 Z_TYPE_P(return_value) = IS_RESOURCE; 3249 pkey = php_openssl_evp_from_zval(cert, 0, passphrase, 1, &Z_LVAL_P(return_value) TSRMLS_CC); 3250 3251 if (pkey == NULL) { 3252 RETURN_FALSE; 3253 } 3254 zend_list_addref(Z_LVAL_P(return_value)); 3255} 3256 3257/* }}} */ 3258 3259/* {{{ proto resource openssl_pkey_get_details(resource key) 3260 returns an array with the key details (bits, pkey, type)*/ 3261PHP_FUNCTION(openssl_pkey_get_details) 3262{ 3263 zval *key; 3264 EVP_PKEY *pkey; 3265 BIO *out; 3266 unsigned int pbio_len; 3267 char *pbio; 3268 long ktype; 3269 3270 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) { 3271 return; 3272 } 3273 ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key); 3274 if (!pkey) { 3275 RETURN_FALSE; 3276 } 3277 out = BIO_new(BIO_s_mem()); 3278 PEM_write_bio_PUBKEY(out, pkey); 3279 pbio_len = BIO_get_mem_data(out, &pbio); 3280 3281 array_init(return_value); 3282 add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey)); 3283 add_assoc_stringl(return_value, "key", pbio, pbio_len, 1); 3284 /*TODO: Use the real values once the openssl constants are used 3285 * See the enum at the top of this file 3286 */ 3287 switch (EVP_PKEY_type(pkey->type)) { 3288 case EVP_PKEY_RSA: 3289 case EVP_PKEY_RSA2: 3290 ktype = OPENSSL_KEYTYPE_RSA; 3291 3292 if (pkey->pkey.rsa != NULL) { 3293 zval *rsa; 3294 3295 ALLOC_INIT_ZVAL(rsa); 3296 array_init(rsa); 3297 OPENSSL_PKEY_GET_BN(rsa, n); 3298 OPENSSL_PKEY_GET_BN(rsa, e); 3299 OPENSSL_PKEY_GET_BN(rsa, d); 3300 OPENSSL_PKEY_GET_BN(rsa, p); 3301 OPENSSL_PKEY_GET_BN(rsa, q); 3302 OPENSSL_PKEY_GET_BN(rsa, dmp1); 3303 OPENSSL_PKEY_GET_BN(rsa, dmq1); 3304 OPENSSL_PKEY_GET_BN(rsa, iqmp); 3305 add_assoc_zval(return_value, "rsa", rsa); 3306 } 3307 3308 break; 3309 case EVP_PKEY_DSA: 3310 case EVP_PKEY_DSA2: 3311 case EVP_PKEY_DSA3: 3312 case EVP_PKEY_DSA4: 3313 ktype = OPENSSL_KEYTYPE_DSA; 3314 3315 if (pkey->pkey.dsa != NULL) { 3316 zval *dsa; 3317 3318 ALLOC_INIT_ZVAL(dsa); 3319 array_init(dsa); 3320 OPENSSL_PKEY_GET_BN(dsa, p); 3321 OPENSSL_PKEY_GET_BN(dsa, q); 3322 OPENSSL_PKEY_GET_BN(dsa, g); 3323 OPENSSL_PKEY_GET_BN(dsa, priv_key); 3324 OPENSSL_PKEY_GET_BN(dsa, pub_key); 3325 add_assoc_zval(return_value, "dsa", dsa); 3326 } 3327 break; 3328 case EVP_PKEY_DH: 3329 3330 ktype = OPENSSL_KEYTYPE_DH; 3331 3332 if (pkey->pkey.dh != NULL) { 3333 zval *dh; 3334 3335 ALLOC_INIT_ZVAL(dh); 3336 array_init(dh); 3337 OPENSSL_PKEY_GET_BN(dh, p); 3338 OPENSSL_PKEY_GET_BN(dh, g); 3339 OPENSSL_PKEY_GET_BN(dh, priv_key); 3340 OPENSSL_PKEY_GET_BN(dh, pub_key); 3341 add_assoc_zval(return_value, "dh", dh); 3342 } 3343 3344 break; 3345#ifdef EVP_PKEY_EC 3346 case EVP_PKEY_EC: 3347 ktype = OPENSSL_KEYTYPE_EC; 3348 break; 3349#endif 3350 default: 3351 ktype = -1; 3352 break; 3353 } 3354 add_assoc_long(return_value, "type", ktype); 3355 3356 BIO_free(out); 3357} 3358/* }}} */ 3359 3360/* }}} */ 3361 3362#if OPENSSL_VERSION_NUMBER >= 0x10000000L 3363 3364/* {{{ proto string openssl_pbkdf2(string password, string salt, long key_length, long iterations [, string digest_method = "sha1"]) 3365 Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */ 3366PHP_FUNCTION(openssl_pbkdf2) 3367{ 3368 long key_length = 0, iterations = 0; 3369 char *password; int password_len; 3370 char *salt; int salt_len; 3371 char *method; int method_len = 0; 3372 unsigned char *out_buffer; 3373 3374 const EVP_MD *digest; 3375 3376 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssll|s", 3377 &password, &password_len, 3378 &salt, &salt_len, 3379 &key_length, &iterations, 3380 &method, &method_len) == FAILURE) { 3381 return; 3382 } 3383 3384 if (key_length <= 0) { 3385 RETURN_FALSE; 3386 } 3387 3388 if (method_len) { 3389 digest = EVP_get_digestbyname(method); 3390 } else { 3391 digest = EVP_sha1(); 3392 } 3393 3394 if (!digest) { 3395 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm"); 3396 RETURN_FALSE; 3397 } 3398 3399 out_buffer = emalloc(key_length + 1); 3400 out_buffer[key_length] = '\0'; 3401 3402 if (PKCS5_PBKDF2_HMAC(password, password_len, (unsigned char *)salt, salt_len, iterations, digest, key_length, out_buffer) == 1) { 3403 RETVAL_STRINGL((char *)out_buffer, key_length, 0); 3404 } else { 3405 efree(out_buffer); 3406 RETURN_FALSE; 3407 } 3408} 3409/* }}} */ 3410 3411#endif 3412 3413/* {{{ PKCS7 S/MIME functions */ 3414 3415/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]]) 3416 Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */ 3417PHP_FUNCTION(openssl_pkcs7_verify) 3418{ 3419 X509_STORE * store = NULL; 3420 zval * cainfo = NULL; 3421 STACK_OF(X509) *signers= NULL; 3422 STACK_OF(X509) *others = NULL; 3423 PKCS7 * p7 = NULL; 3424 BIO * in = NULL, * datain = NULL, * dataout = NULL; 3425 long flags = 0; 3426 char * filename; int filename_len; 3427 char * extracerts = NULL; int extracerts_len = 0; 3428 char * signersfilename = NULL; int signersfilename_len = 0; 3429 char * datafilename = NULL; int datafilename_len = 0; 3430 3431 RETVAL_LONG(-1); 3432 3433 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "pl|papp", &filename, &filename_len, 3434 &flags, &signersfilename, &signersfilename_len, &cainfo, 3435 &extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) { 3436 return; 3437 } 3438 3439 if (extracerts) { 3440 others = load_all_certs_from_file(extracerts); 3441 if (others == NULL) { 3442 goto clean_exit; 3443 } 3444 } 3445 3446 flags = flags & ~PKCS7_DETACHED; 3447 3448 store = setup_verify(cainfo TSRMLS_CC); 3449 3450 if (!store) { 3451 goto clean_exit; 3452 } 3453 if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { 3454 goto clean_exit; 3455 } 3456 3457 in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r"); 3458 if (in == NULL) { 3459 goto clean_exit; 3460 } 3461 p7 = SMIME_read_PKCS7(in, &datain); 3462 if (p7 == NULL) { 3463#if DEBUG_SMIME 3464 zend_printf("SMIME_read_PKCS7 failed\n"); 3465#endif 3466 goto clean_exit; 3467 } 3468 3469 if (datafilename) { 3470 3471 if (php_openssl_open_base_dir_chk(datafilename TSRMLS_CC)) { 3472 goto clean_exit; 3473 } 3474 3475 dataout = BIO_new_file(datafilename, "w"); 3476 if (dataout == NULL) { 3477 goto clean_exit; 3478 } 3479 } 3480#if DEBUG_SMIME 3481 zend_printf("Calling PKCS7 verify\n"); 3482#endif 3483 3484 if (PKCS7_verify(p7, others, store, datain, dataout, flags)) { 3485 3486 RETVAL_TRUE; 3487 3488 if (signersfilename) { 3489 BIO *certout; 3490 3491 if (php_openssl_open_base_dir_chk(signersfilename TSRMLS_CC)) { 3492 goto clean_exit; 3493 } 3494 3495 certout = BIO_new_file(signersfilename, "w"); 3496 if (certout) { 3497 int i; 3498 signers = PKCS7_get0_signers(p7, NULL, flags); 3499 3500 for(i = 0; i < sk_X509_num(signers); i++) { 3501 PEM_write_bio_X509(certout, sk_X509_value(signers, i)); 3502 } 3503 BIO_free(certout); 3504 sk_X509_free(signers); 3505 } else { 3506 php_error_docref(NULL TSRMLS_CC, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename); 3507 RETVAL_LONG(-1); 3508 } 3509 } 3510 goto clean_exit; 3511 } else { 3512 RETVAL_FALSE; 3513 } 3514clean_exit: 3515 X509_STORE_free(store); 3516 BIO_free(datain); 3517 BIO_free(in); 3518 BIO_free(dataout); 3519 PKCS7_free(p7); 3520 sk_X509_free(others); 3521} 3522/* }}} */ 3523 3524/* {{{ proto bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]]) 3525 Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */ 3526PHP_FUNCTION(openssl_pkcs7_encrypt) 3527{ 3528 zval ** zrecipcerts, * zheaders = NULL; 3529 STACK_OF(X509) * recipcerts = NULL; 3530 BIO * infile = NULL, * outfile = NULL; 3531 long flags = 0; 3532 PKCS7 * p7 = NULL; 3533 HashPosition hpos; 3534 zval ** zcertval; 3535 X509 * cert; 3536 const EVP_CIPHER *cipher = NULL; 3537 long cipherid = PHP_OPENSSL_CIPHER_DEFAULT; 3538 uint strindexlen; 3539 ulong intindex; 3540 char * strindex; 3541 char * infilename = NULL; int infilename_len; 3542 char * outfilename = NULL; int outfilename_len; 3543 3544 RETVAL_FALSE; 3545 3546 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZa!|ll", &infilename, &infilename_len, 3547 &outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE) 3548 return; 3549 3550 3551 if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) { 3552 return; 3553 } 3554 3555 infile = BIO_new_file(infilename, "r"); 3556 if (infile == NULL) { 3557 goto clean_exit; 3558 } 3559 3560 outfile = BIO_new_file(outfilename, "w"); 3561 if (outfile == NULL) { 3562 goto clean_exit; 3563 } 3564 3565 recipcerts = sk_X509_new_null(); 3566 3567 /* get certs */ 3568 if (Z_TYPE_PP(zrecipcerts) == IS_ARRAY) { 3569 zend_hash_internal_pointer_reset_ex(HASH_OF(*zrecipcerts), &hpos); 3570 while(zend_hash_get_current_data_ex(HASH_OF(*zrecipcerts), (void**)&zcertval, &hpos) == SUCCESS) { 3571 long certresource; 3572 3573 cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC); 3574 if (cert == NULL) { 3575 goto clean_exit; 3576 } 3577 3578 if (certresource != -1) { 3579 /* we shouldn't free this particular cert, as it is a resource. 3580 make a copy and push that on the stack instead */ 3581 cert = X509_dup(cert); 3582 if (cert == NULL) { 3583 goto clean_exit; 3584 } 3585 } 3586 sk_X509_push(recipcerts, cert); 3587 3588 zend_hash_move_forward_ex(HASH_OF(*zrecipcerts), &hpos); 3589 } 3590 } else { 3591 /* a single certificate */ 3592 long certresource; 3593 3594 cert = php_openssl_x509_from_zval(zrecipcerts, 0, &certresource TSRMLS_CC); 3595 if (cert == NULL) { 3596 goto clean_exit; 3597 } 3598 3599 if (certresource != -1) { 3600 /* we shouldn't free this particular cert, as it is a resource. 3601 make a copy and push that on the stack instead */ 3602 cert = X509_dup(cert); 3603 if (cert == NULL) { 3604 goto clean_exit; 3605 } 3606 } 3607 sk_X509_push(recipcerts, cert); 3608 } 3609 3610 /* sanity check the cipher */ 3611 cipher = php_openssl_get_evp_cipher_from_algo(cipherid); 3612 if (cipher == NULL) { 3613 /* shouldn't happen */ 3614 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get cipher"); 3615 goto clean_exit; 3616 } 3617 3618 p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, flags); 3619 3620 if (p7 == NULL) { 3621 goto clean_exit; 3622 } 3623 3624 /* tack on extra headers */ 3625 if (zheaders) { 3626 zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos); 3627 while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&zcertval, &hpos) == SUCCESS) { 3628 strindex = NULL; 3629 zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos); 3630 3631 convert_to_string_ex(zcertval); 3632 3633 if (strindex) { 3634 BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(zcertval)); 3635 } else { 3636 BIO_printf(outfile, "%s\n", Z_STRVAL_PP(zcertval)); 3637 } 3638 3639 zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos); 3640 } 3641 } 3642 3643 (void)BIO_reset(infile); 3644 3645 /* write the encrypted data */ 3646 SMIME_write_PKCS7(outfile, p7, infile, flags); 3647 3648 RETVAL_TRUE; 3649 3650clean_exit: 3651 PKCS7_free(p7); 3652 BIO_free(infile); 3653 BIO_free(outfile); 3654 if (recipcerts) { 3655 sk_X509_pop_free(recipcerts, X509_free); 3656 } 3657} 3658/* }}} */ 3659 3660/* {{{ proto bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]]) 3661 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 */ 3662 3663PHP_FUNCTION(openssl_pkcs7_sign) 3664{ 3665 zval ** zcert, ** zprivkey, * zheaders; 3666 zval ** hval; 3667 X509 * cert = NULL; 3668 EVP_PKEY * privkey = NULL; 3669 long flags = PKCS7_DETACHED; 3670 PKCS7 * p7 = NULL; 3671 BIO * infile = NULL, * outfile = NULL; 3672 STACK_OF(X509) *others = NULL; 3673 long certresource = -1, keyresource = -1; 3674 ulong intindex; 3675 uint strindexlen; 3676 HashPosition hpos; 3677 char * strindex; 3678 char * infilename; int infilename_len; 3679 char * outfilename; int outfilename_len; 3680 char * extracertsfilename = NULL; int extracertsfilename_len; 3681 3682 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZZa!|lp", 3683 &infilename, &infilename_len, &outfilename, &outfilename_len, 3684 &zcert, &zprivkey, &zheaders, &flags, &extracertsfilename, 3685 &extracertsfilename_len) == FAILURE) { 3686 return; 3687 } 3688 3689 RETVAL_FALSE; 3690 3691 if (extracertsfilename) { 3692 others = load_all_certs_from_file(extracertsfilename); 3693 if (others == NULL) { 3694 goto clean_exit; 3695 } 3696 } 3697 3698 privkey = php_openssl_evp_from_zval(zprivkey, 0, "", 0, &keyresource TSRMLS_CC); 3699 if (privkey == NULL) { 3700 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting private key"); 3701 goto clean_exit; 3702 } 3703 3704 cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); 3705 if (cert == NULL) { 3706 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting cert"); 3707 goto clean_exit; 3708 } 3709 3710 if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) { 3711 goto clean_exit; 3712 } 3713 3714 infile = BIO_new_file(infilename, "r"); 3715 if (infile == NULL) { 3716 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening input file %s!", infilename); 3717 goto clean_exit; 3718 } 3719 3720 outfile = BIO_new_file(outfilename, "w"); 3721 if (outfile == NULL) { 3722 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening output file %s!", outfilename); 3723 goto clean_exit; 3724 } 3725 3726 p7 = PKCS7_sign(cert, privkey, others, infile, flags); 3727 if (p7 == NULL) { 3728 php_error_docref(NULL TSRMLS_CC, E_WARNING, "error creating PKCS7 structure!"); 3729 goto clean_exit; 3730 } 3731 3732 (void)BIO_reset(infile); 3733 3734 /* tack on extra headers */ 3735 if (zheaders) { 3736 zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos); 3737 while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&hval, &hpos) == SUCCESS) { 3738 strindex = NULL; 3739 zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos); 3740 3741 convert_to_string_ex(hval); 3742 3743 if (strindex) { 3744 BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(hval)); 3745 } else { 3746 BIO_printf(outfile, "%s\n", Z_STRVAL_PP(hval)); 3747 } 3748 zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos); 3749 } 3750 } 3751 /* write the signed data */ 3752 SMIME_write_PKCS7(outfile, p7, infile, flags); 3753 3754 RETVAL_TRUE; 3755 3756clean_exit: 3757 PKCS7_free(p7); 3758 BIO_free(infile); 3759 BIO_free(outfile); 3760 if (others) { 3761 sk_X509_pop_free(others, X509_free); 3762 } 3763 if (privkey && keyresource == -1) { 3764 EVP_PKEY_free(privkey); 3765 } 3766 if (cert && certresource == -1) { 3767 X509_free(cert); 3768 } 3769} 3770/* }}} */ 3771 3772/* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey]) 3773 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 */ 3774 3775PHP_FUNCTION(openssl_pkcs7_decrypt) 3776{ 3777 zval ** recipcert, ** recipkey = NULL; 3778 X509 * cert = NULL; 3779 EVP_PKEY * key = NULL; 3780 long certresval, keyresval; 3781 BIO * in = NULL, * out = NULL, * datain = NULL; 3782 PKCS7 * p7 = NULL; 3783 char * infilename; int infilename_len; 3784 char * outfilename; int outfilename_len; 3785 3786 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZ|Z", &infilename, &infilename_len, 3787 &outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE) { 3788 return; 3789 } 3790 3791 RETVAL_FALSE; 3792 3793 cert = php_openssl_x509_from_zval(recipcert, 0, &certresval TSRMLS_CC); 3794 if (cert == NULL) { 3795 php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 3 to x509 cert"); 3796 goto clean_exit; 3797 } 3798 3799 key = php_openssl_evp_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, &keyresval TSRMLS_CC); 3800 if (key == NULL) { 3801 php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to get private key"); 3802 goto clean_exit; 3803 } 3804 3805 if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) { 3806 goto clean_exit; 3807 } 3808 3809 in = BIO_new_file(infilename, "r"); 3810 if (in == NULL) { 3811 goto clean_exit; 3812 } 3813 out = BIO_new_file(outfilename, "w"); 3814 if (out == NULL) { 3815 goto clean_exit; 3816 } 3817 3818 p7 = SMIME_read_PKCS7(in, &datain); 3819 3820 if (p7 == NULL) { 3821 goto clean_exit; 3822 } 3823 if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) { 3824 RETVAL_TRUE; 3825 } 3826clean_exit: 3827 PKCS7_free(p7); 3828 BIO_free(datain); 3829 BIO_free(in); 3830 BIO_free(out); 3831 if (cert && certresval == -1) { 3832 X509_free(cert); 3833 } 3834 if (key && keyresval == -1) { 3835 EVP_PKEY_free(key); 3836 } 3837} 3838/* }}} */ 3839 3840/* }}} */ 3841 3842/* {{{ proto bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding]) 3843 Encrypts data with private key */ 3844PHP_FUNCTION(openssl_private_encrypt) 3845{ 3846 zval **key, *crypted; 3847 EVP_PKEY *pkey; 3848 int cryptedlen; 3849 unsigned char *cryptedbuf = NULL; 3850 int successful = 0; 3851 long keyresource = -1; 3852 char * data; 3853 int data_len; 3854 long padding = RSA_PKCS1_PADDING; 3855 3856 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) { 3857 return; 3858 } 3859 RETVAL_FALSE; 3860 3861 pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC); 3862 3863 if (pkey == NULL) { 3864 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key param is not a valid private key"); 3865 RETURN_FALSE; 3866 } 3867 3868 cryptedlen = EVP_PKEY_size(pkey); 3869 cryptedbuf = emalloc(cryptedlen + 1); 3870 3871 switch (pkey->type) { 3872 case EVP_PKEY_RSA: 3873 case EVP_PKEY_RSA2: 3874 successful = (RSA_private_encrypt(data_len, 3875 (unsigned char *)data, 3876 cryptedbuf, 3877 pkey->pkey.rsa, 3878 padding) == cryptedlen); 3879 break; 3880 default: 3881 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); 3882 } 3883 3884 if (successful) { 3885 zval_dtor(crypted); 3886 cryptedbuf[cryptedlen] = '\0'; 3887 ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0); 3888 cryptedbuf = NULL; 3889 RETVAL_TRUE; 3890 } 3891 if (cryptedbuf) { 3892 efree(cryptedbuf); 3893 } 3894 if (keyresource == -1) { 3895 EVP_PKEY_free(pkey); 3896 } 3897} 3898/* }}} */ 3899 3900/* {{{ proto bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding]) 3901 Decrypts data with private key */ 3902PHP_FUNCTION(openssl_private_decrypt) 3903{ 3904 zval **key, *crypted; 3905 EVP_PKEY *pkey; 3906 int cryptedlen; 3907 unsigned char *cryptedbuf = NULL; 3908 unsigned char *crypttemp; 3909 int successful = 0; 3910 long padding = RSA_PKCS1_PADDING; 3911 long keyresource = -1; 3912 char * data; 3913 int data_len; 3914 3915 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) { 3916 return; 3917 } 3918 RETVAL_FALSE; 3919 3920 pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC); 3921 if (pkey == NULL) { 3922 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid private key"); 3923 RETURN_FALSE; 3924 } 3925 3926 cryptedlen = EVP_PKEY_size(pkey); 3927 crypttemp = emalloc(cryptedlen + 1); 3928 3929 switch (pkey->type) { 3930 case EVP_PKEY_RSA: 3931 case EVP_PKEY_RSA2: 3932 cryptedlen = RSA_private_decrypt(data_len, 3933 (unsigned char *)data, 3934 crypttemp, 3935 pkey->pkey.rsa, 3936 padding); 3937 if (cryptedlen != -1) { 3938 cryptedbuf = emalloc(cryptedlen + 1); 3939 memcpy(cryptedbuf, crypttemp, cryptedlen); 3940 successful = 1; 3941 } 3942 break; 3943 default: 3944 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); 3945 } 3946 3947 efree(crypttemp); 3948 3949 if (successful) { 3950 zval_dtor(crypted); 3951 cryptedbuf[cryptedlen] = '\0'; 3952 ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0); 3953 cryptedbuf = NULL; 3954 RETVAL_TRUE; 3955 } 3956 3957 if (keyresource == -1) { 3958 EVP_PKEY_free(pkey); 3959 } 3960 if (cryptedbuf) { 3961 efree(cryptedbuf); 3962 } 3963} 3964/* }}} */ 3965 3966/* {{{ proto bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding]) 3967 Encrypts data with public key */ 3968PHP_FUNCTION(openssl_public_encrypt) 3969{ 3970 zval **key, *crypted; 3971 EVP_PKEY *pkey; 3972 int cryptedlen; 3973 unsigned char *cryptedbuf; 3974 int successful = 0; 3975 long keyresource = -1; 3976 long padding = RSA_PKCS1_PADDING; 3977 char * data; 3978 int data_len; 3979 3980 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) 3981 return; 3982 3983 RETVAL_FALSE; 3984 3985 pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC); 3986 if (pkey == NULL) { 3987 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key"); 3988 RETURN_FALSE; 3989 } 3990 3991 cryptedlen = EVP_PKEY_size(pkey); 3992 cryptedbuf = emalloc(cryptedlen + 1); 3993 3994 switch (pkey->type) { 3995 case EVP_PKEY_RSA: 3996 case EVP_PKEY_RSA2: 3997 successful = (RSA_public_encrypt(data_len, 3998 (unsigned char *)data, 3999 cryptedbuf, 4000 pkey->pkey.rsa, 4001 padding) == cryptedlen); 4002 break; 4003 default: 4004 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); 4005 4006 } 4007 4008 if (successful) { 4009 zval_dtor(crypted); 4010 cryptedbuf[cryptedlen] = '\0'; 4011 ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0); 4012 cryptedbuf = NULL; 4013 RETVAL_TRUE; 4014 } 4015 if (keyresource == -1) { 4016 EVP_PKEY_free(pkey); 4017 } 4018 if (cryptedbuf) { 4019 efree(cryptedbuf); 4020 } 4021} 4022/* }}} */ 4023 4024/* {{{ proto bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding]) 4025 Decrypts data with public key */ 4026PHP_FUNCTION(openssl_public_decrypt) 4027{ 4028 zval **key, *crypted; 4029 EVP_PKEY *pkey; 4030 int cryptedlen; 4031 unsigned char *cryptedbuf = NULL; 4032 unsigned char *crypttemp; 4033 int successful = 0; 4034 long keyresource = -1; 4035 long padding = RSA_PKCS1_PADDING; 4036 char * data; 4037 int data_len; 4038 4039 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) { 4040 return; 4041 } 4042 RETVAL_FALSE; 4043 4044 pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC); 4045 if (pkey == NULL) { 4046 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key"); 4047 RETURN_FALSE; 4048 } 4049 4050 cryptedlen = EVP_PKEY_size(pkey); 4051 crypttemp = emalloc(cryptedlen + 1); 4052 4053 switch (pkey->type) { 4054 case EVP_PKEY_RSA: 4055 case EVP_PKEY_RSA2: 4056 cryptedlen = RSA_public_decrypt(data_len, 4057 (unsigned char *)data, 4058 crypttemp, 4059 pkey->pkey.rsa, 4060 padding); 4061 if (cryptedlen != -1) { 4062 cryptedbuf = emalloc(cryptedlen + 1); 4063 memcpy(cryptedbuf, crypttemp, cryptedlen); 4064 successful = 1; 4065 } 4066 break; 4067 4068 default: 4069 php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); 4070 4071 } 4072 4073 efree(crypttemp); 4074 4075 if (successful) { 4076 zval_dtor(crypted); 4077 cryptedbuf[cryptedlen] = '\0'; 4078 ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0); 4079 cryptedbuf = NULL; 4080 RETVAL_TRUE; 4081 } 4082 4083 if (cryptedbuf) { 4084 efree(cryptedbuf); 4085 } 4086 if (keyresource == -1) { 4087 EVP_PKEY_free(pkey); 4088 } 4089} 4090/* }}} */ 4091 4092/* {{{ proto mixed openssl_error_string(void) 4093 Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */ 4094PHP_FUNCTION(openssl_error_string) 4095{ 4096 char buf[512]; 4097 unsigned long val; 4098 4099 if (zend_parse_parameters_none() == FAILURE) { 4100 return; 4101 } 4102 4103 val = ERR_get_error(); 4104 if (val) { 4105 RETURN_STRING(ERR_error_string(val, buf), 1); 4106 } else { 4107 RETURN_FALSE; 4108 } 4109} 4110/* }}} */ 4111 4112/* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, mixed method]) 4113 Signs data */ 4114PHP_FUNCTION(openssl_sign) 4115{ 4116 zval **key, *signature; 4117 EVP_PKEY *pkey; 4118 int siglen; 4119 unsigned char *sigbuf; 4120 long keyresource = -1; 4121 char * data; 4122 int data_len; 4123 EVP_MD_CTX md_ctx; 4124 zval *method = NULL; 4125 long signature_algo = OPENSSL_ALGO_SHA1; 4126 const EVP_MD *mdtype; 4127 4128 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|z", &data, &data_len, &signature, &key, &method) == FAILURE) { 4129 return; 4130 } 4131 pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC); 4132 if (pkey == NULL) { 4133 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a private key"); 4134 RETURN_FALSE; 4135 } 4136 4137 if (method == NULL || Z_TYPE_P(method) == IS_LONG) { 4138 if (method != NULL) { 4139 signature_algo = Z_LVAL_P(method); 4140 } 4141 mdtype = php_openssl_get_evp_md_from_algo(signature_algo); 4142 } else if (Z_TYPE_P(method) == IS_STRING) { 4143 mdtype = EVP_get_digestbyname(Z_STRVAL_P(method)); 4144 } else { 4145 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); 4146 RETURN_FALSE; 4147 } 4148 if (!mdtype) { 4149 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); 4150 RETURN_FALSE; 4151 } 4152 4153 siglen = EVP_PKEY_size(pkey); 4154 sigbuf = emalloc(siglen + 1); 4155 4156 EVP_SignInit(&md_ctx, mdtype); 4157 EVP_SignUpdate(&md_ctx, data, data_len); 4158 if (EVP_SignFinal (&md_ctx, sigbuf,(unsigned int *)&siglen, pkey)) { 4159 zval_dtor(signature); 4160 sigbuf[siglen] = '\0'; 4161 ZVAL_STRINGL(signature, (char *)sigbuf, siglen, 0); 4162 RETVAL_TRUE; 4163 } else { 4164 efree(sigbuf); 4165 RETVAL_FALSE; 4166 } 4167 EVP_MD_CTX_cleanup(&md_ctx); 4168 if (keyresource == -1) { 4169 EVP_PKEY_free(pkey); 4170 } 4171} 4172/* }}} */ 4173 4174/* {{{ proto int openssl_verify(string data, string signature, mixed key[, mixed method]) 4175 Verifys data */ 4176PHP_FUNCTION(openssl_verify) 4177{ 4178 zval **key; 4179 EVP_PKEY *pkey; 4180 int err; 4181 EVP_MD_CTX md_ctx; 4182 const EVP_MD *mdtype; 4183 long keyresource = -1; 4184 char * data; int data_len; 4185 char * signature; int signature_len; 4186 zval *method = NULL; 4187 long signature_algo = OPENSSL_ALGO_SHA1; 4188 4189 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|z", &data, &data_len, &signature, &signature_len, &key, &method) == FAILURE) { 4190 return; 4191 } 4192 4193 if (method == NULL || Z_TYPE_P(method) == IS_LONG) { 4194 if (method != NULL) { 4195 signature_algo = Z_LVAL_P(method); 4196 } 4197 mdtype = php_openssl_get_evp_md_from_algo(signature_algo); 4198 } else if (Z_TYPE_P(method) == IS_STRING) { 4199 mdtype = EVP_get_digestbyname(Z_STRVAL_P(method)); 4200 } else { 4201 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); 4202 RETURN_FALSE; 4203 } 4204 if (!mdtype) { 4205 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); 4206 RETURN_FALSE; 4207 } 4208 4209 pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC); 4210 if (pkey == NULL) { 4211 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a public key"); 4212 RETURN_FALSE; 4213 } 4214 4215 EVP_VerifyInit (&md_ctx, mdtype); 4216 EVP_VerifyUpdate (&md_ctx, data, data_len); 4217 err = EVP_VerifyFinal (&md_ctx, (unsigned char *)signature, signature_len, pkey); 4218 EVP_MD_CTX_cleanup(&md_ctx); 4219 4220 if (keyresource == -1) { 4221 EVP_PKEY_free(pkey); 4222 } 4223 RETURN_LONG(err); 4224} 4225/* }}} */ 4226 4227/* {{{ proto int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys) 4228 Seals data */ 4229PHP_FUNCTION(openssl_seal) 4230{ 4231 zval *pubkeys, **pubkey, *sealdata, *ekeys; 4232 HashTable *pubkeysht; 4233 HashPosition pos; 4234 EVP_PKEY **pkeys; 4235 long * key_resources; /* so we know what to cleanup */ 4236 int i, len1, len2, *eksl, nkeys; 4237 unsigned char *buf = NULL, **eks; 4238 char * data; int data_len; 4239 char *method =NULL; 4240 int method_len = 0; 4241 const EVP_CIPHER *cipher; 4242 EVP_CIPHER_CTX ctx; 4243 4244 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szza/|s", &data, &data_len, &sealdata, &ekeys, &pubkeys, &method, &method_len) == FAILURE) { 4245 return; 4246 } 4247 4248 pubkeysht = HASH_OF(pubkeys); 4249 nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0; 4250 if (!nkeys) { 4251 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Fourth argument to openssl_seal() must be a non-empty array"); 4252 RETURN_FALSE; 4253 } 4254 4255 if (method) { 4256 cipher = EVP_get_cipherbyname(method); 4257 if (!cipher) { 4258 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); 4259 RETURN_FALSE; 4260 } 4261 } else { 4262 cipher = EVP_rc4(); 4263 } 4264 4265 pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0); 4266 eksl = safe_emalloc(nkeys, sizeof(*eksl), 0); 4267 eks = safe_emalloc(nkeys, sizeof(*eks), 0); 4268 memset(eks, 0, sizeof(*eks) * nkeys); 4269 key_resources = safe_emalloc(nkeys, sizeof(long), 0); 4270 memset(key_resources, 0, sizeof(*key_resources) * nkeys); 4271 4272 /* get the public keys we are using to seal this data */ 4273 zend_hash_internal_pointer_reset_ex(pubkeysht, &pos); 4274 i = 0; 4275 while (zend_hash_get_current_data_ex(pubkeysht, (void **) &pubkey, 4276 &pos) == SUCCESS) { 4277 pkeys[i] = php_openssl_evp_from_zval(pubkey, 1, NULL, 0, &key_resources[i] TSRMLS_CC); 4278 if (pkeys[i] == NULL) { 4279 php_error_docref(NULL TSRMLS_CC, E_WARNING, "not a public key (%dth member of pubkeys)", i+1); 4280 RETVAL_FALSE; 4281 goto clean_exit; 4282 } 4283 eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1); 4284 zend_hash_move_forward_ex(pubkeysht, &pos); 4285 i++; 4286 } 4287 4288 if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) { 4289 RETVAL_FALSE; 4290 goto clean_exit; 4291 } 4292 4293#if 0 4294 /* Need this if allow ciphers that require initialization vector */ 4295 ivlen = EVP_CIPHER_CTX_iv_length(&ctx); 4296 iv = ivlen ? emalloc(ivlen + 1) : NULL; 4297#endif 4298 /* allocate one byte extra to make room for \0 */ 4299 buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx)); 4300 4301 if (!EVP_SealInit(&ctx, cipher, eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { 4302 RETVAL_FALSE; 4303 efree(buf); 4304 goto clean_exit; 4305 } 4306 4307 EVP_SealFinal(&ctx, buf + len1, &len2); 4308 4309 if (len1 + len2 > 0) { 4310 zval_dtor(sealdata); 4311 buf[len1 + len2] = '\0'; 4312 buf = erealloc(buf, len1 + len2 + 1); 4313 ZVAL_STRINGL(sealdata, (char *)buf, len1 + len2, 0); 4314 4315 zval_dtor(ekeys); 4316 array_init(ekeys); 4317 for (i=0; i<nkeys; i++) { 4318 eks[i][eksl[i]] = '\0'; 4319 add_next_index_stringl(ekeys, erealloc(eks[i], eksl[i] + 1), eksl[i], 0); 4320 eks[i] = NULL; 4321 } 4322#if 0 4323 /* If allow ciphers that need IV, we need this */ 4324 zval_dtor(*ivec); 4325 if (ivlen) { 4326 iv[ivlen] = '\0'; 4327 ZVAL_STRINGL(*ivec, erealloc(iv, ivlen + 1), ivlen, 0); 4328 } else { 4329 ZVAL_EMPTY_STRING(*ivec); 4330 } 4331#endif 4332 } else { 4333 efree(buf); 4334 } 4335 RETVAL_LONG(len1 + len2); 4336 4337clean_exit: 4338 for (i=0; i<nkeys; i++) { 4339 if (key_resources[i] == -1) { 4340 EVP_PKEY_free(pkeys[i]); 4341 } 4342 if (eks[i]) { 4343 efree(eks[i]); 4344 } 4345 } 4346 efree(eks); 4347 efree(eksl); 4348 efree(pkeys); 4349 efree(key_resources); 4350} 4351/* }}} */ 4352 4353/* {{{ proto bool openssl_open(string data, &string opendata, string ekey, mixed privkey) 4354 Opens data */ 4355PHP_FUNCTION(openssl_open) 4356{ 4357 zval **privkey, *opendata; 4358 EVP_PKEY *pkey; 4359 int len1, len2; 4360 unsigned char *buf; 4361 long keyresource = -1; 4362 EVP_CIPHER_CTX ctx; 4363 char * data; int data_len; 4364 char * ekey; int ekey_len; 4365 char *method =NULL; 4366 int method_len = 0; 4367 const EVP_CIPHER *cipher; 4368 4369 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szsZ|s", &data, &data_len, &opendata, &ekey, &ekey_len, &privkey, &method, &method_len) == FAILURE) { 4370 return; 4371 } 4372 4373 pkey = php_openssl_evp_from_zval(privkey, 0, "", 0, &keyresource TSRMLS_CC); 4374 if (pkey == NULL) { 4375 php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 4 into a private key"); 4376 RETURN_FALSE; 4377 } 4378 4379 if (method) { 4380 cipher = EVP_get_cipherbyname(method); 4381 if (!cipher) { 4382 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); 4383 RETURN_FALSE; 4384 } 4385 } else { 4386 cipher = EVP_rc4(); 4387 } 4388 4389 buf = emalloc(data_len + 1); 4390 4391 if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, ekey_len, NULL, pkey) && EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { 4392 if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) { 4393 efree(buf); 4394 if (keyresource == -1) { 4395 EVP_PKEY_free(pkey); 4396 } 4397 RETURN_FALSE; 4398 } 4399 } else { 4400 efree(buf); 4401 if (keyresource == -1) { 4402 EVP_PKEY_free(pkey); 4403 } 4404 RETURN_FALSE; 4405 } 4406 if (keyresource == -1) { 4407 EVP_PKEY_free(pkey); 4408 } 4409 zval_dtor(opendata); 4410 buf[len1 + len2] = '\0'; 4411 ZVAL_STRINGL(opendata, erealloc(buf, len1 + len2 + 1), len1 + len2, 0); 4412 RETURN_TRUE; 4413} 4414/* }}} */ 4415 4416/* SSL verification functions */ 4417 4418#define GET_VER_OPT(name) (stream->context && SUCCESS == php_stream_context_get_option(stream->context, "ssl", name, &val)) 4419#define GET_VER_OPT_STRING(name, str) if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_PP(val); } 4420 4421static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */ 4422{ 4423 php_stream *stream; 4424 SSL *ssl; 4425 X509 *err_cert; 4426 int err, depth, ret; 4427 zval **val; 4428 4429 ret = preverify_ok; 4430 4431 /* determine the status for the current cert */ 4432 err_cert = X509_STORE_CTX_get_current_cert(ctx); 4433 err = X509_STORE_CTX_get_error(ctx); 4434 depth = X509_STORE_CTX_get_error_depth(ctx); 4435 4436 /* conjure the stream & context to use */ 4437 ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); 4438 stream = (php_stream*)SSL_get_ex_data(ssl, ssl_stream_data_index); 4439 4440 /* if allow_self_signed is set, make sure that verification succeeds */ 4441 if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) { 4442 ret = 1; 4443 } 4444 4445 /* check the depth */ 4446 if (GET_VER_OPT("verify_depth")) { 4447 convert_to_long_ex(val); 4448 4449 if (depth > Z_LVAL_PP(val)) { 4450 ret = 0; 4451 X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); 4452 } 4453 } 4454 4455 return ret; 4456 4457} 4458/* }}} */ 4459 4460int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC) /* {{{ */ 4461{ 4462 zval **val = NULL; 4463 char *cnmatch = NULL; 4464 X509_NAME *name; 4465 char buf[1024]; 4466 int err; 4467 4468 /* verification is turned off */ 4469 if (!(GET_VER_OPT("verify_peer") && zval_is_true(*val))) { 4470 return SUCCESS; 4471 } 4472 4473 if (peer == NULL) { 4474 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not get peer certificate"); 4475 return FAILURE; 4476 } 4477 4478 err = SSL_get_verify_result(ssl); 4479 switch (err) { 4480 case X509_V_OK: 4481 /* fine */ 4482 break; 4483 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: 4484 if (GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) { 4485 /* allowed */ 4486 break; 4487 } 4488 /* not allowed, so fall through */ 4489 default: 4490 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not verify peer: code:%d %s", err, X509_verify_cert_error_string(err)); 4491 return FAILURE; 4492 } 4493 4494 /* if the cert passed the usual checks, apply our own local policies now */ 4495 4496 name = X509_get_subject_name(peer); 4497 4498 /* Does the common name match ? (used primarily for https://) */ 4499 GET_VER_OPT_STRING("CN_match", cnmatch); 4500 if (cnmatch) { 4501 int match = 0; 4502 int name_len = X509_NAME_get_text_by_NID(name, NID_commonName, buf, sizeof(buf)); 4503 4504 if (name_len == -1) { 4505 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN"); 4506 return FAILURE; 4507 } else if (name_len != strlen(buf)) { 4508 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", name_len, buf); 4509 return FAILURE; 4510 } 4511 4512 match = strcmp(cnmatch, buf) == 0; 4513 if (!match && strlen(buf) > 3 && buf[0] == '*' && buf[1] == '.') { 4514 /* Try wildcard */ 4515 4516 if (strchr(buf+2, '.')) { 4517 char *tmp = strstr(cnmatch, buf+1); 4518 4519 match = tmp && strcmp(tmp, buf+2) && tmp == strchr(cnmatch, '.'); 4520 } 4521 } 4522 4523 if (!match) { 4524 /* didn't match */ 4525 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", name_len, buf, cnmatch); 4526 return FAILURE; 4527 } 4528 } 4529 4530 return SUCCESS; 4531} 4532/* }}} */ 4533 4534static int passwd_callback(char *buf, int num, int verify, void *data) /* {{{ */ 4535{ 4536 php_stream *stream = (php_stream *)data; 4537 zval **val = NULL; 4538 char *passphrase = NULL; 4539 /* TODO: could expand this to make a callback into PHP user-space */ 4540 4541 GET_VER_OPT_STRING("passphrase", passphrase); 4542 4543 if (passphrase) { 4544 if (Z_STRLEN_PP(val) < num - 1) { 4545 memcpy(buf, Z_STRVAL_PP(val), Z_STRLEN_PP(val)+1); 4546 return Z_STRLEN_PP(val); 4547 } 4548 } 4549 return 0; 4550} 4551/* }}} */ 4552 4553SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{ */ 4554{ 4555 zval **val = NULL; 4556 char *cafile = NULL; 4557 char *capath = NULL; 4558 char *certfile = NULL; 4559 char *cipherlist = NULL; 4560 int ok = 1; 4561 4562 ERR_clear_error(); 4563 4564 /* look at context options in the stream and set appropriate verification flags */ 4565 if (GET_VER_OPT("verify_peer") && zval_is_true(*val)) { 4566 4567 /* turn on verification callback */ 4568 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); 4569 4570 /* CA stuff */ 4571 GET_VER_OPT_STRING("cafile", cafile); 4572 GET_VER_OPT_STRING("capath", capath); 4573 4574 if (cafile || capath) { 4575 if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) { 4576 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set verify locations `%s' `%s'", cafile, capath); 4577 return NULL; 4578 } 4579 } 4580 4581 if (GET_VER_OPT("verify_depth")) { 4582 convert_to_long_ex(val); 4583 SSL_CTX_set_verify_depth(ctx, Z_LVAL_PP(val)); 4584 } 4585 } else { 4586 SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); 4587 } 4588 4589 /* callback for the passphrase (for localcert) */ 4590 if (GET_VER_OPT("passphrase")) { 4591 SSL_CTX_set_default_passwd_cb_userdata(ctx, stream); 4592 SSL_CTX_set_default_passwd_cb(ctx, passwd_callback); 4593 } 4594 4595 GET_VER_OPT_STRING("ciphers", cipherlist); 4596 if (!cipherlist) { 4597 cipherlist = "DEFAULT"; 4598 } 4599 if (SSL_CTX_set_cipher_list(ctx, cipherlist) != 1) { 4600 return NULL; 4601 } 4602 4603 GET_VER_OPT_STRING("local_cert", certfile); 4604 if (certfile) { 4605 X509 *cert = NULL; 4606 EVP_PKEY *key = NULL; 4607 SSL *tmpssl; 4608 char resolved_path_buff[MAXPATHLEN]; 4609 const char * private_key = NULL; 4610 4611 if (VCWD_REALPATH(certfile, resolved_path_buff)) { 4612 /* a certificate to use for authentication */ 4613 if (SSL_CTX_use_certificate_chain_file(ctx, resolved_path_buff) != 1) { 4614 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set local cert chain file `%s'; Check that your cafile/capath settings include details of your certificate and its issuer", certfile); 4615 return NULL; 4616 } 4617 GET_VER_OPT_STRING("local_pk", private_key); 4618 4619 if (private_key) { 4620 char resolved_path_buff_pk[MAXPATHLEN]; 4621 if (VCWD_REALPATH(private_key, resolved_path_buff_pk)) { 4622 if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff_pk, SSL_FILETYPE_PEM) != 1) { 4623 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff_pk); 4624 return NULL; 4625 } 4626 } 4627 } else { 4628 if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff, SSL_FILETYPE_PEM) != 1) { 4629 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff); 4630 return NULL; 4631 } 4632 } 4633 4634 tmpssl = SSL_new(ctx); 4635 cert = SSL_get_certificate(tmpssl); 4636 4637 if (cert) { 4638 key = X509_get_pubkey(cert); 4639 EVP_PKEY_copy_parameters(key, SSL_get_privatekey(tmpssl)); 4640 EVP_PKEY_free(key); 4641 } 4642 SSL_free(tmpssl); 4643 4644 if (!SSL_CTX_check_private_key(ctx)) { 4645 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Private key does not match certificate!"); 4646 } 4647 } 4648 } 4649 if (ok) { 4650 SSL *ssl = SSL_new(ctx); 4651 4652 if (ssl) { 4653 /* map SSL => stream */ 4654 SSL_set_ex_data(ssl, ssl_stream_data_index, stream); 4655 } 4656 return ssl; 4657 } 4658 4659 return NULL; 4660} 4661/* }}} */ 4662 4663static void openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */ 4664{ 4665 add_next_index_string((zval*)arg, (char*)name->name, 1); 4666} 4667/* }}} */ 4668 4669static void openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */ 4670{ 4671 if (name->alias == 0) { 4672 add_next_index_string((zval*)arg, (char*)name->name, 1); 4673 } 4674} 4675/* }}} */ 4676 4677/* {{{ proto array openssl_get_md_methods([bool aliases = false]) 4678 Return array of available digest methods */ 4679PHP_FUNCTION(openssl_get_md_methods) 4680{ 4681 zend_bool aliases = 0; 4682 4683 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) { 4684 return; 4685 } 4686 array_init(return_value); 4687 OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH, 4688 aliases ? openssl_add_method_or_alias: openssl_add_method, 4689 return_value); 4690} 4691/* }}} */ 4692 4693/* {{{ proto array openssl_get_cipher_methods([bool aliases = false]) 4694 Return array of available cipher methods */ 4695PHP_FUNCTION(openssl_get_cipher_methods) 4696{ 4697 zend_bool aliases = 0; 4698 4699 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) { 4700 return; 4701 } 4702 array_init(return_value); 4703 OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, 4704 aliases ? openssl_add_method_or_alias: openssl_add_method, 4705 return_value); 4706} 4707/* }}} */ 4708 4709/* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false]) 4710 Computes digest hash value for given data using given method, returns raw or binhex encoded string */ 4711PHP_FUNCTION(openssl_digest) 4712{ 4713 zend_bool raw_output = 0; 4714 char *data, *method; 4715 int data_len, method_len; 4716 const EVP_MD *mdtype; 4717 EVP_MD_CTX md_ctx; 4718 int siglen; 4719 unsigned char *sigbuf; 4720 4721 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) { 4722 return; 4723 } 4724 mdtype = EVP_get_digestbyname(method); 4725 if (!mdtype) { 4726 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm"); 4727 RETURN_FALSE; 4728 } 4729 4730 siglen = EVP_MD_size(mdtype); 4731 sigbuf = emalloc(siglen + 1); 4732 4733 EVP_DigestInit(&md_ctx, mdtype); 4734 EVP_DigestUpdate(&md_ctx, (unsigned char *)data, data_len); 4735 if (EVP_DigestFinal (&md_ctx, (unsigned char *)sigbuf, (unsigned int *)&siglen)) { 4736 if (raw_output) { 4737 sigbuf[siglen] = '\0'; 4738 RETVAL_STRINGL((char *)sigbuf, siglen, 0); 4739 } else { 4740 int digest_str_len = siglen * 2; 4741 char *digest_str = emalloc(digest_str_len + 1); 4742 4743 make_digest_ex(digest_str, sigbuf, siglen); 4744 efree(sigbuf); 4745 RETVAL_STRINGL(digest_str, digest_str_len, 0); 4746 } 4747 } else { 4748 efree(sigbuf); 4749 RETVAL_FALSE; 4750 } 4751} 4752/* }}} */ 4753 4754static zend_bool php_openssl_validate_iv(char **piv, int *piv_len, int iv_required_len TSRMLS_DC) 4755{ 4756 char *iv_new; 4757 4758 /* Best case scenario, user behaved */ 4759 if (*piv_len == iv_required_len) { 4760 return 0; 4761 } 4762 4763 iv_new = ecalloc(1, iv_required_len + 1); 4764 4765 if (*piv_len <= 0) { 4766 /* BC behavior */ 4767 *piv_len = iv_required_len; 4768 *piv = iv_new; 4769 return 1; 4770 } 4771 4772 if (*piv_len < iv_required_len) { 4773 php_error_docref(NULL TSRMLS_CC, E_WARNING, "IV passed is only %d bytes long, cipher expects an IV of precisely %d bytes, padding with \\0", *piv_len, iv_required_len); 4774 memcpy(iv_new, *piv, *piv_len); 4775 *piv_len = iv_required_len; 4776 *piv = iv_new; 4777 return 1; 4778 } 4779 4780 php_error_docref(NULL TSRMLS_CC, E_WARNING, "IV passed is %d bytes long which is longer than the %d expected by selected cipher, truncating", *piv_len, iv_required_len); 4781 memcpy(iv_new, *piv, iv_required_len); 4782 *piv_len = iv_required_len; 4783 *piv = iv_new; 4784 return 1; 4785 4786} 4787 4788/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv='']]) 4789 Encrypts given data with given method and key, returns raw or base64 encoded string */ 4790PHP_FUNCTION(openssl_encrypt) 4791{ 4792 long options = 0; 4793 char *data, *method, *password, *iv = ""; 4794 int data_len, method_len, password_len, iv_len = 0, max_iv_len; 4795 const EVP_CIPHER *cipher_type; 4796 EVP_CIPHER_CTX cipher_ctx; 4797 int i=0, outlen, keylen; 4798 unsigned char *outbuf, *key; 4799 zend_bool free_iv; 4800 4801 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) { 4802 return; 4803 } 4804 cipher_type = EVP_get_cipherbyname(method); 4805 if (!cipher_type) { 4806 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); 4807 RETURN_FALSE; 4808 } 4809 4810 keylen = EVP_CIPHER_key_length(cipher_type); 4811 if (keylen > password_len) { 4812 key = emalloc(keylen); 4813 memset(key, 0, keylen); 4814 memcpy(key, password, password_len); 4815 } else { 4816 key = (unsigned char*)password; 4817 } 4818 4819 max_iv_len = EVP_CIPHER_iv_length(cipher_type); 4820 if (iv_len <= 0 && max_iv_len > 0) { 4821 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended"); 4822 } 4823 free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len TSRMLS_CC); 4824 4825 outlen = data_len + EVP_CIPHER_block_size(cipher_type); 4826 outbuf = emalloc(outlen + 1); 4827 4828 EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL); 4829 if (password_len > keylen) { 4830 EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len); 4831 } 4832 EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv); 4833 if (options & OPENSSL_ZERO_PADDING) { 4834 EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0); 4835 } 4836 if (data_len > 0) { 4837 EVP_EncryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len); 4838 } 4839 outlen = i; 4840 if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) { 4841 outlen += i; 4842 if (options & OPENSSL_RAW_DATA) { 4843 outbuf[outlen] = '\0'; 4844 RETVAL_STRINGL((char *)outbuf, outlen, 0); 4845 } else { 4846 int base64_str_len; 4847 char *base64_str; 4848 4849 base64_str = (char*)php_base64_encode(outbuf, outlen, &base64_str_len); 4850 efree(outbuf); 4851 RETVAL_STRINGL(base64_str, base64_str_len, 0); 4852 } 4853 } else { 4854 efree(outbuf); 4855 RETVAL_FALSE; 4856 } 4857 if (key != (unsigned char*)password) { 4858 efree(key); 4859 } 4860 if (free_iv) { 4861 efree(iv); 4862 } 4863 EVP_CIPHER_CTX_cleanup(&cipher_ctx); 4864} 4865/* }}} */ 4866 4867/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = '']]) 4868 Takes raw or base64 encoded string and dectupt it using given method and key */ 4869PHP_FUNCTION(openssl_decrypt) 4870{ 4871 long options = 0; 4872 char *data, *method, *password, *iv = ""; 4873 int data_len, method_len, password_len, iv_len = 0; 4874 const EVP_CIPHER *cipher_type; 4875 EVP_CIPHER_CTX cipher_ctx; 4876 int i, outlen, keylen; 4877 unsigned char *outbuf, *key; 4878 int base64_str_len; 4879 char *base64_str = NULL; 4880 zend_bool free_iv; 4881 4882 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) { 4883 return; 4884 } 4885 4886 if (!method_len) { 4887 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); 4888 RETURN_FALSE; 4889 } 4890 4891 cipher_type = EVP_get_cipherbyname(method); 4892 if (!cipher_type) { 4893 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); 4894 RETURN_FALSE; 4895 } 4896 4897 if (!(options & OPENSSL_RAW_DATA)) { 4898 base64_str = (char*)php_base64_decode((unsigned char*)data, data_len, &base64_str_len); 4899 if (!base64_str) { 4900 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to base64 decode the input"); 4901 RETURN_FALSE; 4902 } 4903 data_len = base64_str_len; 4904 data = base64_str; 4905 } 4906 4907 keylen = EVP_CIPHER_key_length(cipher_type); 4908 if (keylen > password_len) { 4909 key = emalloc(keylen); 4910 memset(key, 0, keylen); 4911 memcpy(key, password, password_len); 4912 } else { 4913 key = (unsigned char*)password; 4914 } 4915 4916 free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type) TSRMLS_CC); 4917 4918 outlen = data_len + EVP_CIPHER_block_size(cipher_type); 4919 outbuf = emalloc(outlen + 1); 4920 4921 EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL); 4922 if (password_len > keylen) { 4923 EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len); 4924 } 4925 EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv); 4926 if (options & OPENSSL_ZERO_PADDING) { 4927 EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0); 4928 } 4929 EVP_DecryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len); 4930 outlen = i; 4931 if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) { 4932 outlen += i; 4933 outbuf[outlen] = '\0'; 4934 RETVAL_STRINGL((char *)outbuf, outlen, 0); 4935 } else { 4936 efree(outbuf); 4937 RETVAL_FALSE; 4938 } 4939 if (key != (unsigned char*)password) { 4940 efree(key); 4941 } 4942 if (free_iv) { 4943 efree(iv); 4944 } 4945 if (base64_str) { 4946 efree(base64_str); 4947 } 4948 EVP_CIPHER_CTX_cleanup(&cipher_ctx); 4949} 4950/* }}} */ 4951 4952/* {{{ proto int openssl_cipher_iv_length(string $method) */ 4953PHP_FUNCTION(openssl_cipher_iv_length) 4954{ 4955 char *method; 4956 int method_len; 4957 const EVP_CIPHER *cipher_type; 4958 4959 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len) == FAILURE) { 4960 return; 4961 } 4962 4963 if (!method_len) { 4964 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); 4965 RETURN_FALSE; 4966 } 4967 4968 cipher_type = EVP_get_cipherbyname(method); 4969 if (!cipher_type) { 4970 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); 4971 RETURN_FALSE; 4972 } 4973 4974 RETURN_LONG(EVP_CIPHER_iv_length(cipher_type)); 4975} 4976/* }}} */ 4977 4978 4979/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key) 4980 Computes shared sicret for public value of remote DH key and local DH key */ 4981PHP_FUNCTION(openssl_dh_compute_key) 4982{ 4983 zval *key; 4984 char *pub_str; 4985 int pub_len; 4986 EVP_PKEY *pkey; 4987 BIGNUM *pub; 4988 char *data; 4989 int len; 4990 4991 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr", &pub_str, &pub_len, &key) == FAILURE) { 4992 return; 4993 } 4994 ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key); 4995 if (!pkey || EVP_PKEY_type(pkey->type) != EVP_PKEY_DH || !pkey->pkey.dh) { 4996 RETURN_FALSE; 4997 } 4998 4999 pub = BN_bin2bn((unsigned char*)pub_str, pub_len, NULL); 5000 5001 data = emalloc(DH_size(pkey->pkey.dh) + 1); 5002 len = DH_compute_key((unsigned char*)data, pub, pkey->pkey.dh); 5003 5004 if (len >= 0) { 5005 data[len] = 0; 5006 RETVAL_STRINGL(data, len, 0); 5007 } else { 5008 efree(data); 5009 RETVAL_FALSE; 5010 } 5011 5012 BN_free(pub); 5013} 5014/* }}} */ 5015 5016/* {{{ proto string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result]) 5017 Returns a string of the length specified filled with random pseudo bytes */ 5018PHP_FUNCTION(openssl_random_pseudo_bytes) 5019{ 5020 long buffer_length; 5021 unsigned char *buffer = NULL; 5022 zval *zstrong_result_returned = NULL; 5023 int strong_result = 0; 5024 5025 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) { 5026 return; 5027 } 5028 5029 if (buffer_length <= 0) { 5030 RETURN_FALSE; 5031 } 5032 5033 if (zstrong_result_returned) { 5034 zval_dtor(zstrong_result_returned); 5035 ZVAL_BOOL(zstrong_result_returned, 0); 5036 } 5037 5038 buffer = emalloc(buffer_length + 1); 5039 5040#ifdef PHP_WIN32 5041 strong_result = 1; 5042 /* random/urandom equivalent on Windows */ 5043 if (php_win32_get_random_bytes(buffer, (size_t) buffer_length) == FAILURE){ 5044 efree(buffer); 5045 if (zstrong_result_returned) { 5046 ZVAL_BOOL(zstrong_result_returned, 0); 5047 } 5048 RETURN_FALSE; 5049 } 5050#else 5051 if ((strong_result = RAND_pseudo_bytes(buffer, buffer_length)) < 0) { 5052 efree(buffer); 5053 if (zstrong_result_returned) { 5054 ZVAL_BOOL(zstrong_result_returned, 0); 5055 } 5056 RETURN_FALSE; 5057 } 5058#endif 5059 5060 buffer[buffer_length] = 0; 5061 RETVAL_STRINGL((char *)buffer, buffer_length, 0); 5062 5063 if (zstrong_result_returned) { 5064 ZVAL_BOOL(zstrong_result_returned, strong_result); 5065 } 5066} 5067/* }}} */ 5068 5069/* 5070 * Local variables: 5071 * tab-width: 8 5072 * c-basic-offset: 8 5073 * End: 5074 * vim600: sw=4 ts=4 fdm=marker 5075 * vim<600: sw=4 ts=4 5076 */ 5077