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