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