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