diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-03-14 05:42:27 +0000 |
---|---|---|
committer | <> | 2013-04-03 16:25:08 +0000 |
commit | c4dd7a1a684490673e25aaf4fabec5df138854c4 (patch) | |
tree | 4d57c44caae4480efff02b90b9be86f44bf25409 /ext/openssl | |
download | php2-master.tar.gz |
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/openssl')
73 files changed, 9635 insertions, 0 deletions
diff --git a/ext/openssl/CREDITS b/ext/openssl/CREDITS new file mode 100644 index 0000000..c2f50d6 --- /dev/null +++ b/ext/openssl/CREDITS @@ -0,0 +1,2 @@ +OpenSSL +Stig Venaas, Wez Furlong, Sascha Kettler diff --git a/ext/openssl/README b/ext/openssl/README new file mode 100644 index 0000000..20a3833 --- /dev/null +++ b/ext/openssl/README @@ -0,0 +1,322 @@ +OpenSSL extension for PHP + +$Id$ + +The functions implemented so far make it possible to seal and open data, and +also create and verify signatures. + +NEW: support for S/MIME encrypt/decrypt/sign/verify, as well as more +flexibility for specifying certificates/keys. + +To enable the extension, configure PHP with --with-openssl. + +Specifying keys/certificates +---------------------------- + +Most of the functions require a key or a certificate as a parameter; to make +things easy for you to use openssl, this extension allows you +to specify certificates in the following way: + +1. As an X.509 resource returned from openssl_x509_read +2. As a string in the format file://filename, where filename is the path to the + certificate file (it will be opened and read automatically) +3. As a string containing the data from the certificate file + +Similarly, you can use the following methods of specifying a public key: + +1. As a key resource returned from openssl_get_publickey +2. An X509 resource - public key only +3. As a string in the format file://filename +4. As a string containing the data from the key file + +Additionally, for a private key, when the openssl extension function does not +allow you to enter the passphrase as a parameter you may use the syntax +array($key, "passphrase") where $key can be a key specified using one of the +methods listed above. + +Certificate Verification +------------------------ +When calling a function that will verify a signature/certificate, the cainfo +parameter is an array containing file and directory names that specifiy the +locations of trusted CA files. If a directory is specified, then it must be a +correctly hashed directory. + +Misc: +----- + +mixed openssl_error_string() + +returns the message from the last error that the OpenSSL library encountered +and moves it's internal error pointer to the next message. If there are no +more error messages, returns false. + +General Key/Cert Functions: +--------------------------- + +resource openssl_get_privatekey(mixed key [, string passphrase]) + +Parses the key data and returns a key resource identifier. If the key is +encrypted a passphrase is needed. This can be supplied as second argument. + + +resource openssl_get_publickey(mixed cert) + +Extracts the public key from the given certificate or public key and returns +a key resource identifier. + + +void openssl_free_key(resource key) + +Frees the resource given by the key resource identifier. +Note that this function does not accept the extended key specification +syntax mentioned above, as it doesn't make sense in this case! + +array openssl_x509_parse(mixed x509[, bool shortnames=true]) + +Parses the certificate data and returns an array containing information +about the certificate, it's intended purposes, subject, issuer, validity +etc. etc. If shortnames is true (the default) then the fields will be +keyed by the shortname forms eg: CN as opposed to commonName (shortnames += false). + + +bool openssl_x509_checkpurpose(mixed x509cert, int purpose, + array cainfo[, string untrustedfile]) + +Verifies if the certificate can be used for a specific purpose. +Purpose can be one of the following values: + X509_PURPOSE_SSL_CLIENT + X509_PURPOSE_SSL_SERVER + X509_PURPOSE_NS_SSL_SERVER + X509_PURPOSE_SMIME_SIGN + X509_PURPOSE_SMIME_ENCRYPT + X509_PURPOSE_CRL_SIGN + X509_PURPOSE_ANY + +cainfo is an array of CA information (as mentioned above). +untrusted file specifies a file containing a bunch of certs that +are not trusted but may be useful in validating the certificate. + + +resource openssl_read_x509(mixed cert) + +Parses the cert and returns a resource that can be used with the +other openssl functions + + +void openssl_free_x509(resource x509) + +Frees the resource given by the x509 resource identifier. +Note that this function does not accept the extended cert specification +syntax mentioned above, as it doesn't make sense in this case! + + +PKCS7 (S/MIME) Sign/Verify/Encrypt/Decrypt Functions: +----------------------------------------------------- + +These functions allow you to manipulate S/MIME messages! + +They are based on apps/smime.c from the openssl dist, so for information, +see the documentation for openssl. + +You may pass in some flags that affect how these functions work using +and array containing the following values: +"detached", "nodetached", "text", "nointern", "noverify", "nochain", +"nocerts", "noattr", "binary", "nosigs". +The options correspond to the options of the same name for the +"openssl smime" command (smime(1)). + + +bool openssl_pkcs7_verify(string filename, array flags[, string signerscerts][, + array cainfo]) + +Verifies that the signature on the MIME message contained in the file +named by filename is valid. If signerscerts is passed in, it holds the +name of a file into which the certificates of those that signed the +message will be stored. +cainfo and flags are CA information and flag information as described +above. + + +bool openssl_pkcs7_encrypt(string infile, string outfile, array recipcerts, + array headers[, array flags]) + +Encrypts the MIME message contained in the file named by infile using +the certificates held in recipcerts. The result is place in the file +named outfile. +recipcerts is an array of certificate identifiers representing the certs +of the intended recipients of the message. +headers is an array of headers to prepend to the message: they will +not be included in the encoded section. +flags is flag information as described above. +Hint: you will want to put "To", "From", and "Subject" headers in headers. +Headers can be either an assoc array keyed by header named, or can be +and indexed array containing a single header line per value. +The message will be encoded using a RC2-40 bit cipher. +TODO: allow user to specify cipher. + +bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed + signkey, array headers[, array flags][, string extracertsfilename]) + +Signs the MIME message contained in the file named by infile using the +certificate and key pair identified by signcert/signkey. +Signkey must be the private key corresponding to signcert. +The result is placed in the file named by outfile. +Headers and flags have the same effects as mentioned above. +extracertsfilename names a file containing a bunch of additional certificates +to include in the signature, in order to aid the recipient in verifying the +message. + + +bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed + recipcert, mixed recipkey) + +Decrypts the MIME message contained in the file named by infilename +using the certificate and private key pair recipcert/recipkey. +The descrypted result is placed in outfilename. +TODO: add flags parameter, if needed? + + +EVP Sign/Verify/Encrypt/Decrypt Functions: +------------------------------------------ + +bool openssl_sign(string data, &string signature, mixed key) + +Uses key to create signature for data, returns true on success and false +on failure. signature is passed by reference and contains the newly created +signature on success. + + +int openssl_verify(string data, string signature, mixed key) + +Uses key to verify that the signature is correct for the given data. +Returns 1 if correct, 0 if incorrect, and -1 on error. + + +int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys) + +Encrypts data using pubkeys, so that only owners of the respective private +keys and ekeys can decrypt and read the data. Returns the length of the +sealed data on success, else false. On success, sealdata and ekeys hold +the sealed data and envelope keys. + + +bool openssl_open(string data, &string opendata, string ekey, int privkey) + +Opens (decrypts) sealed data using a private key and the corresponding +envelope key. Returns true on success and false on failure. On success, +opendata will hold the descypted data. + + +See below for more details on usage. Also feel free to mail me at +venaas@php.net if you have questions. The OpenSSL documentation, +especially the EVP documentation at +http://www.openssl.org/docs/crypto/evp.html, might also be of help. + + +HOWTO: + +To do anything you need a private key and a certificate containing the +corresponding public key. This is similar to what you have using say an +Apache webserver with OpenSSL. For testing you could try keys that come +with OpenSSL, that's what the sample scripts below do. You can also get +keys from some CA, or you can create them yourself. + + +Creating private key + +To generate an unprotected 1024 bit RSA private key you can do + + openssl genrsa -out /tmp/test.key 1024 + +Private keys should be protected by a passphrase though. + + +Creating a self signed certificate + +To generate a self signed certificate from the key that is valid for +365 days, do + + openssl req -new -key /tmp/test.key -out /tmp/test.crt -days 365 -x509 + + +Example usage + +These examples use keys that come with OpenSSL, you should perhaps test with +those first. + + +Seal and open + + <?php + $data = "Follow the white rabbit"; + + // Get certificate into a string + // this file comes with OpenSSL 0.9.6 + $fp = fopen("/src/openssl-0.9.6/demos/maurice/cert.pem", "r"); + $cert = fread($fp, 8192); + fclose($fp); + // get public key from certificate + $pk1 = openssl_get_publickey($cert); + // $pk1 is an encryption key resource id if success, else false + // Repeat if want public keys for multiple parties + + $fp = fopen("/src/openssl-0.9.6/demos/sign/cert.pem", "r"); + $cert = fread($fp, 8192); + fclose($fp); + $pk2 = openssl_get_publickey($cert); + + // seal data, only owners of $pk1 and $pk2 can decrypt $sealed with keys + // $ekeys[0] and $ekeys[1] respectively. + openssl_seal($data, $sealed, $ekeys, array($pk1,$pk2)); + openssl_free_key($pk1); + openssl_free_key($pk2); + + // now we try to decrypt data for one of the recipients + $fp = fopen("/src/openssl-0.9.6/demos/sign/key.pem", "r"); + // Get PEM coded key into $pkey + $pkey = fread($fp, 8192); + fclose($fp); + // $key will be resource id for unpacked $pkey + $key = openssl_get_privatekey($pkey); + + openssl_open($sealed, $open, $ekeys[1], $key); + openssl_free_key($key); + echo "$open\n"; + ?> + + +Sign and verify + + <?php + $data = "Follow the white rabbit"; + + // First we need to have a string containing the private key in PEM format + // this file comes with OpenSSL 0.9.6 + $fp = fopen("/src/openssl-0.9.6/demos/sign/key.pem", "r"); + $pkey = fread($fp, 8192); + fclose($fp); + + // get private key from the PEM format + // $key is an encr key resource id if success, else false + $key = openssl_get_privatekey($pkey); + + // calculate signature + openssl_sign($data, $signature, $key); + openssl_free_key($key); + + // recipient verifies signature + // read certificate + $fp = fopen("/src/openssl-0.9.6/demos/sign/cert.pem", "r"); + $cert = fread($fp, 8192); + fclose($fp); + + // Get public key from the certificate + $pubkey = openssl_get_publickey($cert); + + // state whether signature is okay or not + echo openssl_verify($data, $signature, $pubkey) == 1 ? "ok\n" : "bad\n"; + + // free key + openssl_free_key($pubkey); + ?> diff --git a/ext/openssl/config.w32 b/ext/openssl/config.w32 new file mode 100644 index 0000000..49edb06 --- /dev/null +++ b/ext/openssl/config.w32 @@ -0,0 +1,18 @@ +// $Id$ +// vim:ft=javascript + +ARG_WITH("openssl", "OpenSSL support", "no"); + +if (PHP_OPENSSL != "no") { + if (CHECK_LIB("ssleay32.lib", "openssl", PHP_OPENSSL) && + CHECK_LIB("libeay32.lib", "openssl", PHP_OPENSSL) && + CHECK_HEADER_ADD_INCLUDE("openssl/ssl.h", "CFLAGS_OPENSSL")) { + EXTENSION("openssl", "openssl.c xp_ssl.c"); + + AC_DEFINE("HAVE_OPENSSL_EXT", PHP_OPENSSL_SHARED ? 0 : 1, "Have openssl"); + AC_DEFINE("HAVE_OPENSSL", 1); + AC_DEFINE("HAVE_DSA_DEFAULT_METHOD", 1); + } +} + + diff --git a/ext/openssl/config0.m4 b/ext/openssl/config0.m4 new file mode 100644 index 0000000..2c7f4fb --- /dev/null +++ b/ext/openssl/config0.m4 @@ -0,0 +1,28 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_WITH(openssl, for OpenSSL support, +[ --with-openssl[=DIR] Include OpenSSL support (requires OpenSSL >= 0.9.6)]) + +PHP_ARG_WITH(kerberos, for Kerberos support, +[ --with-kerberos[=DIR] OPENSSL: Include Kerberos support], no, no) + +if test "$PHP_OPENSSL" != "no"; then + PHP_NEW_EXTENSION(openssl, openssl.c xp_ssl.c, $ext_shared) + PHP_SUBST(OPENSSL_SHARED_LIBADD) + + if test "$PHP_KERBEROS" != "no"; then + PHP_SETUP_KERBEROS(OPENSSL_SHARED_LIBADD) + fi + + AC_CHECK_LIB(ssl, DSA_get_default_method, AC_DEFINE(HAVE_DSA_DEFAULT_METHOD, 1, [OpenSSL 0.9.7 or later])) + AC_CHECK_LIB(crypto, X509_free, AC_DEFINE(HAVE_DSA_DEFAULT_METHOD, 1, [OpenSSL 0.9.7 or later])) + + PHP_SETUP_OPENSSL(OPENSSL_SHARED_LIBADD, + [ + AC_DEFINE(HAVE_OPENSSL_EXT,1,[ ]) + ], [ + AC_MSG_ERROR([OpenSSL check failed. Please check config.log for more information.]) + ]) +fi diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c new file mode 100644 index 0000000..d7ac117 --- /dev/null +++ b/ext/openssl/openssl.c @@ -0,0 +1,5015 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Stig Venaas <venaas@php.net> | + | Wez Furlong <wez@thebrainroom.com> | + | Sascha Kettler <kettler@gmx.net> | + | Pierre-Alain Joye <pierre@php.net> | + | Marc Delling <delling@silpion.de> (PKCS12 functions) | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_openssl.h" + +/* PHP Includes */ +#include "ext/standard/file.h" +#include "ext/standard/info.h" +#include "ext/standard/php_fopen_wrappers.h" +#include "ext/standard/md5.h" +#include "ext/standard/base64.h" + +#if PHP_WIN32 +# include "win32/winutil.h" +#endif + +/* OpenSSL includes */ +#include <openssl/evp.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/crypto.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/conf.h> +#include <openssl/rand.h> +#include <openssl/ssl.h> +#include <openssl/pkcs12.h> + +/* Common */ +#include <time.h> + +#ifdef NETWARE +#define timezone _timezone /* timezone is called _timezone in LibC */ +#endif + +#define DEFAULT_KEY_LENGTH 512 +#define MIN_KEY_LENGTH 384 + +#define OPENSSL_ALGO_SHA1 1 +#define OPENSSL_ALGO_MD5 2 +#define OPENSSL_ALGO_MD4 3 +#ifdef HAVE_OPENSSL_MD2_H +#define OPENSSL_ALGO_MD2 4 +#endif +#define OPENSSL_ALGO_DSS1 5 +#if OPENSSL_VERSION_NUMBER >= 0x0090708fL +#define OPENSSL_ALGO_SHA224 6 +#define OPENSSL_ALGO_SHA256 7 +#define OPENSSL_ALGO_SHA384 8 +#define OPENSSL_ALGO_SHA512 9 +#define OPENSSL_ALGO_RMD160 10 +#endif +#define DEBUG_SMIME 0 + +/* FIXME: Use the openssl constants instead of + * enum. It is now impossible to match real values + * against php constants. Also sorry to break the + * enum principles here, BC... + */ +enum php_openssl_key_type { + OPENSSL_KEYTYPE_RSA, + OPENSSL_KEYTYPE_DSA, + OPENSSL_KEYTYPE_DH, + OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA, +#ifdef EVP_PKEY_EC + OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1 +#endif +}; + +enum php_openssl_cipher_type { + PHP_OPENSSL_CIPHER_RC2_40, + PHP_OPENSSL_CIPHER_RC2_128, + PHP_OPENSSL_CIPHER_RC2_64, + PHP_OPENSSL_CIPHER_DES, + PHP_OPENSSL_CIPHER_3DES, + PHP_OPENSSL_CIPHER_AES_128_CBC, + PHP_OPENSSL_CIPHER_AES_192_CBC, + PHP_OPENSSL_CIPHER_AES_256_CBC, + + PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_RC2_40 +}; + +PHP_FUNCTION(openssl_get_md_methods); +PHP_FUNCTION(openssl_get_cipher_methods); + +PHP_FUNCTION(openssl_digest); +PHP_FUNCTION(openssl_encrypt); +PHP_FUNCTION(openssl_decrypt); +PHP_FUNCTION(openssl_cipher_iv_length); + +PHP_FUNCTION(openssl_dh_compute_key); +PHP_FUNCTION(openssl_random_pseudo_bytes); + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 0, 2) + ZEND_ARG_INFO(0, x509) + ZEND_ARG_INFO(0, outfilename) + ZEND_ARG_INFO(0, notext) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export, 0, 0, 2) + ZEND_ARG_INFO(0, x509) + ZEND_ARG_INFO(1, out) + ZEND_ARG_INFO(0, notext) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_check_private_key, 0) + ZEND_ARG_INFO(0, cert) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_parse, 0) + ZEND_ARG_INFO(0, x509) + ZEND_ARG_INFO(0, shortname) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_checkpurpose, 0, 0, 3) + ZEND_ARG_INFO(0, x509cert) + ZEND_ARG_INFO(0, purpose) + ZEND_ARG_INFO(0, cainfo) /* array */ + ZEND_ARG_INFO(0, untrustedfile) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_read, 0) + ZEND_ARG_INFO(0, cert) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_free, 0) + ZEND_ARG_INFO(0, x509) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs12_export_to_file, 0, 0, 4) + ZEND_ARG_INFO(0, x509) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, priv_key) + ZEND_ARG_INFO(0, pass) + ZEND_ARG_INFO(0, args) /* array */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_export, 0) + ZEND_ARG_INFO(0, x509) + ZEND_ARG_INFO(1, out) + ZEND_ARG_INFO(0, priv_key) + ZEND_ARG_INFO(0, pass) + ZEND_ARG_INFO(0, args) /* array */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_read, 0) + ZEND_ARG_INFO(0, PKCS12) + ZEND_ARG_INFO(1, certs) /* array */ + ZEND_ARG_INFO(0, pass) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export_to_file, 0, 0, 2) + ZEND_ARG_INFO(0, csr) + ZEND_ARG_INFO(0, outfilename) + ZEND_ARG_INFO(0, notext) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export, 0, 0, 2) + ZEND_ARG_INFO(0, csr) + ZEND_ARG_INFO(1, out) + ZEND_ARG_INFO(0, notext) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_sign, 0, 0, 4) + ZEND_ARG_INFO(0, csr) + ZEND_ARG_INFO(0, x509) + ZEND_ARG_INFO(0, priv_key) + ZEND_ARG_INFO(0, days) + ZEND_ARG_INFO(0, config_args) /* array */ + ZEND_ARG_INFO(0, serial) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_new, 0, 0, 2) + ZEND_ARG_INFO(0, dn) /* array */ + ZEND_ARG_INFO(1, privkey) + ZEND_ARG_INFO(0, configargs) + ZEND_ARG_INFO(0, extraattribs) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_csr_get_subject, 0) + ZEND_ARG_INFO(0, csr) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_csr_get_public_key, 0) + ZEND_ARG_INFO(0, csr) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_new, 0, 0, 0) + ZEND_ARG_INFO(0, configargs) /* array */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export_to_file, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, outfilename) + ZEND_ARG_INFO(0, passphrase) + ZEND_ARG_INFO(0, config_args) /* array */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(1, out) + ZEND_ARG_INFO(0, passphrase) + ZEND_ARG_INFO(0, config_args) /* array */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_public, 0) + ZEND_ARG_INFO(0, cert) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_free, 0) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_get_private, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, passphrase) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_details, 0) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, signerscerts) + ZEND_ARG_INFO(0, cainfo) /* array */ + ZEND_ARG_INFO(0, extracerts) + ZEND_ARG_INFO(0, content) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_encrypt, 0, 0, 4) + ZEND_ARG_INFO(0, infile) + ZEND_ARG_INFO(0, outfile) + ZEND_ARG_INFO(0, recipcerts) + ZEND_ARG_INFO(0, headers) /* array */ + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, cipher) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_sign, 0, 0, 5) + ZEND_ARG_INFO(0, infile) + ZEND_ARG_INFO(0, outfile) + ZEND_ARG_INFO(0, signcert) + ZEND_ARG_INFO(0, signkey) + ZEND_ARG_INFO(0, headers) /* array */ + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, extracertsfilename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_decrypt, 0, 0, 3) + ZEND_ARG_INFO(0, infilename) + ZEND_ARG_INFO(0, outfilename) + ZEND_ARG_INFO(0, recipcert) + ZEND_ARG_INFO(0, recipkey) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_encrypt, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(1, crypted) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, padding) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_decrypt, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(1, crypted) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, padding) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_encrypt, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(1, crypted) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, padding) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_decrypt, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(1, crypted) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, padding) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_error_string, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_sign, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(1, signature) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_verify, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, signature) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_seal, 0) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(1, sealdata) + ZEND_ARG_INFO(1, ekeys) /* arary */ + ZEND_ARG_INFO(0, pubkeys) /* array */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_open, 0) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(1, opendata) + ZEND_ARG_INFO(0, ekey) + ZEND_ARG_INFO(0, privkey) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_md_methods, 0, 0, 0) + ZEND_ARG_INFO(0, aliases) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_cipher_methods, 0, 0, 0) + ZEND_ARG_INFO(0, aliases) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_digest, 0, 0, 2) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, raw_output) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_encrypt, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, iv) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, iv) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_cipher_iv_length, 0) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_openssl_dh_compute_key, 0) + ZEND_ARG_INFO(0, pub_key) + ZEND_ARG_INFO(0, dh_key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_random_pseudo_bytes, 0, 0, 1) + ZEND_ARG_INFO(0, length) + ZEND_ARG_INFO(1, result_is_strong) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ openssl_functions[] + */ +const zend_function_entry openssl_functions[] = { +/* public/private key functions */ + PHP_FE(openssl_pkey_free, arginfo_openssl_pkey_free) + PHP_FE(openssl_pkey_new, arginfo_openssl_pkey_new) + PHP_FE(openssl_pkey_export, arginfo_openssl_pkey_export) + PHP_FE(openssl_pkey_export_to_file, arginfo_openssl_pkey_export_to_file) + PHP_FE(openssl_pkey_get_private, arginfo_openssl_pkey_get_private) + PHP_FE(openssl_pkey_get_public, arginfo_openssl_pkey_get_public) + PHP_FE(openssl_pkey_get_details, arginfo_openssl_pkey_get_details) + + PHP_FALIAS(openssl_free_key, openssl_pkey_free, arginfo_openssl_pkey_free) + PHP_FALIAS(openssl_get_privatekey, openssl_pkey_get_private, arginfo_openssl_pkey_get_private) + PHP_FALIAS(openssl_get_publickey, openssl_pkey_get_public, arginfo_openssl_pkey_get_public) + +/* x.509 cert funcs */ + PHP_FE(openssl_x509_read, arginfo_openssl_x509_read) + PHP_FE(openssl_x509_free, arginfo_openssl_x509_free) + PHP_FE(openssl_x509_parse, arginfo_openssl_x509_parse) + PHP_FE(openssl_x509_checkpurpose, arginfo_openssl_x509_checkpurpose) + PHP_FE(openssl_x509_check_private_key, arginfo_openssl_x509_check_private_key) + PHP_FE(openssl_x509_export, arginfo_openssl_x509_export) + PHP_FE(openssl_x509_export_to_file, arginfo_openssl_x509_export_to_file) + +/* PKCS12 funcs */ + PHP_FE(openssl_pkcs12_export, arginfo_openssl_pkcs12_export) + PHP_FE(openssl_pkcs12_export_to_file, arginfo_openssl_pkcs12_export_to_file) + PHP_FE(openssl_pkcs12_read, arginfo_openssl_pkcs12_read) + +/* CSR funcs */ + PHP_FE(openssl_csr_new, arginfo_openssl_csr_new) + PHP_FE(openssl_csr_export, arginfo_openssl_csr_export) + PHP_FE(openssl_csr_export_to_file, arginfo_openssl_csr_export_to_file) + PHP_FE(openssl_csr_sign, arginfo_openssl_csr_sign) + PHP_FE(openssl_csr_get_subject, arginfo_openssl_csr_get_subject) + PHP_FE(openssl_csr_get_public_key, arginfo_openssl_csr_get_public_key) + + PHP_FE(openssl_digest, arginfo_openssl_digest) + PHP_FE(openssl_encrypt, arginfo_openssl_encrypt) + PHP_FE(openssl_decrypt, arginfo_openssl_decrypt) + PHP_FE(openssl_cipher_iv_length, arginfo_openssl_cipher_iv_length) + PHP_FE(openssl_sign, arginfo_openssl_sign) + PHP_FE(openssl_verify, arginfo_openssl_verify) + PHP_FE(openssl_seal, arginfo_openssl_seal) + PHP_FE(openssl_open, arginfo_openssl_open) + +/* for S/MIME handling */ + PHP_FE(openssl_pkcs7_verify, arginfo_openssl_pkcs7_verify) + PHP_FE(openssl_pkcs7_decrypt, arginfo_openssl_pkcs7_decrypt) + PHP_FE(openssl_pkcs7_sign, arginfo_openssl_pkcs7_sign) + PHP_FE(openssl_pkcs7_encrypt, arginfo_openssl_pkcs7_encrypt) + + PHP_FE(openssl_private_encrypt, arginfo_openssl_private_encrypt) + PHP_FE(openssl_private_decrypt, arginfo_openssl_private_decrypt) + PHP_FE(openssl_public_encrypt, arginfo_openssl_public_encrypt) + PHP_FE(openssl_public_decrypt, arginfo_openssl_public_decrypt) + + PHP_FE(openssl_get_md_methods, arginfo_openssl_get_md_methods) + PHP_FE(openssl_get_cipher_methods, arginfo_openssl_get_cipher_methods) + + PHP_FE(openssl_dh_compute_key, arginfo_openssl_dh_compute_key) + + PHP_FE(openssl_random_pseudo_bytes, arginfo_openssl_random_pseudo_bytes) + PHP_FE(openssl_error_string, arginfo_openssl_error_string) + PHP_FE_END +}; +/* }}} */ + +/* {{{ openssl_module_entry + */ +zend_module_entry openssl_module_entry = { + STANDARD_MODULE_HEADER, + "openssl", + openssl_functions, + PHP_MINIT(openssl), + PHP_MSHUTDOWN(openssl), + NULL, + NULL, + PHP_MINFO(openssl), + NO_VERSION_YET, + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_OPENSSL +ZEND_GET_MODULE(openssl) +#endif + +static int le_key; +static int le_x509; +static int le_csr; +static int ssl_stream_data_index; + +int php_openssl_get_x509_list_id(void) /* {{{ */ +{ + return le_x509; +} +/* }}} */ + +/* {{{ resource destructors */ +static void php_pkey_free(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + EVP_PKEY *pkey = (EVP_PKEY *)rsrc->ptr; + + assert(pkey != NULL); + + EVP_PKEY_free(pkey); +} + +static void php_x509_free(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + X509 *x509 = (X509 *)rsrc->ptr; + X509_free(x509); +} + +static void php_csr_free(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + X509_REQ * csr = (X509_REQ*)rsrc->ptr; + X509_REQ_free(csr); +} +/* }}} */ + +/* {{{ openssl open_basedir check */ +inline static int php_openssl_open_base_dir_chk(char *filename TSRMLS_DC) +{ + if (php_check_open_basedir(filename TSRMLS_CC)) { + return -1; + } + + return 0; +} +/* }}} */ + +/* openssl -> PHP "bridging" */ +/* true global; readonly after module startup */ +static char default_ssl_conf_filename[MAXPATHLEN]; + +struct php_x509_request { /* {{{ */ +#if OPENSSL_VERSION_NUMBER >= 0x10000002L + LHASH_OF(CONF_VALUE) * global_config; /* Global SSL config */ + LHASH_OF(CONF_VALUE) * req_config; /* SSL config for this request */ +#else + LHASH * global_config; /* Global SSL config */ + LHASH * req_config; /* SSL config for this request */ +#endif + const EVP_MD * md_alg; + const EVP_MD * digest; + char * section_name, + * config_filename, + * digest_name, + * extensions_section, + * request_extensions_section; + int priv_key_bits; + int priv_key_type; + + int priv_key_encrypt; + + EVP_PKEY * priv_key; + + const EVP_CIPHER * priv_key_encrypt_cipher; +}; +/* }}} */ + +static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC); +static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC); +static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC); +static X509_STORE * setup_verify(zval * calist TSRMLS_DC); +static STACK_OF(X509) * load_all_certs_from_file(char *certfile); +static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC); +static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC); + +static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname TSRMLS_DC) /* {{{ */ +{ + zval *subitem, *subentries; + int i, j = -1, last = -1, obj_cnt = 0; + char *sname; + int nid; + X509_NAME_ENTRY * ne; + ASN1_STRING * str = NULL; + ASN1_OBJECT * obj; + + if (key != NULL) { + MAKE_STD_ZVAL(subitem); + array_init(subitem); + } else { + subitem = val; + } + + for (i = 0; i < X509_NAME_entry_count(name); i++) { + unsigned char *to_add; + int to_add_len; + + + ne = X509_NAME_get_entry(name, i); + obj = X509_NAME_ENTRY_get_object(ne); + nid = OBJ_obj2nid(obj); + obj_cnt = 0; + + if (shortname) { + sname = (char *) OBJ_nid2sn(nid); + } else { + sname = (char *) OBJ_nid2ln(nid); + } + + MAKE_STD_ZVAL(subentries); + array_init(subentries); + + last = -1; + for (;;) { + j = X509_NAME_get_index_by_OBJ(name, obj, last); + if (j < 0) { + if (last != -1) break; + } else { + obj_cnt++; + ne = X509_NAME_get_entry(name, j); + str = X509_NAME_ENTRY_get_data(ne); + if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) { + to_add_len = ASN1_STRING_to_UTF8(&to_add, str); + if (to_add_len != -1) { + add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1); + } + } else { + to_add = ASN1_STRING_data(str); + to_add_len = ASN1_STRING_length(str); + add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1); + } + } + last = j; + } + i = last; + + if (obj_cnt > 1) { + add_assoc_zval_ex(subitem, sname, strlen(sname) + 1, subentries); + } else { + zval_dtor(subentries); + FREE_ZVAL(subentries); + if (obj_cnt && str && to_add_len > -1) { + add_assoc_stringl(subitem, sname, (char *)to_add, to_add_len, 1); + } + } + } + if (key != NULL) { + zend_hash_update(HASH_OF(val), key, strlen(key) + 1, (void *)&subitem, sizeof(subitem), NULL); + } +} +/* }}} */ + +static void add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) /* {{{ */ +{ + add_assoc_stringl(val, key, (char *)str->data, str->length, 1); +} +/* }}} */ + +static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC) /* {{{ */ +{ +/* + This is how the time string is formatted: + + snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100, + ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec); +*/ + + time_t ret; + struct tm thetime; + char * strbuf; + char * thestr; + long gmadjust = 0; + + if (timestr->length < 13) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "extension author too lazy to parse %s correctly", timestr->data); + return (time_t)-1; + } + + strbuf = estrdup((char *)timestr->data); + + memset(&thetime, 0, sizeof(thetime)); + + /* we work backwards so that we can use atoi more easily */ + + thestr = strbuf + timestr->length - 3; + + thetime.tm_sec = atoi(thestr); + *thestr = '\0'; + thestr -= 2; + thetime.tm_min = atoi(thestr); + *thestr = '\0'; + thestr -= 2; + thetime.tm_hour = atoi(thestr); + *thestr = '\0'; + thestr -= 2; + thetime.tm_mday = atoi(thestr); + *thestr = '\0'; + thestr -= 2; + thetime.tm_mon = atoi(thestr)-1; + *thestr = '\0'; + thestr -= 2; + thetime.tm_year = atoi(thestr); + + if (thetime.tm_year < 68) { + thetime.tm_year += 100; + } + + thetime.tm_isdst = -1; + ret = mktime(&thetime); + +#if HAVE_TM_GMTOFF + gmadjust = thetime.tm_gmtoff; +#else + /* + ** If correcting for daylight savings time, we set the adjustment to + ** the value of timezone - 3600 seconds. Otherwise, we need to overcorrect and + ** set the adjustment to the main timezone + 3600 seconds. + */ + gmadjust = -(thetime.tm_isdst ? (long)timezone - 3600 : (long)timezone + 3600); +#endif + ret += gmadjust; + + efree(strbuf); + + return ret; +} +/* }}} */ + +#if OPENSSL_VERSION_NUMBER >= 0x10000002L +static 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) /* {{{ */ +#else +static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH * config TSRMLS_DC) +#endif +{ + X509V3_CTX ctx; + + X509V3_set_ctx_test(&ctx); + X509V3_set_conf_lhash(&ctx, config); + if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading %s section %s of %s", + section_label, + section, + config_filename); + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + +static int add_oid_section(struct php_x509_request * req TSRMLS_DC) /* {{{ */ +{ + char * str; + STACK_OF(CONF_VALUE) * sktmp; + CONF_VALUE * cnf; + int i; + + str = CONF_get_string(req->req_config, NULL, "oid_section"); + if (str == NULL) { + return SUCCESS; + } + sktmp = CONF_get_section(req->req_config, str); + if (sktmp == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "problem loading oid section %s", str); + return FAILURE; + } + for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) { + cnf = sk_CONF_VALUE_value(sktmp, i); + if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "problem creating object %s=%s", cnf->name, cnf->value); + return FAILURE; + } + } + return SUCCESS; +} +/* }}} */ + +#define PHP_SSL_REQ_INIT(req) memset(req, 0, sizeof(*req)) +#define PHP_SSL_REQ_DISPOSE(req) php_openssl_dispose_config(req TSRMLS_CC) +#define PHP_SSL_REQ_PARSE(req, zval) php_openssl_parse_config(req, zval TSRMLS_CC) + +#define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \ + req->config_filename, req->var, req->req_config TSRMLS_CC) == FAILURE) return FAILURE + +#define SET_OPTIONAL_STRING_ARG(key, varname, defval) \ + if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS) \ + varname = Z_STRVAL_PP(item); \ + else \ + varname = defval + +#define SET_OPTIONAL_LONG_ARG(key, varname, defval) \ + if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS) \ + varname = Z_LVAL_PP(item); \ + else \ + varname = defval + +static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo); + + +static int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args TSRMLS_DC) /* {{{ */ +{ + char * str; + zval ** item; + + SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename); + SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req"); + req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL); + req->req_config = CONF_load(NULL, req->config_filename, NULL); + + if (req->req_config == NULL) { + return FAILURE; + } + + /* read in the oids */ + str = CONF_get_string(req->req_config, NULL, "oid_file"); + if (str && !php_openssl_open_base_dir_chk(str TSRMLS_CC)) { + BIO *oid_bio = BIO_new_file(str, "r"); + if (oid_bio) { + OBJ_create_objects(oid_bio); + BIO_free(oid_bio); + } + } + if (add_oid_section(req TSRMLS_CC) == FAILURE) { + return FAILURE; + } + SET_OPTIONAL_STRING_ARG("digest_alg", req->digest_name, + CONF_get_string(req->req_config, req->section_name, "default_md")); + SET_OPTIONAL_STRING_ARG("x509_extensions", req->extensions_section, + CONF_get_string(req->req_config, req->section_name, "x509_extensions")); + SET_OPTIONAL_STRING_ARG("req_extensions", req->request_extensions_section, + CONF_get_string(req->req_config, req->section_name, "req_extensions")); + SET_OPTIONAL_LONG_ARG("private_key_bits", req->priv_key_bits, + CONF_get_number(req->req_config, req->section_name, "default_bits")); + + SET_OPTIONAL_LONG_ARG("private_key_type", req->priv_key_type, OPENSSL_KEYTYPE_DEFAULT); + + if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), "encrypt_key", sizeof("encrypt_key"), (void**)&item) == SUCCESS) { + req->priv_key_encrypt = Z_BVAL_PP(item); + } else { + str = CONF_get_string(req->req_config, req->section_name, "encrypt_rsa_key"); + if (str == NULL) { + str = CONF_get_string(req->req_config, req->section_name, "encrypt_key"); + } + if (str && strcmp(str, "no") == 0) { + req->priv_key_encrypt = 0; + } else { + req->priv_key_encrypt = 1; + } + } + + 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) { + long cipher_algo = Z_LVAL_PP(item); + const EVP_CIPHER* cipher = php_openssl_get_evp_cipher_from_algo(cipher_algo); + if (cipher == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm for private key."); + return FAILURE; + } else { + req->priv_key_encrypt_cipher = cipher; + } + } else { + req->priv_key_encrypt_cipher = NULL; + } + + + + /* digest alg */ + if (req->digest_name == NULL) { + req->digest_name = CONF_get_string(req->req_config, req->section_name, "default_md"); + } + if (req->digest_name) { + req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name); + } + if (req->md_alg == NULL) { + req->md_alg = req->digest = EVP_md5(); + } + + PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section); + + /* set the string mask */ + str = CONF_get_string(req->req_config, req->section_name, "string_mask"); + if (str && !ASN1_STRING_set_default_mask_asc(str)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid global string mask setting %s", str); + return FAILURE; + } + + PHP_SSL_CONFIG_SYNTAX_CHECK(request_extensions_section); + + return SUCCESS; +} +/* }}} */ + +static void php_openssl_dispose_config(struct php_x509_request * req TSRMLS_DC) /* {{{ */ +{ + if (req->priv_key) { + EVP_PKEY_free(req->priv_key); + req->priv_key = NULL; + } + if (req->global_config) { + CONF_free(req->global_config); + req->global_config = NULL; + } + if (req->req_config) { + CONF_free(req->req_config); + req->req_config = NULL; + } +} +/* }}} */ + +static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded TSRMLS_DC) /* {{{ */ +{ + char buffer[MAXPATHLEN]; + + *egdsocket = 0; + *seeded = 0; + + if (file == NULL) { + file = RAND_file_name(buffer, sizeof(buffer)); + } else if (RAND_egd(file) > 0) { + /* if the given filename is an EGD socket, don't + * write anything back to it */ + *egdsocket = 1; + return SUCCESS; + } + if (file == NULL || !RAND_load_file(file, -1)) { + if (RAND_status() == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to load random state; not enough random data!"); + return FAILURE; + } + return FAILURE; + } + *seeded = 1; + return SUCCESS; +} +/* }}} */ + +static int php_openssl_write_rand_file(const char * file, int egdsocket, int seeded) /* {{{ */ +{ + char buffer[MAXPATHLEN]; + + TSRMLS_FETCH(); + + if (egdsocket || !seeded) { + /* if we did not manage to read the seed file, we should not write + * a low-entropy seed file back */ + return FAILURE; + } + if (file == NULL) { + file = RAND_file_name(buffer, sizeof(buffer)); + } + if (file == NULL || !RAND_write_file(file)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to write random state"); + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + +static EVP_MD * php_openssl_get_evp_md_from_algo(long algo) { /* {{{ */ + EVP_MD *mdtype; + + switch (algo) { + case OPENSSL_ALGO_SHA1: + mdtype = (EVP_MD *) EVP_sha1(); + break; + case OPENSSL_ALGO_MD5: + mdtype = (EVP_MD *) EVP_md5(); + break; + case OPENSSL_ALGO_MD4: + mdtype = (EVP_MD *) EVP_md4(); + break; +#ifdef HAVE_OPENSSL_MD2_H + case OPENSSL_ALGO_MD2: + mdtype = (EVP_MD *) EVP_md2(); + break; +#endif + case OPENSSL_ALGO_DSS1: + mdtype = (EVP_MD *) EVP_dss1(); + break; +#if OPENSSL_VERSION_NUMBER >= 0x0090708fL + case OPENSSL_ALGO_SHA224: + mdtype = (EVP_MD *) EVP_sha224(); + break; + case OPENSSL_ALGO_SHA256: + mdtype = (EVP_MD *) EVP_sha256(); + break; + case OPENSSL_ALGO_SHA384: + mdtype = (EVP_MD *) EVP_sha384(); + break; + case OPENSSL_ALGO_SHA512: + mdtype = (EVP_MD *) EVP_sha512(); + break; + case OPENSSL_ALGO_RMD160: + mdtype = (EVP_MD *) EVP_ripemd160(); + break; +#endif + default: + return NULL; + break; + } + return mdtype; +} +/* }}} */ + +static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo) { /* {{{ */ + switch (algo) { +#ifndef OPENSSL_NO_RC2 + case PHP_OPENSSL_CIPHER_RC2_40: + return EVP_rc2_40_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_64: + return EVP_rc2_64_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_128: + return EVP_rc2_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_DES + case PHP_OPENSSL_CIPHER_DES: + return EVP_des_cbc(); + break; + case PHP_OPENSSL_CIPHER_3DES: + return EVP_des_ede3_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_AES + case PHP_OPENSSL_CIPHER_AES_128_CBC: + return EVP_aes_128_cbc(); + break; + case PHP_OPENSSL_CIPHER_AES_192_CBC: + return EVP_aes_192_cbc(); + break; + case PHP_OPENSSL_CIPHER_AES_256_CBC: + return EVP_aes_256_cbc(); + break; +#endif + + + default: + return NULL; + break; + } +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(openssl) +{ + char * config_filename; + + le_key = zend_register_list_destructors_ex(php_pkey_free, NULL, "OpenSSL key", module_number); + le_x509 = zend_register_list_destructors_ex(php_x509_free, NULL, "OpenSSL X.509", module_number); + le_csr = zend_register_list_destructors_ex(php_csr_free, NULL, "OpenSSL X.509 CSR", module_number); + + SSL_library_init(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); + OpenSSL_add_all_algorithms(); + + SSL_load_error_strings(); + + /* register a resource id number with OpenSSL so that we can map SSL -> stream structures in + * OpenSSL callbacks */ + ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL); + + REGISTER_STRING_CONSTANT("OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER, CONST_CS|CONST_PERSISTENT); + + /* purposes for cert purpose checking */ + REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_CLIENT", X509_PURPOSE_SSL_CLIENT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_SERVER", X509_PURPOSE_SSL_SERVER, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("X509_PURPOSE_NS_SSL_SERVER", X509_PURPOSE_NS_SSL_SERVER, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_SIGN", X509_PURPOSE_SMIME_SIGN, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_ENCRYPT", X509_PURPOSE_SMIME_ENCRYPT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("X509_PURPOSE_CRL_SIGN", X509_PURPOSE_CRL_SIGN, CONST_CS|CONST_PERSISTENT); +#ifdef X509_PURPOSE_ANY + REGISTER_LONG_CONSTANT("X509_PURPOSE_ANY", X509_PURPOSE_ANY, CONST_CS|CONST_PERSISTENT); +#endif + + /* signature algorithm constants */ + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA1", OPENSSL_ALGO_SHA1, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD5", OPENSSL_ALGO_MD5, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD4", OPENSSL_ALGO_MD4, CONST_CS|CONST_PERSISTENT); +#ifdef HAVE_OPENSSL_MD2_H + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD2", OPENSSL_ALGO_MD2, CONST_CS|CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT); +#if OPENSSL_VERSION_NUMBER >= 0x0090708fL + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA224", OPENSSL_ALGO_SHA224, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA256", OPENSSL_ALGO_SHA256, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA384", OPENSSL_ALGO_SHA384, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA512", OPENSSL_ALGO_SHA512, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_RMD160", OPENSSL_ALGO_RMD160, CONST_CS|CONST_PERSISTENT); +#endif + + /* flags for S/MIME */ + REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PKCS7_TEXT", PKCS7_TEXT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PKCS7_NOINTERN", PKCS7_NOINTERN, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PKCS7_NOVERIFY", PKCS7_NOVERIFY, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PKCS7_NOCHAIN", PKCS7_NOCHAIN, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PKCS7_NOCERTS", PKCS7_NOCERTS, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PKCS7_NOATTR", PKCS7_NOATTR, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PKCS7_BINARY", PKCS7_BINARY, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PKCS7_NOSIGS", PKCS7_NOSIGS, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_PADDING", RSA_PKCS1_PADDING, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING", RSA_SSLV23_PADDING, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_NO_PADDING", RSA_NO_PADDING, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING, CONST_CS|CONST_PERSISTENT); + + /* Ciphers */ +#ifndef OPENSSL_NO_RC2 + REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_40", PHP_OPENSSL_CIPHER_RC2_40, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_128", PHP_OPENSSL_CIPHER_RC2_128, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_64", PHP_OPENSSL_CIPHER_RC2_64, CONST_CS|CONST_PERSISTENT); +#endif +#ifndef OPENSSL_NO_DES + REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_DES", PHP_OPENSSL_CIPHER_DES, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_3DES", PHP_OPENSSL_CIPHER_3DES, CONST_CS|CONST_PERSISTENT); +#endif +#ifndef OPENSSL_NO_AES + REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_128_CBC", PHP_OPENSSL_CIPHER_AES_128_CBC, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_192_CBC", PHP_OPENSSL_CIPHER_AES_192_CBC, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_256_CBC", PHP_OPENSSL_CIPHER_AES_256_CBC, CONST_CS|CONST_PERSISTENT); +#endif + + /* Values for key types */ + REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_RSA", OPENSSL_KEYTYPE_RSA, CONST_CS|CONST_PERSISTENT); +#ifndef NO_DSA + REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DSA", OPENSSL_KEYTYPE_DSA, CONST_CS|CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH", OPENSSL_KEYTYPE_DH, CONST_CS|CONST_PERSISTENT); +#ifdef EVP_PKEY_EC + REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_EC", OPENSSL_KEYTYPE_EC, CONST_CS|CONST_PERSISTENT); +#endif + + REGISTER_LONG_CONSTANT("OPENSSL_RAW_DATA", OPENSSL_RAW_DATA, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ZERO_PADDING", OPENSSL_ZERO_PADDING, CONST_CS|CONST_PERSISTENT); + +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) + /* SNI support included in OpenSSL >= 0.9.8j */ + REGISTER_LONG_CONSTANT("OPENSSL_TLSEXT_SERVER_NAME", 1, CONST_CS|CONST_PERSISTENT); +#endif + + /* Determine default SSL configuration file */ + config_filename = getenv("OPENSSL_CONF"); + if (config_filename == NULL) { + config_filename = getenv("SSLEAY_CONF"); + } + + /* default to 'openssl.cnf' if no environment variable is set */ + if (config_filename == NULL) { + snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename), "%s/%s", + X509_get_default_cert_area(), + "openssl.cnf"); + } else { + strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename)); + } + + php_stream_xport_register("ssl", php_openssl_ssl_socket_factory TSRMLS_CC); + php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory TSRMLS_CC); +#ifndef OPENSSL_NO_SSL2 + php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory TSRMLS_CC); +#endif + php_stream_xport_register("tls", php_openssl_ssl_socket_factory TSRMLS_CC); + + /* override the default tcp socket provider */ + php_stream_xport_register("tcp", php_openssl_ssl_socket_factory TSRMLS_CC); + + php_register_url_stream_wrapper("https", &php_stream_http_wrapper TSRMLS_CC); + php_register_url_stream_wrapper("ftps", &php_stream_ftp_wrapper TSRMLS_CC); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(openssl) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "OpenSSL support", "enabled"); + php_info_print_table_row(2, "OpenSSL Library Version", SSLeay_version(SSLEAY_VERSION)); + php_info_print_table_row(2, "OpenSSL Header Version", OPENSSL_VERSION_TEXT); + php_info_print_table_end(); +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(openssl) +{ + EVP_cleanup(); + + php_unregister_url_stream_wrapper("https" TSRMLS_CC); + php_unregister_url_stream_wrapper("ftps" TSRMLS_CC); + + php_stream_xport_unregister("ssl" TSRMLS_CC); +#ifndef OPENSSL_NO_SSL2 + php_stream_xport_unregister("sslv2" TSRMLS_CC); +#endif + php_stream_xport_unregister("sslv3" TSRMLS_CC); + php_stream_xport_unregister("tls" TSRMLS_CC); + + /* reinstate the default tcp handler */ + php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC); + + return SUCCESS; +} +/* }}} */ + +/* {{{ x509 cert functions */ + +/* {{{ php_openssl_x509_from_zval + Given a zval, coerce it into an X509 object. + The zval can be: + . X509 resource created using openssl_read_x509() + . if it starts with file:// then it will be interpreted as the path to that cert + . it will be interpreted as the cert data + If you supply makeresource, the result will be registered as an x509 resource and + it's value returned in makeresource. +*/ +static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC) +{ + X509 *cert = NULL; + + if (resourceval) { + *resourceval = -1; + } + if (Z_TYPE_PP(val) == IS_RESOURCE) { + /* is it an x509 resource ? */ + void * what; + int type; + + what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509", &type, 1, le_x509); + if (!what) { + return NULL; + } + /* this is so callers can decide if they should free the X509 */ + if (resourceval) { + *resourceval = Z_LVAL_PP(val); + } + if (type == le_x509) { + return (X509*)what; + } + /* other types could be used here - eg: file pointers and read in the data from them */ + + return NULL; + } + + if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) { + return NULL; + } + + /* force it to be a string and check if it refers to a file */ + convert_to_string_ex(val); + + if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) { + /* read cert from the named file */ + BIO *in; + + if (php_openssl_open_base_dir_chk(Z_STRVAL_PP(val) + (sizeof("file://") - 1) TSRMLS_CC)) { + return NULL; + } + + in = BIO_new_file(Z_STRVAL_PP(val) + (sizeof("file://") - 1), "r"); + if (in == NULL) { + return NULL; + } + cert = PEM_read_bio_X509(in, NULL, NULL, NULL); + BIO_free(in); + } else { + BIO *in; + + in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + if (in == NULL) { + return NULL; + } +#ifdef TYPEDEF_D2I_OF + cert = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL); +#else + cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL); +#endif + BIO_free(in); + } + + if (cert && makeresource && resourceval) { + *resourceval = zend_list_insert(cert, le_x509 TSRMLS_CC); + } + return cert; +} + +/* }}} */ + +/* {{{ proto bool openssl_x509_export_to_file(mixed x509, string outfilename [, bool notext = true]) + Exports a CERT to file or a var */ +PHP_FUNCTION(openssl_x509_export_to_file) +{ + X509 * cert; + zval ** zcert; + zend_bool notext = 1; + BIO * bio_out; + long certresource; + char * filename; + int filename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zp|b", &zcert, &filename, &filename_len, ¬ext) == FAILURE) { + return; + } + RETVAL_FALSE; + + cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1"); + return; + } + + if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { + return; + } + + bio_out = BIO_new_file(filename, "w"); + if (bio_out) { + if (!notext) { + X509_print(bio_out, cert); + } + PEM_write_bio_X509(bio_out, cert); + + RETVAL_TRUE; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename); + } + if (certresource == -1 && cert) { + X509_free(cert); + } + BIO_free(bio_out); +} +/* }}} */ + +/* {{{ proto bool openssl_x509_export(mixed x509, string &out [, bool notext = true]) + Exports a CERT to file or a var */ +PHP_FUNCTION(openssl_x509_export) +{ + X509 * cert; + zval ** zcert, *zout; + zend_bool notext = 1; + BIO * bio_out; + long certresource; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|b", &zcert, &zout, ¬ext) == FAILURE) { + return; + } + RETVAL_FALSE; + + cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1"); + return; + } + + bio_out = BIO_new(BIO_s_mem()); + if (!notext) { + X509_print(bio_out, cert); + } + if (PEM_write_bio_X509(bio_out, cert)) { + BUF_MEM *bio_buf; + + zval_dtor(zout); + BIO_get_mem_ptr(bio_out, &bio_buf); + ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1); + + RETVAL_TRUE; + } + + if (certresource == -1 && cert) { + X509_free(cert); + } + BIO_free(bio_out); +} +/* }}} */ + +/* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key) + Checks if a private key corresponds to a CERT */ +PHP_FUNCTION(openssl_x509_check_private_key) +{ + zval ** zcert, **zkey; + X509 * cert = NULL; + EVP_PKEY * key = NULL; + long certresource = -1, keyresource = -1; + + RETVAL_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ", &zcert, &zkey) == FAILURE) { + return; + } + cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + RETURN_FALSE; + } + key = php_openssl_evp_from_zval(zkey, 0, "", 1, &keyresource TSRMLS_CC); + if (key) { + RETVAL_BOOL(X509_check_private_key(cert, key)); + } + + if (keyresource == -1 && key) { + EVP_PKEY_free(key); + } + if (certresource == -1 && cert) { + X509_free(cert); + } +} +/* }}} */ + +/* {{{ proto array openssl_x509_parse(mixed x509 [, bool shortnames=true]) + Returns an array of the fields/values of the CERT */ +PHP_FUNCTION(openssl_x509_parse) +{ + zval ** zcert; + X509 * cert = NULL; + long certresource = -1; + int i; + zend_bool useshortnames = 1; + char * tmpstr; + zval * subitem; + X509_EXTENSION *extension; + char *extname; + BIO *bio_out; + BUF_MEM *bio_buf; + char buf[256]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcert, &useshortnames) == FAILURE) { + return; + } + cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + RETURN_FALSE; + } + array_init(return_value); + + if (cert->name) { + add_assoc_string(return_value, "name", cert->name, 1); + } +/* add_assoc_bool(return_value, "valid", cert->valid); */ + + add_assoc_name_entry(return_value, "subject", X509_get_subject_name(cert), useshortnames TSRMLS_CC); + /* hash as used in CA directories to lookup cert by subject name */ + { + char buf[32]; + snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert)); + add_assoc_string(return_value, "hash", buf, 1); + } + + add_assoc_name_entry(return_value, "issuer", X509_get_issuer_name(cert), useshortnames TSRMLS_CC); + add_assoc_long(return_value, "version", X509_get_version(cert)); + + add_assoc_string(return_value, "serialNumber", i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(cert)), 1); + + add_assoc_asn1_string(return_value, "validFrom", X509_get_notBefore(cert)); + add_assoc_asn1_string(return_value, "validTo", X509_get_notAfter(cert)); + + add_assoc_long(return_value, "validFrom_time_t", asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC)); + add_assoc_long(return_value, "validTo_time_t", asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC)); + + tmpstr = (char *)X509_alias_get0(cert, NULL); + if (tmpstr) { + add_assoc_string(return_value, "alias", tmpstr, 1); + } +/* + add_assoc_long(return_value, "signaturetypeLONG", X509_get_signature_type(cert)); + add_assoc_string(return_value, "signaturetype", OBJ_nid2sn(X509_get_signature_type(cert)), 1); + add_assoc_string(return_value, "signaturetypeLN", OBJ_nid2ln(X509_get_signature_type(cert)), 1); +*/ + MAKE_STD_ZVAL(subitem); + array_init(subitem); + + /* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines + in x509v3.h */ + for (i = 0; i < X509_PURPOSE_get_count(); i++) { + int id, purpset; + char * pname; + X509_PURPOSE * purp; + zval * subsub; + + MAKE_STD_ZVAL(subsub); + array_init(subsub); + + purp = X509_PURPOSE_get0(i); + id = X509_PURPOSE_get_id(purp); + + purpset = X509_check_purpose(cert, id, 0); + add_index_bool(subsub, 0, purpset); + + purpset = X509_check_purpose(cert, id, 1); + add_index_bool(subsub, 1, purpset); + + pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp); + add_index_string(subsub, 2, pname, 1); + + /* NOTE: if purpset > 1 then it's a warning - we should mention it ? */ + + add_index_zval(subitem, id, subsub); + } + add_assoc_zval(return_value, "purposes", subitem); + + MAKE_STD_ZVAL(subitem); + array_init(subitem); + + + for (i = 0; i < X509_get_ext_count(cert); i++) { + extension = X509_get_ext(cert, i); + if (OBJ_obj2nid(X509_EXTENSION_get_object(extension)) != NID_undef) { + extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension))); + } else { + OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1); + extname = buf; + } + bio_out = BIO_new(BIO_s_mem()); + if (X509V3_EXT_print(bio_out, extension, 0, 0)) { + BIO_get_mem_ptr(bio_out, &bio_buf); + add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1); + } else { + add_assoc_asn1_string(subitem, extname, X509_EXTENSION_get_data(extension)); + } + BIO_free(bio_out); + } + add_assoc_zval(return_value, "extensions", subitem); + + if (certresource == -1 && cert) { + X509_free(cert); + } +} +/* }}} */ + +/* {{{ load_all_certs_from_file */ +static STACK_OF(X509) * load_all_certs_from_file(char *certfile) +{ + STACK_OF(X509_INFO) *sk=NULL; + STACK_OF(X509) *stack=NULL, *ret=NULL; + BIO *in=NULL; + X509_INFO *xi; + TSRMLS_FETCH(); + + if(!(stack = sk_X509_new_null())) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure"); + goto end; + } + + if (php_openssl_open_base_dir_chk(certfile TSRMLS_CC)) { + sk_X509_free(stack); + goto end; + } + + if(!(in=BIO_new_file(certfile, "r"))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening the file, %s", certfile); + sk_X509_free(stack); + goto end; + } + + /* This loads from a file, a stack of x509/crl/pkey sets */ + if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error reading the file, %s", certfile); + sk_X509_free(stack); + goto end; + } + + /* scan over it and pull out the certs */ + while (sk_X509_INFO_num(sk)) { + xi=sk_X509_INFO_shift(sk); + if (xi->x509 != NULL) { + sk_X509_push(stack,xi->x509); + xi->x509=NULL; + } + X509_INFO_free(xi); + } + if(!sk_X509_num(stack)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "no certificates in file, %s", certfile); + sk_X509_free(stack); + goto end; + } + ret=stack; +end: + BIO_free(in); + sk_X509_INFO_free(sk); + + return ret; +} +/* }}} */ + +/* {{{ check_cert */ +static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose) +{ + int ret=0; + X509_STORE_CTX *csc; + TSRMLS_FETCH(); + + csc = X509_STORE_CTX_new(); + if (csc == NULL) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure"); + return 0; + } + X509_STORE_CTX_init(csc, ctx, x, untrustedchain); + if(purpose >= 0) { + X509_STORE_CTX_set_purpose(csc, purpose); + } + ret = X509_verify_cert(csc); + X509_STORE_CTX_free(csc); + + return ret; +} +/* }}} */ + +/* {{{ proto int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo [, string untrustedfile]) + Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs */ +PHP_FUNCTION(openssl_x509_checkpurpose) +{ + zval ** zcert, * zcainfo = NULL; + X509_STORE * cainfo = NULL; + X509 * cert = NULL; + long certresource = -1; + STACK_OF(X509) * untrustedchain = NULL; + long purpose; + char * untrusted = NULL; + int untrusted_len = 0, ret; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zl|a!s", &zcert, &purpose, &zcainfo, &untrusted, &untrusted_len) == FAILURE) { + return; + } + + RETVAL_LONG(-1); + + if (untrusted) { + untrustedchain = load_all_certs_from_file(untrusted); + if (untrustedchain == NULL) { + goto clean_exit; + } + } + + cainfo = setup_verify(zcainfo TSRMLS_CC); + if (cainfo == NULL) { + goto clean_exit; + } + cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + goto clean_exit; + } + + ret = check_cert(cainfo, cert, untrustedchain, purpose); + if (ret != 0 && ret != 1) { + RETVAL_LONG(ret); + } else { + RETVAL_BOOL(ret); + } + +clean_exit: + if (certresource == 1 && cert) { + X509_free(cert); + } + if (cainfo) { + X509_STORE_free(cainfo); + } + if (untrustedchain) { + sk_X509_pop_free(untrustedchain, X509_free); + } +} +/* }}} */ + +/* {{{ setup_verify + * calist is an array containing file and directory names. create a + * certificate store and add those certs to it for use in verification. +*/ +static X509_STORE * setup_verify(zval * calist TSRMLS_DC) +{ + X509_STORE *store; + X509_LOOKUP * dir_lookup, * file_lookup; + HashPosition pos; + int ndirs = 0, nfiles = 0; + + store = X509_STORE_new(); + + if (store == NULL) { + return NULL; + } + + if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) { + zend_hash_internal_pointer_reset_ex(HASH_OF(calist), &pos); + for (;; zend_hash_move_forward_ex(HASH_OF(calist), &pos)) { + zval ** item; + struct stat sb; + + if (zend_hash_get_current_data_ex(HASH_OF(calist), (void**)&item, &pos) == FAILURE) { + break; + } + convert_to_string_ex(item); + + if (VCWD_STAT(Z_STRVAL_PP(item), &sb) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to stat %s", Z_STRVAL_PP(item)); + continue; + } + + if ((sb.st_mode & S_IFREG) == S_IFREG) { + file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading file %s", Z_STRVAL_PP(item)); + } else { + nfiles++; + } + file_lookup = NULL; + } else { + dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); + if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading directory %s", Z_STRVAL_PP(item)); + } else { + ndirs++; + } + dir_lookup = NULL; + } + } + } + if (nfiles == 0) { + file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if (file_lookup) { + X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT); + } + } + if (ndirs == 0) { + dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); + if (dir_lookup) { + X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT); + } + } + return store; +} +/* }}} */ + +/* {{{ proto resource openssl_x509_read(mixed cert) + Reads X.509 certificates */ +PHP_FUNCTION(openssl_x509_read) +{ + zval **cert; + X509 *x509; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) { + return; + } + Z_TYPE_P(return_value) = IS_RESOURCE; + x509 = php_openssl_x509_from_zval(cert, 1, &Z_LVAL_P(return_value) TSRMLS_CC); + + if (x509 == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied parameter cannot be coerced into an X509 certificate!"); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto void openssl_x509_free(resource x509) + Frees X.509 certificates */ +PHP_FUNCTION(openssl_x509_free) +{ + zval *x509; + X509 *cert; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &x509) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(cert, X509 *, &x509, -1, "OpenSSL X.509", le_x509); + zend_list_delete(Z_LVAL_P(x509)); +} +/* }}} */ + +/* }}} */ + +/* Pop all X509 from Stack and free them, free the stack afterwards */ +static void php_sk_X509_free(STACK_OF(X509) * sk) /* {{{ */ +{ + for (;;) { + X509* x = sk_X509_pop(sk); + if (!x) break; + X509_free(x); + } + sk_X509_free(sk); +} +/* }}} */ + +static STACK_OF(X509) * php_array_to_X509_sk(zval ** zcerts TSRMLS_DC) /* {{{ */ +{ + HashPosition hpos; + zval ** zcertval; + STACK_OF(X509) * sk = NULL; + X509 * cert; + long certresource; + + sk = sk_X509_new_null(); + + /* get certs */ + if (Z_TYPE_PP(zcerts) == IS_ARRAY) { + zend_hash_internal_pointer_reset_ex(HASH_OF(*zcerts), &hpos); + while(zend_hash_get_current_data_ex(HASH_OF(*zcerts), (void**)&zcertval, &hpos) == SUCCESS) { + + cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + goto clean_exit; + } + + if (certresource != -1) { + cert = X509_dup(cert); + + if (cert == NULL) { + goto clean_exit; + } + + } + sk_X509_push(sk, cert); + + zend_hash_move_forward_ex(HASH_OF(*zcerts), &hpos); + } + } else { + /* a single certificate */ + cert = php_openssl_x509_from_zval(zcerts, 0, &certresource TSRMLS_CC); + + if (cert == NULL) { + goto clean_exit; + } + + if (certresource != -1) { + cert = X509_dup(cert); + if (cert == NULL) { + goto clean_exit; + } + } + sk_X509_push(sk, cert); + } + + clean_exit: + return sk; +} +/* }}} */ + +/* {{{ proto bool openssl_pkcs12_export_to_file(mixed x509, string filename, mixed priv_key, string pass[, array args]) + Creates and exports a PKCS to file */ +PHP_FUNCTION(openssl_pkcs12_export_to_file) +{ + X509 * cert = NULL; + BIO * bio_out = NULL; + PKCS12 * p12 = NULL; + char * filename; + char * friendly_name = NULL; + int filename_len; + char * pass; + int pass_len; + zval **zcert = NULL, *zpkey = NULL, *args = NULL; + EVP_PKEY *priv_key = NULL; + long certresource, keyresource; + zval ** item; + STACK_OF(X509) *ca = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zpzs|a", &zcert, &filename, &filename_len, &zpkey, &pass, &pass_len, &args) == FAILURE) + return; + + RETVAL_FALSE; + + cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1"); + return; + } + priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC); + if (priv_key == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3"); + goto cleanup; + } + if (cert && !X509_check_private_key(cert, priv_key)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert"); + goto cleanup; + } + if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { + goto cleanup; + } + + /* parse extra config from args array, promote this to an extra function */ + if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS) + friendly_name = Z_STRVAL_PP(item); + /* certpbe (default RC2-40) + keypbe (default 3DES) + friendly_caname + */ + + if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS) + ca = php_array_to_X509_sk(item TSRMLS_CC); + /* end parse extra config */ + + /*PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ca, + int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/ + + p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0); + + bio_out = BIO_new_file(filename, "w"); + if (bio_out) { + + i2d_PKCS12_bio(bio_out, p12); + + RETVAL_TRUE; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename); + } + + BIO_free(bio_out); + PKCS12_free(p12); + php_sk_X509_free(ca); + +cleanup: + + if (keyresource == -1 && priv_key) { + EVP_PKEY_free(priv_key); + } + if (certresource == -1 && cert) { + X509_free(cert); + } +} +/* }}} */ + +/* {{{ proto bool openssl_pkcs12_export(mixed x509, string &out, mixed priv_key, string pass[, array args]) + Creates and exports a PKCS12 to a var */ +PHP_FUNCTION(openssl_pkcs12_export) +{ + X509 * cert = NULL; + BIO * bio_out; + PKCS12 * p12 = NULL; + zval * zcert = NULL, *zout = NULL, *zpkey, *args = NULL; + EVP_PKEY *priv_key = NULL; + long certresource, keyresource; + char * pass; + int pass_len; + char * friendly_name = NULL; + zval ** item; + STACK_OF(X509) *ca = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzzs|a", &zcert, &zout, &zpkey, &pass, &pass_len, &args) == FAILURE) + return; + + RETVAL_FALSE; + + cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1"); + return; + } + priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC); + if (priv_key == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3"); + goto cleanup; + } + if (cert && !X509_check_private_key(cert, priv_key)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert"); + goto cleanup; + } + + /* parse extra config from args array, promote this to an extra function */ + if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS) + friendly_name = Z_STRVAL_PP(item); + + if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS) + ca = php_array_to_X509_sk(item TSRMLS_CC); + /* end parse extra config */ + + p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0); + + bio_out = BIO_new(BIO_s_mem()); + if (i2d_PKCS12_bio(bio_out, p12)) { + BUF_MEM *bio_buf; + + zval_dtor(zout); + BIO_get_mem_ptr(bio_out, &bio_buf); + ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1); + + RETVAL_TRUE; + } + + BIO_free(bio_out); + PKCS12_free(p12); + php_sk_X509_free(ca); + +cleanup: + + if (keyresource == -1 && priv_key) { + EVP_PKEY_free(priv_key); + } + if (certresource == -1 && cert) { + X509_free(cert); + } +} +/* }}} */ + +/* {{{ proto bool openssl_pkcs12_read(string PKCS12, array &certs, string pass) + Parses a PKCS12 to an array */ +PHP_FUNCTION(openssl_pkcs12_read) +{ + zval *zout = NULL, *zextracerts, *zcert, *zpkey; + char *pass, *zp12; + int pass_len, zp12_len; + PKCS12 * p12 = NULL; + EVP_PKEY * pkey = NULL; + X509 * cert = NULL; + STACK_OF(X509) * ca = NULL; + BIO * bio_in = NULL; + int i; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szs", &zp12, &zp12_len, &zout, &pass, &pass_len) == FAILURE) + return; + + RETVAL_FALSE; + + bio_in = BIO_new(BIO_s_mem()); + + if(!BIO_write(bio_in, zp12, zp12_len)) + goto cleanup; + + if(d2i_PKCS12_bio(bio_in, &p12)) { + if(PKCS12_parse(p12, pass, &pkey, &cert, &ca)) { + BIO * bio_out; + + zval_dtor(zout); + array_init(zout); + + bio_out = BIO_new(BIO_s_mem()); + if (PEM_write_bio_X509(bio_out, cert)) { + BUF_MEM *bio_buf; + BIO_get_mem_ptr(bio_out, &bio_buf); + MAKE_STD_ZVAL(zcert); + ZVAL_STRINGL(zcert, bio_buf->data, bio_buf->length, 1); + add_assoc_zval(zout, "cert", zcert); + } + BIO_free(bio_out); + + bio_out = BIO_new(BIO_s_mem()); + if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) { + BUF_MEM *bio_buf; + BIO_get_mem_ptr(bio_out, &bio_buf); + MAKE_STD_ZVAL(zpkey); + ZVAL_STRINGL(zpkey, bio_buf->data, bio_buf->length, 1); + add_assoc_zval(zout, "pkey", zpkey); + } + BIO_free(bio_out); + + MAKE_STD_ZVAL(zextracerts); + array_init(zextracerts); + + for (i=0;;i++) { + zval * zextracert; + X509* aCA = sk_X509_pop(ca); + if (!aCA) break; + + bio_out = BIO_new(BIO_s_mem()); + if (PEM_write_bio_X509(bio_out, aCA)) { + BUF_MEM *bio_buf; + BIO_get_mem_ptr(bio_out, &bio_buf); + MAKE_STD_ZVAL(zextracert); + ZVAL_STRINGL(zextracert, bio_buf->data, bio_buf->length, 1); + add_index_zval(zextracerts, i, zextracert); + + } + BIO_free(bio_out); + + X509_free(aCA); + } + if(ca) { + sk_X509_free(ca); + add_assoc_zval(zout, "extracerts", zextracerts); + } else { + zval_dtor(zextracerts); + } + + RETVAL_TRUE; + + PKCS12_free(p12); + } + } + + cleanup: + if (bio_in) { + BIO_free(bio_in); + } + if (pkey) { + EVP_PKEY_free(pkey); + } + if (cert) { + X509_free(cert); + } +} +/* }}} */ + +/* {{{ x509 CSR functions */ + +/* {{{ php_openssl_make_REQ */ +static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs TSRMLS_DC) +{ + STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL; + char * str, *dn_sect, *attr_sect; + + dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name"); + if (dn_sect == NULL) { + return FAILURE; + } + dn_sk = CONF_get_section(req->req_config, dn_sect); + if (dn_sk == NULL) { + return FAILURE; + } + attr_sect = CONF_get_string(req->req_config, req->section_name, "attributes"); + if (attr_sect == NULL) { + attr_sk = NULL; + } else { + attr_sk = CONF_get_section(req->req_config, attr_sect); + if (attr_sk == NULL) { + return FAILURE; + } + } + /* setup the version number: version 1 */ + if (X509_REQ_set_version(csr, 0L)) { + int i, nid; + char * type; + CONF_VALUE * v; + X509_NAME * subj; + HashPosition hpos; + zval ** item; + + subj = X509_REQ_get_subject_name(csr); + /* apply values from the dn hash */ + zend_hash_internal_pointer_reset_ex(HASH_OF(dn), &hpos); + while(zend_hash_get_current_data_ex(HASH_OF(dn), (void**)&item, &hpos) == SUCCESS) { + char * strindex = NULL; + uint strindexlen = 0; + ulong intindex; + + zend_hash_get_current_key_ex(HASH_OF(dn), &strindex, &strindexlen, &intindex, 0, &hpos); + + convert_to_string_ex(item); + + if (strindex) { + int nid; + + nid = OBJ_txt2nid(strindex); + if (nid != NID_undef) { + if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, + (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0)) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "dn: add_entry_by_NID %d -> %s (failed; check error" + " queue and value of string_mask OpenSSL option " + "if illegal characters are reported)", + nid, Z_STRVAL_PP(item)); + return FAILURE; + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex); + } + } + zend_hash_move_forward_ex(HASH_OF(dn), &hpos); + } + + /* Finally apply defaults from config file */ + for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) { + int len; + char buffer[200 + 1]; /*200 + \0 !*/ + + v = sk_CONF_VALUE_value(dn_sk, i); + type = v->name; + + len = strlen(type); + if (len < sizeof("_default")) { + continue; + } + len -= sizeof("_default") - 1; + if (strcmp("_default", type + len) != 0) { + continue; + } + if (len > 200) { + len = 200; + } + memcpy(buffer, type, len); + buffer[len] = '\0'; + type = buffer; + + /* Skip past any leading X. X: X, etc to allow for multiple + * instances */ + for (str = type; *str; str++) { + if (*str == ':' || *str == ',' || *str == '.') { + str++; + if (*str) { + type = str; + } + break; + } + } + /* if it is already set, skip this */ + nid = OBJ_txt2nid(type); + if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) { + continue; + } + if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value); + return FAILURE; + } + if (!X509_NAME_entry_count(subj)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "no objects specified in config file"); + return FAILURE; + } + } + if (attribs) { + zend_hash_internal_pointer_reset_ex(HASH_OF(attribs), &hpos); + while(zend_hash_get_current_data_ex(HASH_OF(attribs), (void**)&item, &hpos) == SUCCESS) { + char *strindex = NULL; + uint strindexlen; + ulong intindex; + + zend_hash_get_current_key_ex(HASH_OF(attribs), &strindex, &strindexlen, &intindex, 0, &hpos); + convert_to_string_ex(item); + + if (strindex) { + int nid; + + nid = OBJ_txt2nid(strindex); + if (nid != NID_undef) { + if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item)); + return FAILURE; + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex); + } + } + zend_hash_move_forward_ex(HASH_OF(attribs), &hpos); + } + for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) { + v = sk_CONF_VALUE_value(attr_sk, i); + /* if it is already set, skip this */ + nid = OBJ_txt2nid(v->name); + if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) { + continue; + } + if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "add1_attr_by_txt %s -> %s (failed; check error queue " + "and value of string_mask OpenSSL option if illegal " + "characters are reported)", + v->name, v->value); + return FAILURE; + } + } + } + } + + X509_REQ_set_pubkey(csr, req->priv_key); + return SUCCESS; +} +/* }}} */ + +/* {{{ php_openssl_csr_from_zval */ +static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC) +{ + X509_REQ * csr = NULL; + char * filename = NULL; + BIO * in; + + if (resourceval) { + *resourceval = -1; + } + if (Z_TYPE_PP(val) == IS_RESOURCE) { + void * what; + int type; + + what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509 CSR", &type, 1, le_csr); + if (what) { + if (resourceval) { + *resourceval = Z_LVAL_PP(val); + } + return (X509_REQ*)what; + } + return NULL; + } else if (Z_TYPE_PP(val) != IS_STRING) { + return NULL; + } + + if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) { + filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1); + } + if (filename) { + if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { + return NULL; + } + in = BIO_new_file(filename, "r"); + } else { + in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + } + csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL); + BIO_free(in); + + return csr; +} +/* }}} */ + +/* {{{ proto bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true]) + Exports a CSR to file */ +PHP_FUNCTION(openssl_csr_export_to_file) +{ + X509_REQ * csr; + zval * zcsr = NULL; + zend_bool notext = 1; + char * filename = NULL; int filename_len; + BIO * bio_out; + long csr_resource; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp|b", &zcsr, &filename, &filename_len, ¬ext) == FAILURE) { + return; + } + RETVAL_FALSE; + + csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC); + if (csr == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1"); + return; + } + + if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { + return; + } + + bio_out = BIO_new_file(filename, "w"); + if (bio_out) { + if (!notext) { + X509_REQ_print(bio_out, csr); + } + PEM_write_bio_X509_REQ(bio_out, csr); + RETVAL_TRUE; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename); + } + + if (csr_resource == -1 && csr) { + X509_REQ_free(csr); + } + BIO_free(bio_out); +} +/* }}} */ + +/* {{{ proto bool openssl_csr_export(resource csr, string &out [, bool notext=true]) + Exports a CSR to file or a var */ +PHP_FUNCTION(openssl_csr_export) +{ + X509_REQ * csr; + zval * zcsr = NULL, *zout=NULL; + zend_bool notext = 1; + BIO * bio_out; + + long csr_resource; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|b", &zcsr, &zout, ¬ext) == FAILURE) { + return; + } + RETVAL_FALSE; + + csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC); + if (csr == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1"); + return; + } + + /* export to a var */ + + bio_out = BIO_new(BIO_s_mem()); + if (!notext) { + X509_REQ_print(bio_out, csr); + } + + if (PEM_write_bio_X509_REQ(bio_out, csr)) { + BUF_MEM *bio_buf; + + BIO_get_mem_ptr(bio_out, &bio_buf); + zval_dtor(zout); + ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1); + + RETVAL_TRUE; + } + + if (csr_resource == -1 && csr) { + X509_REQ_free(csr); + } + BIO_free(bio_out); +} +/* }}} */ + +/* {{{ proto resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]]) + Signs a cert with another CERT */ +PHP_FUNCTION(openssl_csr_sign) +{ + zval ** zcert = NULL, **zcsr, **zpkey, *args = NULL; + long num_days; + long serial = 0L; + X509 * cert = NULL, *new_cert = NULL; + X509_REQ * csr; + EVP_PKEY * key = NULL, *priv_key = NULL; + long csr_resource, certresource = 0, keyresource = -1; + int i; + struct php_x509_request req; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ!Zl|a!l", &zcsr, &zcert, &zpkey, &num_days, &args, &serial) == FAILURE) + return; + + RETVAL_FALSE; + PHP_SSL_REQ_INIT(&req); + + csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC); + if (csr == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1"); + return; + } + if (zcert) { + cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 2"); + goto cleanup; + } + } + priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 1, &keyresource TSRMLS_CC); + if (priv_key == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3"); + goto cleanup; + } + if (cert && !X509_check_private_key(cert, priv_key)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to signing cert"); + goto cleanup; + } + + if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) { + goto cleanup; + } + /* Check that the request matches the signature */ + key = X509_REQ_get_pubkey(csr); + if (key == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error unpacking public key"); + goto cleanup; + } + i = X509_REQ_verify(csr, key); + + if (i < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature verification problems"); + goto cleanup; + } + else if (i == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature did not match the certificate request"); + goto cleanup; + } + + /* Now we can get on with it */ + + new_cert = X509_new(); + if (new_cert == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No memory"); + goto cleanup; + } + /* Version 3 cert */ + if (!X509_set_version(new_cert, 2)) + goto cleanup; + + ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial); + + X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr)); + + if (cert == NULL) { + cert = new_cert; + } + if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) { + goto cleanup; + } + X509_gmtime_adj(X509_get_notBefore(new_cert), 0); + X509_gmtime_adj(X509_get_notAfter(new_cert), (long)60*60*24*num_days); + i = X509_set_pubkey(new_cert, key); + if (!i) { + goto cleanup; + } + if (req.extensions_section) { + X509V3_CTX ctx; + + X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0); + X509V3_set_conf_lhash(&ctx, req.req_config); + if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) { + goto cleanup; + } + } + + /* Now sign it */ + if (!X509_sign(new_cert, priv_key, req.digest)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to sign it"); + goto cleanup; + } + + /* Succeeded; lets return the cert */ + RETVAL_RESOURCE(zend_list_insert(new_cert, le_x509 TSRMLS_CC)); + new_cert = NULL; + +cleanup: + + if (cert == new_cert) { + cert = NULL; + } + PHP_SSL_REQ_DISPOSE(&req); + + if (keyresource == -1 && priv_key) { + EVP_PKEY_free(priv_key); + } + if (key) { + EVP_PKEY_free(key); + } + if (csr_resource == -1 && csr) { + X509_REQ_free(csr); + } + if (certresource == -1 && cert) { + X509_free(cert); + } + if (new_cert) { + X509_free(new_cert); + } +} +/* }}} */ + +/* {{{ proto bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]]) + Generates a privkey and CSR */ +PHP_FUNCTION(openssl_csr_new) +{ + struct php_x509_request req; + zval * args = NULL, * dn, *attribs = NULL; + zval * out_pkey; + X509_REQ * csr = NULL; + int we_made_the_key = 1; + long key_resource; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) { + return; + } + RETVAL_FALSE; + + PHP_SSL_REQ_INIT(&req); + + if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) { + /* Generate or use a private key */ + if (Z_TYPE_P(out_pkey) != IS_NULL) { + req.priv_key = php_openssl_evp_from_zval(&out_pkey, 0, NULL, 0, &key_resource TSRMLS_CC); + if (req.priv_key != NULL) { + we_made_the_key = 0; + } + } + if (req.priv_key == NULL) { + php_openssl_generate_private_key(&req TSRMLS_CC); + } + if (req.priv_key == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to generate a private key"); + } else { + csr = X509_REQ_new(); + if (csr) { + if (php_openssl_make_REQ(&req, csr, dn, attribs TSRMLS_CC) == SUCCESS) { + X509V3_CTX ext_ctx; + + X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0); + X509V3_set_conf_lhash(&ext_ctx, req.req_config); + + /* Add extensions */ + if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config, + &ext_ctx, req.request_extensions_section, csr)) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading extension section %s", req.request_extensions_section); + } else { + RETVAL_TRUE; + + if (X509_REQ_sign(csr, req.priv_key, req.digest)) { + RETVAL_RESOURCE(zend_list_insert(csr, le_csr TSRMLS_CC)); + csr = NULL; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error signing request"); + } + + if (we_made_the_key) { + /* and a resource for the private key */ + zval_dtor(out_pkey); + ZVAL_RESOURCE(out_pkey, zend_list_insert(req.priv_key, le_key TSRMLS_CC)); + req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */ + } else if (key_resource != -1) { + req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */ + } + } + } + else { + if (!we_made_the_key) { + /* if we have not made the key we are not supposed to zap it by calling dispose! */ + req.priv_key = NULL; + } + } + } + } + } + if (csr) { + X509_REQ_free(csr); + } + PHP_SSL_REQ_DISPOSE(&req); +} +/* }}} */ + +/* {{{ proto mixed openssl_csr_get_subject(mixed csr) + Returns the subject of a CERT or FALSE on error */ +PHP_FUNCTION(openssl_csr_get_subject) +{ + zval ** zcsr; + zend_bool use_shortnames = 1; + long csr_resource; + X509_NAME * subject; + X509_REQ * csr; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) { + return; + } + + csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC); + + if (csr == NULL) { + RETURN_FALSE; + } + + subject = X509_REQ_get_subject_name(csr); + + array_init(return_value); + add_assoc_name_entry(return_value, NULL, subject, use_shortnames TSRMLS_CC); + return; +} +/* }}} */ + +/* {{{ proto mixed openssl_csr_get_public_key(mixed csr) + Returns the subject of a CERT or FALSE on error */ +PHP_FUNCTION(openssl_csr_get_public_key) +{ + zval ** zcsr; + zend_bool use_shortnames = 1; + long csr_resource; + + X509_REQ * csr; + EVP_PKEY *tpubkey; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) { + return; + } + + csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC); + + if (csr == NULL) { + RETURN_FALSE; + } + + tpubkey=X509_REQ_get_pubkey(csr); + RETVAL_RESOURCE(zend_list_insert(tpubkey, le_key TSRMLS_CC)); + return; +} +/* }}} */ + +/* }}} */ + +/* {{{ EVP Public/Private key functions */ + +/* {{{ php_openssl_evp_from_zval + Given a zval, coerce it into a EVP_PKEY object. + It can be: + 1. private key resource from openssl_get_privatekey() + 2. X509 resource -> public key will be extracted from it + 3. if it starts with file:// interpreted as path to key file + 4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey() + 5. an array(0 => [items 2..4], 1 => passphrase) + 6. if val is a string (possibly starting with file:///) and it is not an X509 certificate, then interpret as public key + NOTE: If you are requesting a private key but have not specified a passphrase, you should use an + empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in + the Apache error log! +*/ +static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC) +{ + EVP_PKEY * key = NULL; + X509 * cert = NULL; + int free_cert = 0; + long cert_res = -1; + char * filename = NULL; + zval tmp; + + Z_TYPE(tmp) = IS_NULL; + +#define TMP_CLEAN \ + if (Z_TYPE(tmp) == IS_STRING) {\ + zval_dtor(&tmp); \ + } \ + return NULL; + + if (resourceval) { + *resourceval = -1; + } + if (Z_TYPE_PP(val) == IS_ARRAY) { + zval ** zphrase; + + /* get passphrase */ + + if (zend_hash_index_find(HASH_OF(*val), 1, (void **)&zphrase) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)"); + return NULL; + } + + if (Z_TYPE_PP(zphrase) == IS_STRING) { + passphrase = Z_STRVAL_PP(zphrase); + } else { + tmp = **zphrase; + zval_copy_ctor(&tmp); + convert_to_string(&tmp); + passphrase = Z_STRVAL(tmp); + } + + /* now set val to be the key param and continue */ + if (zend_hash_index_find(HASH_OF(*val), 0, (void **)&val) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)"); + TMP_CLEAN; + } + } + + if (Z_TYPE_PP(val) == IS_RESOURCE) { + void * what; + int type; + + what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key); + if (!what) { + TMP_CLEAN; + } + if (resourceval) { + *resourceval = Z_LVAL_PP(val); + } + if (type == le_x509) { + /* extract key from cert, depending on public_key param */ + cert = (X509*)what; + free_cert = 0; + } else if (type == le_key) { + int is_priv; + + is_priv = php_openssl_is_private_key((EVP_PKEY*)what TSRMLS_CC); + + /* check whether it is actually a private key if requested */ + if (!public_key && !is_priv) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param is a public key"); + TMP_CLEAN; + } + + if (public_key && is_priv) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Don't know how to get public key from this private key"); + TMP_CLEAN; + } else { + if (Z_TYPE(tmp) == IS_STRING) { + zval_dtor(&tmp); + } + /* got the key - return it */ + return (EVP_PKEY*)what; + } + } else { + /* other types could be used here - eg: file pointers and read in the data from them */ + TMP_CLEAN; + } + } else { + /* force it to be a string and check if it refers to a file */ + /* passing non string values leaks, object uses toString, it returns NULL + * See bug38255.phpt + */ + if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) { + TMP_CLEAN; + } + convert_to_string_ex(val); + + if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) { + filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1); + } + /* it's an X509 file/cert of some kind, and we need to extract the data from that */ + if (public_key) { + cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC); + free_cert = (cert_res == -1); + /* actual extraction done later */ + if (!cert) { + /* not a X509 certificate, try to retrieve public key */ + BIO* in; + if (filename) { + in = BIO_new_file(filename, "r"); + } else { + in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + } + if (in == NULL) { + TMP_CLEAN; + } + key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL); + BIO_free(in); + } + } else { + /* we want the private key */ + BIO *in; + + if (filename) { + if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { + TMP_CLEAN; + } + in = BIO_new_file(filename, "r"); + } else { + in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); + } + + if (in == NULL) { + TMP_CLEAN; + } + key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase); + BIO_free(in); + } + } + + if (public_key && cert && key == NULL) { + /* extract public key from X509 cert */ + key = (EVP_PKEY *) X509_get_pubkey(cert); + } + + if (free_cert && cert) { + X509_free(cert); + } + if (key && makeresource && resourceval) { + *resourceval = ZEND_REGISTER_RESOURCE(NULL, key, le_key); + } + if (Z_TYPE(tmp) == IS_STRING) { + zval_dtor(&tmp); + } + return key; +} +/* }}} */ + +/* {{{ php_openssl_generate_private_key */ +static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC) +{ + char * randfile = NULL; + int egdsocket, seeded; + EVP_PKEY * return_val = NULL; + + if (req->priv_key_bits < MIN_KEY_LENGTH) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key length is too short; it needs to be at least %d bits, not %d", + MIN_KEY_LENGTH, req->priv_key_bits); + return NULL; + } + + randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE"); + php_openssl_load_rand_file(randfile, &egdsocket, &seeded TSRMLS_CC); + + if ((req->priv_key = EVP_PKEY_new()) != NULL) { + switch(req->priv_key_type) { + case OPENSSL_KEYTYPE_RSA: + if (EVP_PKEY_assign_RSA(req->priv_key, RSA_generate_key(req->priv_key_bits, 0x10001, NULL, NULL))) { + return_val = req->priv_key; + } + break; +#if !defined(NO_DSA) && defined(HAVE_DSA_DEFAULT_METHOD) + case OPENSSL_KEYTYPE_DSA: + { + DSA *dsapar = DSA_generate_parameters(req->priv_key_bits, NULL, 0, NULL, NULL, NULL, NULL); + if (dsapar) { + DSA_set_method(dsapar, DSA_get_default_method()); + if (DSA_generate_key(dsapar)) { + if (EVP_PKEY_assign_DSA(req->priv_key, dsapar)) { + return_val = req->priv_key; + } + } else { + DSA_free(dsapar); + } + } + } + break; +#endif +#if !defined(NO_DH) + case OPENSSL_KEYTYPE_DH: + { + DH *dhpar = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL); + int codes = 0; + + if (dhpar) { + DH_set_method(dhpar, DH_get_default_method()); + if (DH_check(dhpar, &codes) && codes == 0 && DH_generate_key(dhpar)) { + if (EVP_PKEY_assign_DH(req->priv_key, dhpar)) { + return_val = req->priv_key; + } + } else { + DH_free(dhpar); + } + } + } + break; +#endif + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported private key type"); + } + } + + php_openssl_write_rand_file(randfile, egdsocket, seeded); + + if (return_val == NULL) { + EVP_PKEY_free(req->priv_key); + req->priv_key = NULL; + return NULL; + } + + return return_val; +} +/* }}} */ + +/* {{{ php_openssl_is_private_key + Check whether the supplied key is a private key by checking if the secret prime factors are set */ +static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC) +{ + assert(pkey != NULL); + + switch (pkey->type) { +#ifndef NO_RSA + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + assert(pkey->pkey.rsa != NULL); + if (pkey->pkey.rsa != NULL && (NULL == pkey->pkey.rsa->p || NULL == pkey->pkey.rsa->q)) { + return 0; + } + break; +#endif +#ifndef NO_DSA + case EVP_PKEY_DSA: + case EVP_PKEY_DSA1: + case EVP_PKEY_DSA2: + case EVP_PKEY_DSA3: + case EVP_PKEY_DSA4: + assert(pkey->pkey.dsa != NULL); + + if (NULL == pkey->pkey.dsa->p || NULL == pkey->pkey.dsa->q || NULL == pkey->pkey.dsa->priv_key){ + return 0; + } + break; +#endif +#ifndef NO_DH + case EVP_PKEY_DH: + assert(pkey->pkey.dh != NULL); + + if (NULL == pkey->pkey.dh->p || NULL == pkey->pkey.dh->priv_key) { + return 0; + } + break; +#endif + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); + break; + } + return 1; +} +/* }}} */ + +#define OPENSSL_PKEY_GET_BN(_type, _name) do { \ + if (pkey->pkey._type->_name != NULL) { \ + int len = BN_num_bytes(pkey->pkey._type->_name); \ + char *str = emalloc(len + 1); \ + BN_bn2bin(pkey->pkey._type->_name, (unsigned char*)str); \ + str[len] = 0; \ + add_assoc_stringl(_type, #_name, str, len, 0); \ + } \ + } while (0) + +#define OPENSSL_PKEY_SET_BN(_ht, _type, _name) do { \ + zval **bn; \ + if (zend_hash_find(_ht, #_name, sizeof(#_name), (void**)&bn) == SUCCESS && \ + Z_TYPE_PP(bn) == IS_STRING) { \ + _type->_name = BN_bin2bn( \ + (unsigned char*)Z_STRVAL_PP(bn), \ + Z_STRLEN_PP(bn), NULL); \ + } \ + } while (0); + + +/* {{{ proto resource openssl_pkey_new([array configargs]) + Generates a new private key */ +PHP_FUNCTION(openssl_pkey_new) +{ + struct php_x509_request req; + zval * args = NULL; + zval **data; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &args) == FAILURE) { + return; + } + RETVAL_FALSE; + + if (args && Z_TYPE_P(args) == IS_ARRAY) { + EVP_PKEY *pkey; + + if (zend_hash_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa"), (void**)&data) == SUCCESS && + Z_TYPE_PP(data) == IS_ARRAY) { + pkey = EVP_PKEY_new(); + if (pkey) { + RSA *rsa = RSA_new(); + if (rsa) { + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, n); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, e); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, d); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, p); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, q); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmp1); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmq1); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, iqmp); + if (rsa->n && rsa->d) { + if (EVP_PKEY_assign_RSA(pkey, rsa)) { + RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC)); + } + } + RSA_free(rsa); + } + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } else if (zend_hash_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa"), (void**)&data) == SUCCESS && + Z_TYPE_PP(data) == IS_ARRAY) { + pkey = EVP_PKEY_new(); + if (pkey) { + DSA *dsa = DSA_new(); + if (dsa) { + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, p); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, q); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, g); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, priv_key); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, pub_key); + if (dsa->p && dsa->q && dsa->g) { + if (!dsa->priv_key && !dsa->pub_key) { + DSA_generate_key(dsa); + } + if (EVP_PKEY_assign_DSA(pkey, dsa)) { + RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC)); + } + } + DSA_free(dsa); + } + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } else if (zend_hash_find(Z_ARRVAL_P(args), "dh", sizeof("dh"), (void**)&data) == SUCCESS && + Z_TYPE_PP(data) == IS_ARRAY) { + pkey = EVP_PKEY_new(); + if (pkey) { + DH *dh = DH_new(); + if (dh) { + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, p); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, g); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, priv_key); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, pub_key); + if (dh->p && dh->g) { + if (!dh->pub_key) { + DH_generate_key(dh); + } + if (EVP_PKEY_assign_DH(pkey, dh)) { + RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC)); + } + } + DH_free(dh); + } + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } + } + + PHP_SSL_REQ_INIT(&req); + + if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) + { + if (php_openssl_generate_private_key(&req TSRMLS_CC)) { + /* pass back a key resource */ + RETVAL_RESOURCE(zend_list_insert(req.priv_key, le_key TSRMLS_CC)); + /* make sure the cleanup code doesn't zap it! */ + req.priv_key = NULL; + } + } + PHP_SSL_REQ_DISPOSE(&req); +} +/* }}} */ + +/* {{{ proto bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args) + Gets an exportable representation of a key into a file */ +PHP_FUNCTION(openssl_pkey_export_to_file) +{ + struct php_x509_request req; + zval ** zpkey, * args = NULL; + char * passphrase = NULL; int passphrase_len = 0; + char * filename = NULL; int filename_len = 0; + long key_resource = -1; + EVP_PKEY * key; + BIO * bio_out = NULL; + const EVP_CIPHER * cipher; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zp|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) { + return; + } + RETVAL_FALSE; + + key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC); + + if (key == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1"); + RETURN_FALSE; + } + + if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { + RETURN_FALSE; + } + + PHP_SSL_REQ_INIT(&req); + + if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) { + bio_out = BIO_new_file(filename, "w"); + + if (passphrase && req.priv_key_encrypt) { + if (req.priv_key_encrypt_cipher) { + cipher = req.priv_key_encrypt_cipher; + } else { + cipher = (EVP_CIPHER *) EVP_des_ede3_cbc(); + } + } else { + cipher = NULL; + } + if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) { + /* Success! + * If returning the output as a string, do so now */ + RETVAL_TRUE; + } + } + PHP_SSL_REQ_DISPOSE(&req); + + if (key_resource == -1 && key) { + EVP_PKEY_free(key); + } + if (bio_out) { + BIO_free(bio_out); + } +} +/* }}} */ + +/* {{{ proto bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]]) + Gets an exportable representation of a key into a string or file */ +PHP_FUNCTION(openssl_pkey_export) +{ + struct php_x509_request req; + zval ** zpkey, * args = NULL, *out; + char * passphrase = NULL; int passphrase_len = 0; + long key_resource = -1; + EVP_PKEY * key; + BIO * bio_out = NULL; + const EVP_CIPHER * cipher; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) { + return; + } + RETVAL_FALSE; + + key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC); + + if (key == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1"); + RETURN_FALSE; + } + + PHP_SSL_REQ_INIT(&req); + + if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) { + bio_out = BIO_new(BIO_s_mem()); + + if (passphrase && req.priv_key_encrypt) { + if (req.priv_key_encrypt_cipher) { + cipher = req.priv_key_encrypt_cipher; + } else { + cipher = (EVP_CIPHER *) EVP_des_ede3_cbc(); + } + } else { + cipher = NULL; + } + if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) { + /* Success! + * If returning the output as a string, do so now */ + + char * bio_mem_ptr; + long bio_mem_len; + RETVAL_TRUE; + + bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr); + zval_dtor(out); + ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len, 1); + } + } + PHP_SSL_REQ_DISPOSE(&req); + + if (key_resource == -1 && key) { + EVP_PKEY_free(key); + } + if (bio_out) { + BIO_free(bio_out); + } +} +/* }}} */ + +/* {{{ proto int openssl_pkey_get_public(mixed cert) + Gets public key from X.509 certificate */ +PHP_FUNCTION(openssl_pkey_get_public) +{ + zval **cert; + EVP_PKEY *pkey; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) { + return; + } + Z_TYPE_P(return_value) = IS_RESOURCE; + pkey = php_openssl_evp_from_zval(cert, 1, NULL, 1, &Z_LVAL_P(return_value) TSRMLS_CC); + + if (pkey == NULL) { + RETURN_FALSE; + } + zend_list_addref(Z_LVAL_P(return_value)); +} +/* }}} */ + +/* {{{ proto void openssl_pkey_free(int key) + Frees a key */ +PHP_FUNCTION(openssl_pkey_free) +{ + zval *key; + EVP_PKEY *pkey; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key); + zend_list_delete(Z_LVAL_P(key)); +} +/* }}} */ + +/* {{{ proto int openssl_pkey_get_private(string key [, string passphrase]) + Gets private keys */ +PHP_FUNCTION(openssl_pkey_get_private) +{ + zval **cert; + EVP_PKEY *pkey; + char * passphrase = ""; + int passphrase_len = sizeof("")-1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|s", &cert, &passphrase, &passphrase_len) == FAILURE) { + return; + } + Z_TYPE_P(return_value) = IS_RESOURCE; + pkey = php_openssl_evp_from_zval(cert, 0, passphrase, 1, &Z_LVAL_P(return_value) TSRMLS_CC); + + if (pkey == NULL) { + RETURN_FALSE; + } + zend_list_addref(Z_LVAL_P(return_value)); +} + +/* }}} */ + +/* {{{ proto resource openssl_pkey_get_details(resource key) + returns an array with the key details (bits, pkey, type)*/ +PHP_FUNCTION(openssl_pkey_get_details) +{ + zval *key; + EVP_PKEY *pkey; + BIO *out; + unsigned int pbio_len; + char *pbio; + long ktype; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key); + if (!pkey) { + RETURN_FALSE; + } + out = BIO_new(BIO_s_mem()); + PEM_write_bio_PUBKEY(out, pkey); + pbio_len = BIO_get_mem_data(out, &pbio); + + array_init(return_value); + add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey)); + add_assoc_stringl(return_value, "key", pbio, pbio_len, 1); + /*TODO: Use the real values once the openssl constants are used + * See the enum at the top of this file + */ + switch (EVP_PKEY_type(pkey->type)) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + ktype = OPENSSL_KEYTYPE_RSA; + + if (pkey->pkey.rsa != NULL) { + zval *rsa; + + ALLOC_INIT_ZVAL(rsa); + array_init(rsa); + OPENSSL_PKEY_GET_BN(rsa, n); + OPENSSL_PKEY_GET_BN(rsa, e); + OPENSSL_PKEY_GET_BN(rsa, d); + OPENSSL_PKEY_GET_BN(rsa, p); + OPENSSL_PKEY_GET_BN(rsa, q); + OPENSSL_PKEY_GET_BN(rsa, dmp1); + OPENSSL_PKEY_GET_BN(rsa, dmq1); + OPENSSL_PKEY_GET_BN(rsa, iqmp); + add_assoc_zval(return_value, "rsa", rsa); + } + + break; + case EVP_PKEY_DSA: + case EVP_PKEY_DSA2: + case EVP_PKEY_DSA3: + case EVP_PKEY_DSA4: + ktype = OPENSSL_KEYTYPE_DSA; + + if (pkey->pkey.dsa != NULL) { + zval *dsa; + + ALLOC_INIT_ZVAL(dsa); + array_init(dsa); + OPENSSL_PKEY_GET_BN(dsa, p); + OPENSSL_PKEY_GET_BN(dsa, q); + OPENSSL_PKEY_GET_BN(dsa, g); + OPENSSL_PKEY_GET_BN(dsa, priv_key); + OPENSSL_PKEY_GET_BN(dsa, pub_key); + add_assoc_zval(return_value, "dsa", dsa); + } + break; + case EVP_PKEY_DH: + + ktype = OPENSSL_KEYTYPE_DH; + + if (pkey->pkey.dh != NULL) { + zval *dh; + + ALLOC_INIT_ZVAL(dh); + array_init(dh); + OPENSSL_PKEY_GET_BN(dh, p); + OPENSSL_PKEY_GET_BN(dh, g); + OPENSSL_PKEY_GET_BN(dh, priv_key); + OPENSSL_PKEY_GET_BN(dh, pub_key); + add_assoc_zval(return_value, "dh", dh); + } + + break; +#ifdef EVP_PKEY_EC + case EVP_PKEY_EC: + ktype = OPENSSL_KEYTYPE_EC; + break; +#endif + default: + ktype = -1; + break; + } + add_assoc_long(return_value, "type", ktype); + + BIO_free(out); +} +/* }}} */ + +/* }}} */ + +/* {{{ PKCS7 S/MIME functions */ + +/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]]) + Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */ +PHP_FUNCTION(openssl_pkcs7_verify) +{ + X509_STORE * store = NULL; + zval * cainfo = NULL; + STACK_OF(X509) *signers= NULL; + STACK_OF(X509) *others = NULL; + PKCS7 * p7 = NULL; + BIO * in = NULL, * datain = NULL, * dataout = NULL; + long flags = 0; + char * filename; int filename_len; + char * extracerts = NULL; int extracerts_len = 0; + char * signersfilename = NULL; int signersfilename_len = 0; + char * datafilename = NULL; int datafilename_len = 0; + + RETVAL_LONG(-1); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "pl|papp", &filename, &filename_len, + &flags, &signersfilename, &signersfilename_len, &cainfo, + &extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) { + return; + } + + if (extracerts) { + others = load_all_certs_from_file(extracerts); + if (others == NULL) { + goto clean_exit; + } + } + + flags = flags & ~PKCS7_DETACHED; + + store = setup_verify(cainfo TSRMLS_CC); + + if (!store) { + goto clean_exit; + } + if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) { + goto clean_exit; + } + + in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r"); + if (in == NULL) { + goto clean_exit; + } + p7 = SMIME_read_PKCS7(in, &datain); + if (p7 == NULL) { +#if DEBUG_SMIME + zend_printf("SMIME_read_PKCS7 failed\n"); +#endif + goto clean_exit; + } + + if (datafilename) { + + if (php_openssl_open_base_dir_chk(datafilename TSRMLS_CC)) { + goto clean_exit; + } + + dataout = BIO_new_file(datafilename, "w"); + if (dataout == NULL) { + goto clean_exit; + } + } +#if DEBUG_SMIME + zend_printf("Calling PKCS7 verify\n"); +#endif + + if (PKCS7_verify(p7, others, store, datain, dataout, flags)) { + + RETVAL_TRUE; + + if (signersfilename) { + BIO *certout; + + if (php_openssl_open_base_dir_chk(signersfilename TSRMLS_CC)) { + goto clean_exit; + } + + certout = BIO_new_file(signersfilename, "w"); + if (certout) { + int i; + signers = PKCS7_get0_signers(p7, NULL, flags); + + for(i = 0; i < sk_X509_num(signers); i++) { + PEM_write_bio_X509(certout, sk_X509_value(signers, i)); + } + BIO_free(certout); + sk_X509_free(signers); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename); + RETVAL_LONG(-1); + } + } + goto clean_exit; + } else { + RETVAL_FALSE; + } +clean_exit: + X509_STORE_free(store); + BIO_free(datain); + BIO_free(in); + BIO_free(dataout); + PKCS7_free(p7); + sk_X509_free(others); +} +/* }}} */ + +/* {{{ proto bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]]) + Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */ +PHP_FUNCTION(openssl_pkcs7_encrypt) +{ + zval ** zrecipcerts, * zheaders = NULL; + STACK_OF(X509) * recipcerts = NULL; + BIO * infile = NULL, * outfile = NULL; + long flags = 0; + PKCS7 * p7 = NULL; + HashPosition hpos; + zval ** zcertval; + X509 * cert; + const EVP_CIPHER *cipher = NULL; + long cipherid = PHP_OPENSSL_CIPHER_DEFAULT; + uint strindexlen; + ulong intindex; + char * strindex; + char * infilename = NULL; int infilename_len; + char * outfilename = NULL; int outfilename_len; + + RETVAL_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZa!|ll", &infilename, &infilename_len, + &outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE) + return; + + + if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) { + return; + } + + infile = BIO_new_file(infilename, "r"); + if (infile == NULL) { + goto clean_exit; + } + + outfile = BIO_new_file(outfilename, "w"); + if (outfile == NULL) { + goto clean_exit; + } + + recipcerts = sk_X509_new_null(); + + /* get certs */ + if (Z_TYPE_PP(zrecipcerts) == IS_ARRAY) { + zend_hash_internal_pointer_reset_ex(HASH_OF(*zrecipcerts), &hpos); + while(zend_hash_get_current_data_ex(HASH_OF(*zrecipcerts), (void**)&zcertval, &hpos) == SUCCESS) { + long certresource; + + cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + goto clean_exit; + } + + if (certresource != -1) { + /* we shouldn't free this particular cert, as it is a resource. + make a copy and push that on the stack instead */ + cert = X509_dup(cert); + if (cert == NULL) { + goto clean_exit; + } + } + sk_X509_push(recipcerts, cert); + + zend_hash_move_forward_ex(HASH_OF(*zrecipcerts), &hpos); + } + } else { + /* a single certificate */ + long certresource; + + cert = php_openssl_x509_from_zval(zrecipcerts, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + goto clean_exit; + } + + if (certresource != -1) { + /* we shouldn't free this particular cert, as it is a resource. + make a copy and push that on the stack instead */ + cert = X509_dup(cert); + if (cert == NULL) { + goto clean_exit; + } + } + sk_X509_push(recipcerts, cert); + } + + /* sanity check the cipher */ + cipher = php_openssl_get_evp_cipher_from_algo(cipherid); + if (cipher == NULL) { + /* shouldn't happen */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get cipher"); + goto clean_exit; + } + + p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, flags); + + if (p7 == NULL) { + goto clean_exit; + } + + /* tack on extra headers */ + if (zheaders) { + zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos); + while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&zcertval, &hpos) == SUCCESS) { + strindex = NULL; + zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos); + + convert_to_string_ex(zcertval); + + if (strindex) { + BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(zcertval)); + } else { + BIO_printf(outfile, "%s\n", Z_STRVAL_PP(zcertval)); + } + + zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos); + } + } + + (void)BIO_reset(infile); + + /* write the encrypted data */ + SMIME_write_PKCS7(outfile, p7, infile, flags); + + RETVAL_TRUE; + +clean_exit: + PKCS7_free(p7); + BIO_free(infile); + BIO_free(outfile); + if (recipcerts) { + sk_X509_pop_free(recipcerts, X509_free); + } +} +/* }}} */ + +/* {{{ proto bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]]) + 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 */ + +PHP_FUNCTION(openssl_pkcs7_sign) +{ + zval ** zcert, ** zprivkey, * zheaders; + zval ** hval; + X509 * cert = NULL; + EVP_PKEY * privkey = NULL; + long flags = PKCS7_DETACHED; + PKCS7 * p7 = NULL; + BIO * infile = NULL, * outfile = NULL; + STACK_OF(X509) *others = NULL; + long certresource = -1, keyresource = -1; + ulong intindex; + uint strindexlen; + HashPosition hpos; + char * strindex; + char * infilename; int infilename_len; + char * outfilename; int outfilename_len; + char * extracertsfilename = NULL; int extracertsfilename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZZa!|lp", + &infilename, &infilename_len, &outfilename, &outfilename_len, + &zcert, &zprivkey, &zheaders, &flags, &extracertsfilename, + &extracertsfilename_len) == FAILURE) { + return; + } + + RETVAL_FALSE; + + if (extracertsfilename) { + others = load_all_certs_from_file(extracertsfilename); + if (others == NULL) { + goto clean_exit; + } + } + + privkey = php_openssl_evp_from_zval(zprivkey, 0, "", 0, &keyresource TSRMLS_CC); + if (privkey == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting private key"); + goto clean_exit; + } + + cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC); + if (cert == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting cert"); + goto clean_exit; + } + + if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) { + goto clean_exit; + } + + infile = BIO_new_file(infilename, "r"); + if (infile == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening input file %s!", infilename); + goto clean_exit; + } + + outfile = BIO_new_file(outfilename, "w"); + if (outfile == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening output file %s!", outfilename); + goto clean_exit; + } + + p7 = PKCS7_sign(cert, privkey, others, infile, flags); + if (p7 == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "error creating PKCS7 structure!"); + goto clean_exit; + } + + (void)BIO_reset(infile); + + /* tack on extra headers */ + if (zheaders) { + zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos); + while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&hval, &hpos) == SUCCESS) { + strindex = NULL; + zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos); + + convert_to_string_ex(hval); + + if (strindex) { + BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(hval)); + } else { + BIO_printf(outfile, "%s\n", Z_STRVAL_PP(hval)); + } + zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos); + } + } + /* write the signed data */ + SMIME_write_PKCS7(outfile, p7, infile, flags); + + RETVAL_TRUE; + +clean_exit: + PKCS7_free(p7); + BIO_free(infile); + BIO_free(outfile); + if (others) { + sk_X509_pop_free(others, X509_free); + } + if (privkey && keyresource == -1) { + EVP_PKEY_free(privkey); + } + if (cert && certresource == -1) { + X509_free(cert); + } +} +/* }}} */ + +/* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey]) + 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 */ + +PHP_FUNCTION(openssl_pkcs7_decrypt) +{ + zval ** recipcert, ** recipkey = NULL; + X509 * cert = NULL; + EVP_PKEY * key = NULL; + long certresval, keyresval; + BIO * in = NULL, * out = NULL, * datain = NULL; + PKCS7 * p7 = NULL; + char * infilename; int infilename_len; + char * outfilename; int outfilename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZ|Z", &infilename, &infilename_len, + &outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE) { + return; + } + + RETVAL_FALSE; + + cert = php_openssl_x509_from_zval(recipcert, 0, &certresval TSRMLS_CC); + if (cert == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 3 to x509 cert"); + goto clean_exit; + } + + key = php_openssl_evp_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, &keyresval TSRMLS_CC); + if (key == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to get private key"); + goto clean_exit; + } + + if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) { + goto clean_exit; + } + + in = BIO_new_file(infilename, "r"); + if (in == NULL) { + goto clean_exit; + } + out = BIO_new_file(outfilename, "w"); + if (out == NULL) { + goto clean_exit; + } + + p7 = SMIME_read_PKCS7(in, &datain); + + if (p7 == NULL) { + goto clean_exit; + } + if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) { + RETVAL_TRUE; + } +clean_exit: + PKCS7_free(p7); + BIO_free(datain); + BIO_free(in); + BIO_free(out); + if (cert && certresval == -1) { + X509_free(cert); + } + if (key && keyresval == -1) { + EVP_PKEY_free(key); + } +} +/* }}} */ + +/* }}} */ + +/* {{{ proto bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding]) + Encrypts data with private key */ +PHP_FUNCTION(openssl_private_encrypt) +{ + zval **key, *crypted; + EVP_PKEY *pkey; + int cryptedlen; + unsigned char *cryptedbuf = NULL; + int successful = 0; + long keyresource = -1; + char * data; + int data_len; + long padding = RSA_PKCS1_PADDING; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) { + return; + } + RETVAL_FALSE; + + pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC); + + if (pkey == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key param is not a valid private key"); + RETURN_FALSE; + } + + cryptedlen = EVP_PKEY_size(pkey); + cryptedbuf = emalloc(cryptedlen + 1); + + switch (pkey->type) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + successful = (RSA_private_encrypt(data_len, + (unsigned char *)data, + cryptedbuf, + pkey->pkey.rsa, + padding) == cryptedlen); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); + } + + if (successful) { + zval_dtor(crypted); + cryptedbuf[cryptedlen] = '\0'; + ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0); + cryptedbuf = NULL; + RETVAL_TRUE; + } + if (cryptedbuf) { + efree(cryptedbuf); + } + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } +} +/* }}} */ + +/* {{{ proto bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding]) + Decrypts data with private key */ +PHP_FUNCTION(openssl_private_decrypt) +{ + zval **key, *crypted; + EVP_PKEY *pkey; + int cryptedlen; + unsigned char *cryptedbuf = NULL; + unsigned char *crypttemp; + int successful = 0; + long padding = RSA_PKCS1_PADDING; + long keyresource = -1; + char * data; + int data_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) { + return; + } + RETVAL_FALSE; + + pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC); + if (pkey == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid private key"); + RETURN_FALSE; + } + + cryptedlen = EVP_PKEY_size(pkey); + crypttemp = emalloc(cryptedlen + 1); + + switch (pkey->type) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + cryptedlen = RSA_private_decrypt(data_len, + (unsigned char *)data, + crypttemp, + pkey->pkey.rsa, + padding); + if (cryptedlen != -1) { + cryptedbuf = emalloc(cryptedlen + 1); + memcpy(cryptedbuf, crypttemp, cryptedlen); + successful = 1; + } + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); + } + + efree(crypttemp); + + if (successful) { + zval_dtor(crypted); + cryptedbuf[cryptedlen] = '\0'; + ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0); + cryptedbuf = NULL; + RETVAL_TRUE; + } + + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } + if (cryptedbuf) { + efree(cryptedbuf); + } +} +/* }}} */ + +/* {{{ proto bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding]) + Encrypts data with public key */ +PHP_FUNCTION(openssl_public_encrypt) +{ + zval **key, *crypted; + EVP_PKEY *pkey; + int cryptedlen; + unsigned char *cryptedbuf; + int successful = 0; + long keyresource = -1; + long padding = RSA_PKCS1_PADDING; + char * data; + int data_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) + return; + + RETVAL_FALSE; + + pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC); + if (pkey == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key"); + RETURN_FALSE; + } + + cryptedlen = EVP_PKEY_size(pkey); + cryptedbuf = emalloc(cryptedlen + 1); + + switch (pkey->type) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + successful = (RSA_public_encrypt(data_len, + (unsigned char *)data, + cryptedbuf, + pkey->pkey.rsa, + padding) == cryptedlen); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); + + } + + if (successful) { + zval_dtor(crypted); + cryptedbuf[cryptedlen] = '\0'; + ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0); + cryptedbuf = NULL; + RETVAL_TRUE; + } + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } + if (cryptedbuf) { + efree(cryptedbuf); + } +} +/* }}} */ + +/* {{{ proto bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding]) + Decrypts data with public key */ +PHP_FUNCTION(openssl_public_decrypt) +{ + zval **key, *crypted; + EVP_PKEY *pkey; + int cryptedlen; + unsigned char *cryptedbuf = NULL; + unsigned char *crypttemp; + int successful = 0; + long keyresource = -1; + long padding = RSA_PKCS1_PADDING; + char * data; + int data_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) { + return; + } + RETVAL_FALSE; + + pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC); + if (pkey == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key"); + RETURN_FALSE; + } + + cryptedlen = EVP_PKEY_size(pkey); + crypttemp = emalloc(cryptedlen + 1); + + switch (pkey->type) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + cryptedlen = RSA_public_decrypt(data_len, + (unsigned char *)data, + crypttemp, + pkey->pkey.rsa, + padding); + if (cryptedlen != -1) { + cryptedbuf = emalloc(cryptedlen + 1); + memcpy(cryptedbuf, crypttemp, cryptedlen); + successful = 1; + } + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); + + } + + efree(crypttemp); + + if (successful) { + zval_dtor(crypted); + cryptedbuf[cryptedlen] = '\0'; + ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0); + cryptedbuf = NULL; + RETVAL_TRUE; + } + + if (cryptedbuf) { + efree(cryptedbuf); + } + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } +} +/* }}} */ + +/* {{{ proto mixed openssl_error_string(void) + Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */ +PHP_FUNCTION(openssl_error_string) +{ + char buf[512]; + unsigned long val; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + val = ERR_get_error(); + if (val) { + RETURN_STRING(ERR_error_string(val, buf), 1); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, mixed method]) + Signs data */ +PHP_FUNCTION(openssl_sign) +{ + zval **key, *signature; + EVP_PKEY *pkey; + int siglen; + unsigned char *sigbuf; + long keyresource = -1; + char * data; + int data_len; + EVP_MD_CTX md_ctx; + zval *method = NULL; + long signature_algo = OPENSSL_ALGO_SHA1; + const EVP_MD *mdtype; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|z", &data, &data_len, &signature, &key, &method) == FAILURE) { + return; + } + pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC); + if (pkey == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a private key"); + RETURN_FALSE; + } + + if (method == NULL || Z_TYPE_P(method) == IS_LONG) { + if (method != NULL) { + signature_algo = Z_LVAL_P(method); + } + mdtype = php_openssl_get_evp_md_from_algo(signature_algo); + } else if (Z_TYPE_P(method) == IS_STRING) { + mdtype = EVP_get_digestbyname(Z_STRVAL_P(method)); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } + if (!mdtype) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } + + siglen = EVP_PKEY_size(pkey); + sigbuf = emalloc(siglen + 1); + + EVP_SignInit(&md_ctx, mdtype); + EVP_SignUpdate(&md_ctx, data, data_len); + if (EVP_SignFinal (&md_ctx, sigbuf,(unsigned int *)&siglen, pkey)) { + zval_dtor(signature); + sigbuf[siglen] = '\0'; + ZVAL_STRINGL(signature, (char *)sigbuf, siglen, 0); + RETVAL_TRUE; + } else { + efree(sigbuf); + RETVAL_FALSE; + } + EVP_MD_CTX_cleanup(&md_ctx); + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } +} +/* }}} */ + +/* {{{ proto int openssl_verify(string data, string signature, mixed key[, mixed method]) + Verifys data */ +PHP_FUNCTION(openssl_verify) +{ + zval **key; + EVP_PKEY *pkey; + int err; + EVP_MD_CTX md_ctx; + const EVP_MD *mdtype; + long keyresource = -1; + char * data; int data_len; + char * signature; int signature_len; + zval *method = NULL; + long signature_algo = OPENSSL_ALGO_SHA1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|z", &data, &data_len, &signature, &signature_len, &key, &method) == FAILURE) { + return; + } + + if (method == NULL || Z_TYPE_P(method) == IS_LONG) { + if (method != NULL) { + signature_algo = Z_LVAL_P(method); + } + mdtype = php_openssl_get_evp_md_from_algo(signature_algo); + } else if (Z_TYPE_P(method) == IS_STRING) { + mdtype = EVP_get_digestbyname(Z_STRVAL_P(method)); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } + if (!mdtype) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } + + pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC); + if (pkey == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a public key"); + RETURN_FALSE; + } + + EVP_VerifyInit (&md_ctx, mdtype); + EVP_VerifyUpdate (&md_ctx, data, data_len); + err = EVP_VerifyFinal (&md_ctx, (unsigned char *)signature, signature_len, pkey); + EVP_MD_CTX_cleanup(&md_ctx); + + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } + RETURN_LONG(err); +} +/* }}} */ + +/* {{{ proto int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys) + Seals data */ +PHP_FUNCTION(openssl_seal) +{ + zval *pubkeys, **pubkey, *sealdata, *ekeys; + HashTable *pubkeysht; + HashPosition pos; + EVP_PKEY **pkeys; + long * key_resources; /* so we know what to cleanup */ + int i, len1, len2, *eksl, nkeys; + unsigned char *buf = NULL, **eks; + char * data; int data_len; + char *method =NULL; + int method_len = 0; + const EVP_CIPHER *cipher; + EVP_CIPHER_CTX ctx; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szza/|s", &data, &data_len, &sealdata, &ekeys, &pubkeys, &method, &method_len) == FAILURE) { + return; + } + + pubkeysht = HASH_OF(pubkeys); + nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0; + if (!nkeys) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Fourth argument to openssl_seal() must be a non-empty array"); + RETURN_FALSE; + } + + if (method) { + cipher = EVP_get_cipherbyname(method); + if (!cipher) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } + } else { + cipher = EVP_rc4(); + } + + pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0); + eksl = safe_emalloc(nkeys, sizeof(*eksl), 0); + eks = safe_emalloc(nkeys, sizeof(*eks), 0); + memset(eks, 0, sizeof(*eks) * nkeys); + key_resources = safe_emalloc(nkeys, sizeof(long), 0); + memset(key_resources, 0, sizeof(*key_resources) * nkeys); + + /* get the public keys we are using to seal this data */ + zend_hash_internal_pointer_reset_ex(pubkeysht, &pos); + i = 0; + while (zend_hash_get_current_data_ex(pubkeysht, (void **) &pubkey, + &pos) == SUCCESS) { + pkeys[i] = php_openssl_evp_from_zval(pubkey, 1, NULL, 0, &key_resources[i] TSRMLS_CC); + if (pkeys[i] == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "not a public key (%dth member of pubkeys)", i+1); + RETVAL_FALSE; + goto clean_exit; + } + eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1); + zend_hash_move_forward_ex(pubkeysht, &pos); + i++; + } + + if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) { + RETVAL_FALSE; + goto clean_exit; + } + +#if 0 + /* Need this if allow ciphers that require initialization vector */ + ivlen = EVP_CIPHER_CTX_iv_length(&ctx); + iv = ivlen ? emalloc(ivlen + 1) : NULL; +#endif + /* allocate one byte extra to make room for \0 */ + buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx)); + + if (!EVP_SealInit(&ctx, cipher, eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { + RETVAL_FALSE; + efree(buf); + goto clean_exit; + } + + EVP_SealFinal(&ctx, buf + len1, &len2); + + if (len1 + len2 > 0) { + zval_dtor(sealdata); + buf[len1 + len2] = '\0'; + buf = erealloc(buf, len1 + len2 + 1); + ZVAL_STRINGL(sealdata, (char *)buf, len1 + len2, 0); + + zval_dtor(ekeys); + array_init(ekeys); + for (i=0; i<nkeys; i++) { + eks[i][eksl[i]] = '\0'; + add_next_index_stringl(ekeys, erealloc(eks[i], eksl[i] + 1), eksl[i], 0); + eks[i] = NULL; + } +#if 0 + /* If allow ciphers that need IV, we need this */ + zval_dtor(*ivec); + if (ivlen) { + iv[ivlen] = '\0'; + ZVAL_STRINGL(*ivec, erealloc(iv, ivlen + 1), ivlen, 0); + } else { + ZVAL_EMPTY_STRING(*ivec); + } +#endif + } else { + efree(buf); + } + RETVAL_LONG(len1 + len2); + +clean_exit: + for (i=0; i<nkeys; i++) { + if (key_resources[i] == -1) { + EVP_PKEY_free(pkeys[i]); + } + if (eks[i]) { + efree(eks[i]); + } + } + efree(eks); + efree(eksl); + efree(pkeys); + efree(key_resources); +} +/* }}} */ + +/* {{{ proto bool openssl_open(string data, &string opendata, string ekey, mixed privkey) + Opens data */ +PHP_FUNCTION(openssl_open) +{ + zval **privkey, *opendata; + EVP_PKEY *pkey; + int len1, len2; + unsigned char *buf; + long keyresource = -1; + EVP_CIPHER_CTX ctx; + char * data; int data_len; + char * ekey; int ekey_len; + char *method =NULL; + int method_len = 0; + const EVP_CIPHER *cipher; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szsZ|s", &data, &data_len, &opendata, &ekey, &ekey_len, &privkey, &method, &method_len) == FAILURE) { + return; + } + + pkey = php_openssl_evp_from_zval(privkey, 0, "", 0, &keyresource TSRMLS_CC); + if (pkey == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 4 into a private key"); + RETURN_FALSE; + } + + if (method) { + cipher = EVP_get_cipherbyname(method); + if (!cipher) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } + } else { + cipher = EVP_rc4(); + } + + buf = emalloc(data_len + 1); + + if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, ekey_len, NULL, pkey) && EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { + if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) { + efree(buf); + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } + } else { + efree(buf); + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } + if (keyresource == -1) { + EVP_PKEY_free(pkey); + } + zval_dtor(opendata); + buf[len1 + len2] = '\0'; + ZVAL_STRINGL(opendata, erealloc(buf, len1 + len2 + 1), len1 + len2, 0); + RETURN_TRUE; +} +/* }}} */ + +/* SSL verification functions */ + +#define GET_VER_OPT(name) (stream->context && SUCCESS == php_stream_context_get_option(stream->context, "ssl", name, &val)) +#define GET_VER_OPT_STRING(name, str) if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_PP(val); } + +static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */ +{ + php_stream *stream; + SSL *ssl; + X509 *err_cert; + int err, depth, ret; + zval **val; + + ret = preverify_ok; + + /* determine the status for the current cert */ + err_cert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + depth = X509_STORE_CTX_get_error_depth(ctx); + + /* conjure the stream & context to use */ + ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + stream = (php_stream*)SSL_get_ex_data(ssl, ssl_stream_data_index); + + /* if allow_self_signed is set, make sure that verification succeeds */ + if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) { + ret = 1; + } + + /* check the depth */ + if (GET_VER_OPT("verify_depth")) { + convert_to_long_ex(val); + + if (depth > Z_LVAL_PP(val)) { + ret = 0; + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); + } + } + + return ret; + +} +/* }}} */ + +int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC) /* {{{ */ +{ + zval **val = NULL; + char *cnmatch = NULL; + X509_NAME *name; + char buf[1024]; + int err; + + /* verification is turned off */ + if (!(GET_VER_OPT("verify_peer") && zval_is_true(*val))) { + return SUCCESS; + } + + if (peer == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not get peer certificate"); + return FAILURE; + } + + err = SSL_get_verify_result(ssl); + switch (err) { + case X509_V_OK: + /* fine */ + break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + if (GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) { + /* allowed */ + break; + } + /* not allowed, so fall through */ + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not verify peer: code:%d %s", err, X509_verify_cert_error_string(err)); + return FAILURE; + } + + /* if the cert passed the usual checks, apply our own local policies now */ + + name = X509_get_subject_name(peer); + + /* Does the common name match ? (used primarily for https://) */ + GET_VER_OPT_STRING("CN_match", cnmatch); + if (cnmatch) { + int match = 0; + int name_len = X509_NAME_get_text_by_NID(name, NID_commonName, buf, sizeof(buf)); + + if (name_len == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN"); + return FAILURE; + } else if (name_len != strlen(buf)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", name_len, buf); + return FAILURE; + } + + match = strcmp(cnmatch, buf) == 0; + if (!match && strlen(buf) > 3 && buf[0] == '*' && buf[1] == '.') { + /* Try wildcard */ + + if (strchr(buf+2, '.')) { + char *tmp = strstr(cnmatch, buf+1); + + match = tmp && strcmp(tmp, buf+2) && tmp == strchr(cnmatch, '.'); + } + } + + if (!match) { + /* didn't match */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", name_len, buf, cnmatch); + return FAILURE; + } + } + + return SUCCESS; +} +/* }}} */ + +static int passwd_callback(char *buf, int num, int verify, void *data) /* {{{ */ +{ + php_stream *stream = (php_stream *)data; + zval **val = NULL; + char *passphrase = NULL; + /* TODO: could expand this to make a callback into PHP user-space */ + + GET_VER_OPT_STRING("passphrase", passphrase); + + if (passphrase) { + if (Z_STRLEN_PP(val) < num - 1) { + memcpy(buf, Z_STRVAL_PP(val), Z_STRLEN_PP(val)+1); + return Z_STRLEN_PP(val); + } + } + return 0; +} +/* }}} */ + +SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{ */ +{ + zval **val = NULL; + char *cafile = NULL; + char *capath = NULL; + char *certfile = NULL; + char *cipherlist = NULL; + int ok = 1; + + ERR_clear_error(); + + /* look at context options in the stream and set appropriate verification flags */ + if (GET_VER_OPT("verify_peer") && zval_is_true(*val)) { + + /* turn on verification callback */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); + + /* CA stuff */ + GET_VER_OPT_STRING("cafile", cafile); + GET_VER_OPT_STRING("capath", capath); + + if (cafile || capath) { + if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set verify locations `%s' `%s'", cafile, capath); + return NULL; + } + } + + if (GET_VER_OPT("verify_depth")) { + convert_to_long_ex(val); + SSL_CTX_set_verify_depth(ctx, Z_LVAL_PP(val)); + } + } else { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + } + + /* callback for the passphrase (for localcert) */ + if (GET_VER_OPT("passphrase")) { + SSL_CTX_set_default_passwd_cb_userdata(ctx, stream); + SSL_CTX_set_default_passwd_cb(ctx, passwd_callback); + } + + GET_VER_OPT_STRING("ciphers", cipherlist); + if (!cipherlist) { + cipherlist = "DEFAULT"; + } + if (SSL_CTX_set_cipher_list(ctx, cipherlist) != 1) { + return NULL; + } + + GET_VER_OPT_STRING("local_cert", certfile); + if (certfile) { + X509 *cert = NULL; + EVP_PKEY *key = NULL; + SSL *tmpssl; + char resolved_path_buff[MAXPATHLEN]; + const char * private_key = NULL; + + if (VCWD_REALPATH(certfile, resolved_path_buff)) { + /* a certificate to use for authentication */ + if (SSL_CTX_use_certificate_chain_file(ctx, resolved_path_buff) != 1) { + 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); + return NULL; + } + GET_VER_OPT_STRING("local_pk", private_key); + + if (private_key) { + char resolved_path_buff_pk[MAXPATHLEN]; + if (VCWD_REALPATH(private_key, resolved_path_buff_pk)) { + if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff_pk, SSL_FILETYPE_PEM) != 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff_pk); + return NULL; + } + } + } else { + if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff, SSL_FILETYPE_PEM) != 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff); + return NULL; + } + } + + tmpssl = SSL_new(ctx); + cert = SSL_get_certificate(tmpssl); + + if (cert) { + key = X509_get_pubkey(cert); + EVP_PKEY_copy_parameters(key, SSL_get_privatekey(tmpssl)); + EVP_PKEY_free(key); + } + SSL_free(tmpssl); + + if (!SSL_CTX_check_private_key(ctx)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Private key does not match certificate!"); + } + } + } + if (ok) { + SSL *ssl = SSL_new(ctx); + + if (ssl) { + /* map SSL => stream */ + SSL_set_ex_data(ssl, ssl_stream_data_index, stream); + } + return ssl; + } + + return NULL; +} +/* }}} */ + +static void openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */ +{ + add_next_index_string((zval*)arg, (char*)name->name, 1); +} +/* }}} */ + +static void openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */ +{ + if (name->alias == 0) { + add_next_index_string((zval*)arg, (char*)name->name, 1); + } +} +/* }}} */ + +/* {{{ proto array openssl_get_md_methods([bool aliases = false]) + Return array of available digest methods */ +PHP_FUNCTION(openssl_get_md_methods) +{ + zend_bool aliases = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) { + return; + } + array_init(return_value); + OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH, + aliases ? openssl_add_method_or_alias: openssl_add_method, + return_value); +} +/* }}} */ + +/* {{{ proto array openssl_get_cipher_methods([bool aliases = false]) + Return array of available cipher methods */ +PHP_FUNCTION(openssl_get_cipher_methods) +{ + zend_bool aliases = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) { + return; + } + array_init(return_value); + OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, + aliases ? openssl_add_method_or_alias: openssl_add_method, + return_value); +} +/* }}} */ + +/* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false]) + Computes digest hash value for given data using given method, returns raw or binhex encoded string */ +PHP_FUNCTION(openssl_digest) +{ + zend_bool raw_output = 0; + char *data, *method; + int data_len, method_len; + const EVP_MD *mdtype; + EVP_MD_CTX md_ctx; + int siglen; + unsigned char *sigbuf; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) { + return; + } + mdtype = EVP_get_digestbyname(method); + if (!mdtype) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm"); + RETURN_FALSE; + } + + siglen = EVP_MD_size(mdtype); + sigbuf = emalloc(siglen + 1); + + EVP_DigestInit(&md_ctx, mdtype); + EVP_DigestUpdate(&md_ctx, (unsigned char *)data, data_len); + if (EVP_DigestFinal (&md_ctx, (unsigned char *)sigbuf, (unsigned int *)&siglen)) { + if (raw_output) { + sigbuf[siglen] = '\0'; + RETVAL_STRINGL((char *)sigbuf, siglen, 0); + } else { + int digest_str_len = siglen * 2; + char *digest_str = emalloc(digest_str_len + 1); + + make_digest_ex(digest_str, sigbuf, siglen); + efree(sigbuf); + RETVAL_STRINGL(digest_str, digest_str_len, 0); + } + } else { + efree(sigbuf); + RETVAL_FALSE; + } +} +/* }}} */ + +static zend_bool php_openssl_validate_iv(char **piv, int *piv_len, int iv_required_len TSRMLS_DC) +{ + char *iv_new; + + /* Best case scenario, user behaved */ + if (*piv_len == iv_required_len) { + return 0; + } + + iv_new = ecalloc(1, iv_required_len + 1); + + if (*piv_len <= 0) { + /* BC behavior */ + *piv_len = iv_required_len; + *piv = iv_new; + return 1; + } + + if (*piv_len < iv_required_len) { + 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); + memcpy(iv_new, *piv, *piv_len); + *piv_len = iv_required_len; + *piv = iv_new; + return 1; + } + + 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); + memcpy(iv_new, *piv, iv_required_len); + *piv_len = iv_required_len; + *piv = iv_new; + return 1; + +} + +/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv='']]) + Encrypts given data with given method and key, returns raw or base64 encoded string */ +PHP_FUNCTION(openssl_encrypt) +{ + long options = 0; + char *data, *method, *password, *iv = ""; + int data_len, method_len, password_len, iv_len = 0, max_iv_len; + const EVP_CIPHER *cipher_type; + EVP_CIPHER_CTX cipher_ctx; + int i=0, outlen, keylen; + unsigned char *outbuf, *key; + zend_bool free_iv; + + 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) { + return; + } + cipher_type = EVP_get_cipherbyname(method); + if (!cipher_type) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); + RETURN_FALSE; + } + + keylen = EVP_CIPHER_key_length(cipher_type); + if (keylen > password_len) { + key = emalloc(keylen); + memset(key, 0, keylen); + memcpy(key, password, password_len); + } else { + key = (unsigned char*)password; + } + + max_iv_len = EVP_CIPHER_iv_length(cipher_type); + if (iv_len <= 0 && max_iv_len > 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended"); + } + free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len TSRMLS_CC); + + outlen = data_len + EVP_CIPHER_block_size(cipher_type); + outbuf = emalloc(outlen + 1); + + EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL); + if (password_len > keylen) { + EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len); + } + EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv); + if (options & OPENSSL_ZERO_PADDING) { + EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0); + } + if (data_len > 0) { + EVP_EncryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len); + } + outlen = i; + if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) { + outlen += i; + if (options & OPENSSL_RAW_DATA) { + outbuf[outlen] = '\0'; + RETVAL_STRINGL((char *)outbuf, outlen, 0); + } else { + int base64_str_len; + char *base64_str; + + base64_str = (char*)php_base64_encode(outbuf, outlen, &base64_str_len); + efree(outbuf); + RETVAL_STRINGL(base64_str, base64_str_len, 0); + } + } else { + efree(outbuf); + RETVAL_FALSE; + } + if (key != (unsigned char*)password) { + efree(key); + } + if (free_iv) { + efree(iv); + } + EVP_CIPHER_CTX_cleanup(&cipher_ctx); +} +/* }}} */ + +/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = '']]) + Takes raw or base64 encoded string and dectupt it using given method and key */ +PHP_FUNCTION(openssl_decrypt) +{ + long options = 0; + char *data, *method, *password, *iv = ""; + int data_len, method_len, password_len, iv_len = 0; + const EVP_CIPHER *cipher_type; + EVP_CIPHER_CTX cipher_ctx; + int i, outlen, keylen; + unsigned char *outbuf, *key; + int base64_str_len; + char *base64_str = NULL; + zend_bool free_iv; + + 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) { + return; + } + + if (!method_len) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); + RETURN_FALSE; + } + + cipher_type = EVP_get_cipherbyname(method); + if (!cipher_type) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); + RETURN_FALSE; + } + + if (!(options & OPENSSL_RAW_DATA)) { + base64_str = (char*)php_base64_decode((unsigned char*)data, data_len, &base64_str_len); + if (!base64_str) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to base64 decode the input"); + RETURN_FALSE; + } + data_len = base64_str_len; + data = base64_str; + } + + keylen = EVP_CIPHER_key_length(cipher_type); + if (keylen > password_len) { + key = emalloc(keylen); + memset(key, 0, keylen); + memcpy(key, password, password_len); + } else { + key = (unsigned char*)password; + } + + free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type) TSRMLS_CC); + + outlen = data_len + EVP_CIPHER_block_size(cipher_type); + outbuf = emalloc(outlen + 1); + + EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL); + if (password_len > keylen) { + EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len); + } + EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv); + if (options & OPENSSL_ZERO_PADDING) { + EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0); + } + EVP_DecryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len); + outlen = i; + if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) { + outlen += i; + outbuf[outlen] = '\0'; + RETVAL_STRINGL((char *)outbuf, outlen, 0); + } else { + efree(outbuf); + RETVAL_FALSE; + } + if (key != (unsigned char*)password) { + efree(key); + } + if (free_iv) { + efree(iv); + } + if (base64_str) { + efree(base64_str); + } + EVP_CIPHER_CTX_cleanup(&cipher_ctx); +} +/* }}} */ + +/* {{{ proto int openssl_cipher_iv_length(string $method) */ +PHP_FUNCTION(openssl_cipher_iv_length) +{ + char *method; + int method_len; + const EVP_CIPHER *cipher_type; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len) == FAILURE) { + return; + } + + if (!method_len) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); + RETURN_FALSE; + } + + cipher_type = EVP_get_cipherbyname(method); + if (!cipher_type) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); + RETURN_FALSE; + } + + RETURN_LONG(EVP_CIPHER_iv_length(cipher_type)); +} +/* }}} */ + + +/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key) + Computes shared sicret for public value of remote DH key and local DH key */ +PHP_FUNCTION(openssl_dh_compute_key) +{ + zval *key; + char *pub_str; + int pub_len; + EVP_PKEY *pkey; + BIGNUM *pub; + char *data; + int len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr", &pub_str, &pub_len, &key) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key); + if (!pkey || EVP_PKEY_type(pkey->type) != EVP_PKEY_DH || !pkey->pkey.dh) { + RETURN_FALSE; + } + + pub = BN_bin2bn((unsigned char*)pub_str, pub_len, NULL); + + data = emalloc(DH_size(pkey->pkey.dh) + 1); + len = DH_compute_key((unsigned char*)data, pub, pkey->pkey.dh); + + if (len >= 0) { + data[len] = 0; + RETVAL_STRINGL(data, len, 0); + } else { + efree(data); + RETVAL_FALSE; + } + + BN_free(pub); +} +/* }}} */ + +/* {{{ proto string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result]) + Returns a string of the length specified filled with random pseudo bytes */ +PHP_FUNCTION(openssl_random_pseudo_bytes) +{ + long buffer_length; + unsigned char *buffer = NULL; + zval *zstrong_result_returned = NULL; + int strong_result = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) { + return; + } + + if (buffer_length <= 0) { + RETURN_FALSE; + } + + if (zstrong_result_returned) { + zval_dtor(zstrong_result_returned); + ZVAL_BOOL(zstrong_result_returned, 0); + } + + buffer = emalloc(buffer_length + 1); + +#ifdef PHP_WIN32 + strong_result = 1; + /* random/urandom equivalent on Windows */ + if (php_win32_get_random_bytes(buffer, (size_t) buffer_length) == FAILURE) { + efree(buffer); + if (zstrong_result_returned) { + ZVAL_BOOL(zstrong_result_returned, 0); + } + RETURN_FALSE; + } +#else + if ((strong_result = RAND_pseudo_bytes(buffer, buffer_length)) < 0) { + efree(buffer); + if (zstrong_result_returned) { + ZVAL_BOOL(zstrong_result_returned, 0); + } + RETURN_FALSE; + } +#endif + + buffer[buffer_length] = 0; + RETVAL_STRINGL((char *)buffer, buffer_length, 0); + + if (zstrong_result_returned) { + ZVAL_BOOL(zstrong_result_returned, strong_result); + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 8 + * c-basic-offset: 8 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/openssl/openssl.dsp b/ext/openssl/openssl.dsp new file mode 100644 index 0000000..857666a --- /dev/null +++ b/ext/openssl/openssl.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="openssl" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=openssl - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "openssl.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "openssl.mak" CFG="openssl - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "openssl - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "openssl - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "openssl - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPENSSL_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /D ZEND_DEBUG=0 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "COMPILE_DL_OPENSSL" /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_OPENSSL_EXT=1 /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x407 /d "NDEBUG"
+# ADD RSC /l 0x407 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib ssleay32.lib libeay32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS/php_openssl.dll" /libpath:"..\..\Release_TS" /libpath:"..\..\Release_TS_Inline"
+
+!ELSEIF "$(CFG)" == "openssl - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPENSSL_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /D ZEND_DEBUG=1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "COMPILE_DL_OPENSSL" /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_OPENSSL_EXT=1 /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x407 /d "_DEBUG"
+# ADD RSC /l 0x407 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 php5ts_debug.lib ssleay32.lib libeay32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"..\..\Debug_TS/php_openssl.dll" /pdbtype:sept /libpath:"..\..\Debug_TS"
+
+!ENDIF
+
+# Begin Target
+
+# Name "openssl - Win32 Release_TS"
+# Name "openssl - Win32 Debug_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\openssl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\xp_ssl.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\php_openssl.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/ext/openssl/openssl.mak b/ext/openssl/openssl.mak new file mode 100644 index 0000000..a0c6fa4 --- /dev/null +++ b/ext/openssl/openssl.mak @@ -0,0 +1,185 @@ +# Temporarily here -- later may go into some batch file +# which will set this as an environment variable +PROJECT_ROOT = ..\.. + +# Module details +MODULE_NAME = php_ossl +MODULE_DESC = "PHP 5 - OpenSSL Extension" +VMAJ = 1 +VMIN = 0 +VREV = 0 + +#include the common settings +include $(PROJECT_ROOT)/netware/common.mif + +# OpenSSL directory +OSSL_DIR = P:/APPS/script/sw/OpenSSL + +# Build type defaults to 'release' +ifndef BUILD +BUILD = release +endif + +# Extensions of all input and output files +.SUFFIXES: +.SUFFIXES: .nlm .lib .obj .cpp .c .msg .mlc .mdb .xdc .d + +# Source files +C_SRC = openssl.c \ + xp_ssl.c \ + start.c \ + +CPP_SRC_NODIR = $(notdir $(CPP_SRC)) +C_SRC_NODIR = $(notdir $(C_SRC)) +SRC_DIR = $(dir $(CPP_SRC) $(C_SRC)) + +# Library files +LIBRARY = $(OSSL_DIR)/lib/RSAglue.lib \ + $(OSSL_DIR)/lib/crypto.lib \ + $(OSSL_DIR)/lib/ssl.lib + +# Destination directories and files +OBJ_DIR = $(BUILD) +FINAL_DIR = $(BUILD) +MAP_FILE = $(FINAL_DIR)\$(MODULE_NAME).map +OBJECTS = $(addprefix $(OBJ_DIR)/,$(CPP_SRC_NODIR:.c=.obj) $(C_SRC_NODIR:.c=.obj)) +DEPDS = $(addprefix $(OBJ_DIR)/,$(CPP_SRC_NODIR:.c=.d) $(C_SRC_NODIR:.c=.d)) + +# Binary file +ifndef BINARY + BINARY=$(FINAL_DIR)\$(MODULE_NAME).nlm +endif + +# Compile flags +C_FLAGS += -c -maxerrors 25 -msgstyle gcc +C_FLAGS += -wchar_t on -bool on -processor Pentium +C_FLAGS += -nostdinc -nosyspath +C_FLAGS += -relax_pointers # To remove type-casting errors +C_FLAGS += -DNETWARE -DZTS +C_FLAGS += -DUSE_OLD_FUNCTIONS -DCOMPILE_DL_OPENSSL=1 + +C_FLAGS += -I. -I$(PROJECT_ROOT) -I$(PROJECT_ROOT)/main +C_FLAGS += -I$(PROJECT_ROOT)/ext/standard -I$(PROJECT_ROOT)/netware +C_FLAGS += -I$(PROJECT_ROOT)/zend -I$(PROJECT_ROOT)/tsrm +C_FLAGS += -I- -I$(SDK_DIR)/include -I$(MWCIncludes) +C_FLAGS += -I$(OSSL_DIR)/include + +ifndef STACK_SIZE +STACK_SIZE=8192 +endif + +# Extra stuff based on debug / release builds +ifeq '$(BUILD)' 'debug' + SYM_FILE = $(FINAL_DIR)\$(MODULE_NAME).sym + C_FLAGS += -inline smart -sym on -sym codeview4 -opt off -opt intrinsics -sym internal -DDEBUGGING -DDKFBPON + C_FLAGS += -exc cw -DZEND_DEBUG=1 + LD_FLAGS += -sym on -sym codeview4 -osym $(SYM_FILE) + export MWLibraryFiles=$(SDK_DIR)/imports/libcpre.o;mwcrtld.lib +else + C_FLAGS += -opt all -inline on -inline smart -inline auto -sym off + C_FLAGS += -opt intrinsics -opt level=4 -DZEND_DEBUG=0 + LD_FLAGS += -sym off + export MWLibraryFiles=$(SDK_DIR)/imports/libcpre.o;mwcrtl.lib +endif + + +# Dependencies +MODULE = LibC \ + phplib +IMPORT = @$(SDK_DIR)/imports/libc.imp \ + @$(SDK_DIR)/imports/ws2nlm.imp \ + @$(SDK_DIR)/imports/netware.imp \ + @$(MPK_DIR)/import/mpkOrg.imp \ + @$(PROJECT_ROOT)/netware/phplib.imp +EXPORT = ($(MODULE_NAME)) get_module +API = OutputToScreen + +# Virtual paths +vpath %.cpp . +vpath %.c . ..\..\netware +vpath %.obj $(OBJ_DIR) + + +all: prebuild project + +.PHONY: all + +prebuild: + @if not exist $(OBJ_DIR) md $(OBJ_DIR) + +project: $(BINARY) + @echo Build complete. + +$(OBJ_DIR)/%.d: %.cpp + @echo Building Dependencies for $(<F) + @$(CC) -M $< $(C_FLAGS) -o $@ + +$(OBJ_DIR)/%.d: %.c + @echo Building Dependencies for $(<F) + @$(CC) -M $< $(C_FLAGS) -o $@ + +$(OBJ_DIR)/%.obj: %.cpp + @echo Compiling $?... + @$(CC) $< $(C_FLAGS) -o $@ + +$(OBJ_DIR)/%.obj: %.c + @echo Compiling $?... + @$(CC) $< $(C_FLAGS) -o $@ + + +$(BINARY): $(OBJECTS) + @echo Import $(IMPORT) > $(basename $@).def +ifdef API + @echo Import $(API) >> $(basename $@).def +endif + @echo Module $(MODULE) >> $(basename $@).def +ifdef EXPORT + @echo Export $(EXPORT) >> $(basename $@).def +endif + @echo AutoUnload >> $(basename $@).def +ifeq '$(BUILD)' 'debug' + @echo Debug >> $(basename $@).def +endif + @echo Flag_On 0x00000008 >> $(basename $@).def + @echo Start _LibCPrelude >> $(basename $@).def + @echo Exit _LibCPostlude >> $(basename $@).def + +# Two functions imported to build the openssl extension + @echo Import GetProcessSwitchCount >> $(basename $@).def + @echo Import RunningProcess >> $(basename $@).def + + $(MPKTOOL) $(XDCFLAGS) $(basename $@).xdc + @echo xdcdata $(basename $@).xdc >> $(basename $@).def + + @echo Linking $@... + @echo $(LD_FLAGS) -commandfile $(basename $@).def > $(basename $@).link + + @echo $(LIBRARY) $(OBJECTS) >> $(basename $@).link + + @$(LINK) @$(basename $@).link + + +.PHONY: clean +clean: cleanobj cleanbin + +.PHONY: cleand +cleand: + @echo Deleting all dependency files... + -@del "$(OBJ_DIR)\*.d" + +.PHONY: cleanobj +cleanobj: + @echo Deleting all object files... + -@del "$(OBJ_DIR)\*.obj" + +.PHONY: cleanbin +cleanbin: + @echo Deleting binary files... + -@del "$(FINAL_DIR)\$(MODULE_NAME).nlm" + @echo Deleting MAP, DEF files, etc.... + -@del "$(FINAL_DIR)\$(MODULE_NAME).map" + -@del "$(FINAL_DIR)\$(MODULE_NAME).def" + -@del "$(FINAL_DIR)\$(MODULE_NAME).link" +ifeq '$(BUILD)' 'debug' + -@del $(FINAL_DIR)\$(MODULE_NAME).sym +endif diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h new file mode 100644 index 0000000..427a643 --- /dev/null +++ b/ext/openssl/php_openssl.h @@ -0,0 +1,93 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Stig Venaas <venaas@php.net> | + | Wez Furlong <wez@thebrainroom.com | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_OPENSSL_H +#define PHP_OPENSSL_H +/* HAVE_OPENSSL would include SSL MySQL stuff */ +#ifdef HAVE_OPENSSL_EXT +extern zend_module_entry openssl_module_entry; +#define phpext_openssl_ptr &openssl_module_entry + +#define OPENSSL_RAW_DATA 1 +#define OPENSSL_ZERO_PADDING 2 + +php_stream_transport_factory_func php_openssl_ssl_socket_factory; + +PHP_MINIT_FUNCTION(openssl); +PHP_MSHUTDOWN_FUNCTION(openssl); +PHP_MINFO_FUNCTION(openssl); + +PHP_FUNCTION(openssl_pkey_get_private); +PHP_FUNCTION(openssl_pkey_get_public); +PHP_FUNCTION(openssl_pkey_free); +PHP_FUNCTION(openssl_pkey_new); +PHP_FUNCTION(openssl_pkey_export); +PHP_FUNCTION(openssl_pkey_export_to_file); +PHP_FUNCTION(openssl_pkey_get_details); + +PHP_FUNCTION(openssl_sign); +PHP_FUNCTION(openssl_verify); +PHP_FUNCTION(openssl_seal); +PHP_FUNCTION(openssl_open); +PHP_FUNCTION(openssl_private_encrypt); +PHP_FUNCTION(openssl_private_decrypt); +PHP_FUNCTION(openssl_public_encrypt); +PHP_FUNCTION(openssl_public_decrypt); + +PHP_FUNCTION(openssl_pkcs7_verify); +PHP_FUNCTION(openssl_pkcs7_decrypt); +PHP_FUNCTION(openssl_pkcs7_sign); +PHP_FUNCTION(openssl_pkcs7_encrypt); + +PHP_FUNCTION(openssl_error_string); + +PHP_FUNCTION(openssl_x509_read); +PHP_FUNCTION(openssl_x509_free); +PHP_FUNCTION(openssl_x509_parse); +PHP_FUNCTION(openssl_x509_checkpurpose); +PHP_FUNCTION(openssl_x509_export); +PHP_FUNCTION(openssl_x509_export_to_file); +PHP_FUNCTION(openssl_x509_check_private_key); + +PHP_FUNCTION(openssl_pkcs12_export); +PHP_FUNCTION(openssl_pkcs12_export_to_file); +PHP_FUNCTION(openssl_pkcs12_read); + +PHP_FUNCTION(openssl_csr_new); +PHP_FUNCTION(openssl_csr_export); +PHP_FUNCTION(openssl_csr_export_to_file); +PHP_FUNCTION(openssl_csr_sign); +PHP_FUNCTION(openssl_csr_get_subject); +PHP_FUNCTION(openssl_csr_get_public_key); +#else + +#define phpext_openssl_ptr NULL + +#endif + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/openssl/tests/001.phpt b/ext/openssl/tests/001.phpt new file mode 100644 index 0000000..4ca9970 --- /dev/null +++ b/ext/openssl/tests/001.phpt @@ -0,0 +1,74 @@ +--TEST-- +OpenSSL private key functions +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +if (!@openssl_pkey_new()) die("skip cannot create private key"); +?> +--FILE-- +<?php +echo "Creating private key\n"; + +/* stack up some entropy; performance is not critical, + * and being slow will most likely even help the test. + */ +for ($z = "", $i = 0; $i < 1024; $i++) { + $z .= $i * $i; + if (function_exists("usleep")) + usleep($i); +} + +$privkey = openssl_pkey_new(); + +if ($privkey === false) + die("failed to create private key"); + +$passphrase = "banana"; +$key_file_name = tempnam("/tmp", "ssl"); +if ($key_file_name === false) + die("failed to get a temporary filename!"); + +echo "Export key to file\n"; + +openssl_pkey_export_to_file($privkey, $key_file_name, $passphrase) or die("failed to export to file $key_file_name"); + +echo "Load key from file - array syntax\n"; + +$loaded_key = openssl_pkey_get_private(array("file://$key_file_name", $passphrase)); + +if ($loaded_key === false) + die("failed to load key using array syntax"); + +openssl_pkey_free($loaded_key); + +echo "Load key using direct syntax\n"; + +$loaded_key = openssl_pkey_get_private("file://$key_file_name", $passphrase); + +if ($loaded_key === false) + die("failed to load key using direct syntax"); + +openssl_pkey_free($loaded_key); + +echo "Load key manually and use string syntax\n"; + +$key_content = file_get_contents($key_file_name); +$loaded_key = openssl_pkey_get_private($key_content, $passphrase); + +if ($loaded_key === false) + die("failed to load key using string syntax"); + +openssl_pkey_free($loaded_key); + +echo "OK!\n"; + +@unlink($key_file_name); + +?> +--EXPECT-- +Creating private key +Export key to file +Load key from file - array syntax +Load key using direct syntax +Load key manually and use string syntax +OK! diff --git a/ext/openssl/tests/002.phpt b/ext/openssl/tests/002.phpt new file mode 100644 index 0000000..dd4f04a --- /dev/null +++ b/ext/openssl/tests/002.phpt @@ -0,0 +1,32 @@ +--TEST-- +openssl_seal() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php + +$a = 1; +$b = array(1); +$c = array(1); +$d = array(1); + +var_dump(openssl_seal($a, $b, $c, $d)); +var_dump(openssl_seal($a, $a, $a, array())); +var_dump(openssl_seal($c, $c, $c, 1)); +var_dump(openssl_seal($b, $b, $b, "")); + +echo "Done\n"; +?> +--EXPECTF-- +Warning: openssl_seal(): not a public key (1th member of pubkeys) in %s on line %d +bool(false) + +Warning: openssl_seal(): Fourth argument to openssl_seal() must be a non-empty array in %s on line %d +bool(false) + +Warning: openssl_seal() expects parameter 1 to be string, array given in %s on line %d +NULL + +Warning: openssl_seal() expects parameter 1 to be string, array given in %s on line %d +NULL +Done diff --git a/ext/openssl/tests/003.phpt b/ext/openssl/tests/003.phpt new file mode 100644 index 0000000..92c8c85 --- /dev/null +++ b/ext/openssl/tests/003.phpt @@ -0,0 +1,43 @@ +--TEST-- +openssl_pkcs7_decrypt() and invalid parameters +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php + +function myErrorHandler($errno, $errstr, $errfile, $errline) { +var_dump($errstr); +} +set_error_handler("myErrorHandler"); + +$a = 1; +$b = 1; +$c = new stdclass; +$d = new stdclass; + +var_dump(openssl_pkcs7_decrypt($a, $b, $c, $d)); +var_dump($c); + +var_dump(openssl_pkcs7_decrypt($b, $b, $b, $b)); +var_dump(openssl_pkcs7_decrypt($a, $b, "", "")); +var_dump(openssl_pkcs7_decrypt($a, $b, true, false)); +var_dump(openssl_pkcs7_decrypt($a, $b, 0, 0)); + +echo "Done\n"; +?> +--EXPECTF-- +string(57) "Object of class stdClass could not be converted to string" +string(45) "Object of class stdClass to string conversion" +string(66) "openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert" +bool(false) +object(stdClass)#1 (0) { +} +string(66) "openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert" +bool(false) +string(66) "openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert" +bool(false) +string(66) "openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert" +bool(false) +string(66) "openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert" +bool(false) +Done diff --git a/ext/openssl/tests/004.phpt b/ext/openssl/tests/004.phpt new file mode 100644 index 0000000..508ccab --- /dev/null +++ b/ext/openssl/tests/004.phpt @@ -0,0 +1,34 @@ +--TEST-- +openssl_csr_new() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php + +$a = 1; +var_dump(openssl_csr_new(1,$a)); +var_dump(openssl_csr_new(1,$a,1,1)); +$a = array(); +var_dump(openssl_csr_new(array(), $a, array('config' => __DIR__ . DIRECTORY_SEPARATOR . 'openssl.cnf'), array())); + +//this leaks +$a = array(1,2); +$b = array(1,2); +var_dump(openssl_csr_new($a, $b, array('config' => __DIR__ . DIRECTORY_SEPARATOR . 'openssl.cnf'))); + + +echo "Done\n"; +?> +--EXPECTF-- +Warning: openssl_csr_new() expects parameter 1 to be array, integer given in %s on line %d +NULL + +Warning: openssl_csr_new() expects parameter 1 to be array, integer given in %s on line %d +NULL + +Warning: openssl_csr_new(): key array must be of the form array(0 => key, 1 => phrase) in %s on line %d + +Warning: openssl_csr_new(): add1_attr_by_txt challengePassword_min -> 4 (failed; check error queue and value of string_mask OpenSSL option if illegal characters are reported) in %s on line %d +bool(false) +resource(%d) of type (OpenSSL X.509 CSR) +Done diff --git a/ext/openssl/tests/005.phpt b/ext/openssl/tests/005.phpt new file mode 100644 index 0000000..f7fa201 --- /dev/null +++ b/ext/openssl/tests/005.phpt @@ -0,0 +1,34 @@ +--TEST-- +openssl_csr_get_subject() tests +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +if (!function_exists("utf8_decode")) die("skip"); +?> +--FILE-- +<?php + +$csr = file_get_contents(dirname(__FILE__) . '/005_crt.txt'); +if ($out = openssl_csr_get_subject($csr, 1)) { + var_dump($out); +} +echo "\n"; +$cn = utf8_decode($out['CN']); +var_dump($cn); +--EXPECTF-- +array(6) { + ["C"]=> + string(2) "NL" + ["ST"]=> + string(13) "Noord Brabant" + ["L"]=> + string(4) "Uden" + ["O"]=> + string(10) "Triconnect" + ["OU"]=> + string(10) "Triconnect" + ["CN"]=> + string(15) "*.triconnect.nl" +} + +string(15) "*.triconnect.nl" diff --git a/ext/openssl/tests/005_crt.txt b/ext/openssl/tests/005_crt.txt new file mode 100644 index 0000000..39084bc --- /dev/null +++ b/ext/openssl/tests/005_crt.txt @@ -0,0 +1,22 @@ +-----BEGIN NEW CERTIFICATE REQUEST----- +MIIDYzCCAswCAQAwgYcxCzAJBgNVBAYTAk5MMRYwFAYDVQQIEw1Ob29yZCBCcmFi +YW50MQ0wCwYDVQQHEwRVZGVuMRMwEQYDVQQKEwpUcmljb25uZWN0MRMwEQYDVQQL +EwpUcmljb25uZWN0MScwJQYDVQQDHh4AKgAuAHQAcgBpAGMAbwBuAG4AZQBjAHQA +LgBuAGwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANjE/qKAKgo93162HhtX +OZdvunF5eG/PFK2yn6uAUDWgZciPoKBslgL6a6sK+RdcS7LjWdjWEOOANGzZY1Kk +FelzxrIjIGSGJHC9eubebdu2LWFFM5cEMDiH0QSD9Rdiy7svSLWvngUDYj0wwd+m +iV2duzUFHnusj9iVPpD9s47RAgMBAAGgggGZMBoGCisGAQQBgjcNAgMxDBYKNS4y +LjM3OTAuMjB7BgorBgEEAYI3AgEOMW0wazAOBgNVHQ8BAf8EBAMCBPAwRAYJKoZI +hvcNAQkPBDcwNTAOBggqhkiG9w0DAgICAIAwDgYIKoZIhvcNAwQCAgCAMAcGBSsO +AwIHMAoGCCqGSIb3DQMHMBMGA1UdJQQMMAoGCCsGAQUFBwMBMIH9BgorBgEEAYI3 +DQICMYHuMIHrAgEBHloATQBpAGMAcgBvAHMAbwBmAHQAIABSAFMAQQAgAFMAQwBo +AGEAbgBuAGUAbAAgAEMAcgB5AHAAdABvAGcAcgBhAHAAaABpAGMAIABQAHIAbwB2 +AGkAZABlAHIDgYkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAADANBgkqhkiG9w0BAQUFAAOBgQA4agiUkIblhF+n0wth4vQY+PwMadyaBpA4 +epr4TKL0QEkA0bQBbIERw5dDE3WQi6aVFJe6y870QymBwmKIvfBBfOyyA0IlQq/n +uybhzQNQbSMKF1T82hpfh1w2RwVGaGrw7f6qH+CLyP1ydvBPvmD88HwiibNBBB3c +R23mEEGYUQ== +-----END NEW CERTIFICATE REQUEST----- + diff --git a/ext/openssl/tests/006.phpt b/ext/openssl/tests/006.phpt new file mode 100644 index 0000000..d6e41e4 --- /dev/null +++ b/ext/openssl/tests/006.phpt @@ -0,0 +1,25 @@ +--TEST-- +openssl_pkey_new() with an empty sub-array arg generates a malformed resource +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +/* openssl_pkey_get_details() segfaults when getting the information + from openssl_pkey_new() with an empty sub-array arg */ + +$rsa = array("rsa" => array()); +$dsa = array("dsa" => array()); +$dh = array("dh" => array()); + +openssl_pkey_get_details(openssl_pkey_new($rsa)); +openssl_pkey_get_details(openssl_pkey_new($dsa)); +openssl_pkey_get_details(openssl_pkey_new($dh)); +?> +--EXPECTF-- + +Warning: openssl_pkey_get_details() expects parameter 1 to be resource, boolean given in %s on line %d + +Warning: openssl_pkey_get_details() expects parameter 1 to be resource, boolean given in %s on line %d + +Warning: openssl_pkey_get_details() expects parameter 1 to be resource, boolean given in %s on line %d + diff --git a/ext/openssl/tests/007.phpt b/ext/openssl/tests/007.phpt new file mode 100644 index 0000000..0a74bd3 --- /dev/null +++ b/ext/openssl/tests/007.phpt @@ -0,0 +1,60 @@ +--TEST-- +openssl_x509_read() and openssl_x509_free() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$fp = fopen(dirname(__FILE__) . "/cert.crt","r"); +$a = fread($fp,8192); +fclose($fp); + +$b = "file://" . dirname(__FILE__) . "/cert.crt"; +$c = "invalid cert"; +$d = openssl_x509_read($a); +$e = array(); +$f = array($b); + +var_dump($res = openssl_x509_read($a)); // read cert as a string +openssl_x509_free($res); +var_dump($res); +var_dump($res = openssl_x509_read($b)); // read cert as a filename string +openssl_x509_free($res); +var_dump($res); +var_dump($res = openssl_x509_read($c)); // read an invalid cert, fails +openssl_x509_free($res); +var_dump($res); +var_dump($res = openssl_x509_read($d)); // read cert from a resource +openssl_x509_free($res); +var_dump($res); +var_dump($res = openssl_x509_read($e)); // read an array +openssl_x509_free($res); +var_dump($res); +var_dump($res = openssl_x509_read($f)); // read an array with the filename +openssl_x509_free($res); +var_dump($res); +?> +--EXPECTF-- +resource(%d) of type (OpenSSL X.509) +resource(%d) of type (Unknown) +resource(%d) of type (OpenSSL X.509) +resource(%d) of type (Unknown) + +Warning: openssl_x509_read(): supplied parameter cannot be coerced into an X509 certificate! in %s on line %d +bool(false) + +Warning: openssl_x509_free() expects parameter 1 to be resource, boolean given in %s on line %d +bool(false) +resource(%d) of type (OpenSSL X.509) +resource(%d) of type (Unknown) + +Warning: openssl_x509_read(): supplied parameter cannot be coerced into an X509 certificate! in %s on line %d +bool(false) + +Warning: openssl_x509_free() expects parameter 1 to be resource, boolean given in %s on line %d +bool(false) + +Warning: openssl_x509_read(): supplied parameter cannot be coerced into an X509 certificate! in %s on line %d +bool(false) + +Warning: openssl_x509_free() expects parameter 1 to be resource, boolean given in %s on line %d +bool(false) diff --git a/ext/openssl/tests/008.phpt b/ext/openssl/tests/008.phpt new file mode 100644 index 0000000..8963864 --- /dev/null +++ b/ext/openssl/tests/008.phpt @@ -0,0 +1,74 @@ +--TEST-- +openssl_x509_export() and openssl_x509_export_to_file() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$fp = fopen(dirname(__FILE__) . "/cert.crt","r"); +$a = fread($fp,8192); +fclose($fp); + +$b = "file://" . dirname(__FILE__) . "/cert.crt"; +$c = "invalid cert"; +$d = openssl_x509_read($a); +$e = array(); + +var_dump(openssl_x509_export($a, $output)); // read cert as a binary string +var_dump(openssl_x509_export($b, $output2)); // read cert from a filename string +var_dump(openssl_x509_export($c, $output3)); // read an invalid cert, fails +var_dump(openssl_x509_export($d, $output4)); // read cert from a resource +var_dump(openssl_x509_export($e, $output5)); // read an array, fails + +$outfilename = tempnam("/tmp", "ssl"); +if ($outfilename === false) + die("failed to get a temporary filename!"); + +echo "---\n"; + +var_dump(openssl_x509_export_to_file($a, $outfilename)); // read cert as a binary string +var_dump(openssl_x509_export_to_file($b, $outfilename)); // read cert from a filename string +var_dump(openssl_x509_export_to_file($c, $outfilename)); // read an invalid cert, fails +var_dump(openssl_x509_export_to_file($d, $outfilename)); // read cert from a resource +var_dump(openssl_x509_export_to_file($e, $outfilename)); // read an array, fails +echo "---\n"; + +var_dump($exists = file_exists($outfilename)); +if ($exists) { + @unlink($outfilename); +} +echo "---\n"; + +var_dump(strcmp($output, $a)); +var_dump(strcmp($output, $output2)); +var_dump(strcmp($output, $output3)); +var_dump(strcmp($output, $output4)); // different +var_dump(strcmp($output, $output5)); // different +?> +--EXPECTF-- +bool(true) +bool(true) + +Warning: openssl_x509_export(): cannot get cert from parameter 1 in %s on line %d +bool(false) +bool(true) + +Warning: openssl_x509_export(): cannot get cert from parameter 1 in %s on line %d +bool(false) +--- +bool(true) +bool(true) + +Warning: openssl_x509_export_to_file(): cannot get cert from parameter 1 in %s on line %d +bool(false) +bool(true) + +Warning: openssl_x509_export_to_file(): cannot get cert from parameter 1 in %s on line %d +bool(false) +--- +bool(true) +--- +int(0) +int(0) +int(%d) +int(0) +int(%d) diff --git a/ext/openssl/tests/009.phpt b/ext/openssl/tests/009.phpt new file mode 100644 index 0000000..a7156dd --- /dev/null +++ b/ext/openssl/tests/009.phpt @@ -0,0 +1,29 @@ +--TEST-- +openssl_x509_check_private_key() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$fp = fopen(dirname(__FILE__) . "/cert.crt","r"); +$a = fread($fp,8192); +fclose($fp); + +$fp = fopen(dirname(__FILE__) . "/private.key","r"); +$b = fread($fp,8192); +fclose($fp); + +$cert = "file://" . dirname(__FILE__) . "/cert.crt"; +$key = "file://" . dirname(__FILE__) . "/private.key"; + +var_dump(openssl_x509_check_private_key($cert, $key)); +var_dump(openssl_x509_check_private_key("", $key)); +var_dump(openssl_x509_check_private_key($cert, "")); +var_dump(openssl_x509_check_private_key("", "")); +var_dump(openssl_x509_check_private_key($a, $b)); +?> +--EXPECT-- +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) diff --git a/ext/openssl/tests/011.phpt b/ext/openssl/tests/011.phpt new file mode 100644 index 0000000..118e952 --- /dev/null +++ b/ext/openssl/tests/011.phpt @@ -0,0 +1,31 @@ +--TEST-- +openssl_encrypt() and openssl_decrypt() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "openssl_encrypt() and openssl_decrypt() tests"; +$method = "AES-128-CBC"; +$password = "openssl"; + +$ivlen = openssl_cipher_iv_length($method); +$iv = ''; +srand(time() + ((microtime(true) * 1000000) % 1000000)); +while(strlen($iv) < $ivlen) $iv .= chr(rand(0,255)); + +$encrypted = openssl_encrypt($data, $method, $password, 0, $iv); +$output = openssl_decrypt($encrypted, $method, $password, 0, $iv); +var_dump($output); +$encrypted = openssl_encrypt($data, $method, $password, OPENSSL_RAW_DATA, $iv); +$output = openssl_decrypt($encrypted, $method, $password, OPENSSL_RAW_DATA, $iv); +var_dump($output); +// if we want to manage our own padding +$padded_data = $data . str_repeat(' ', 16 - (strlen($data) % 16)); +$encrypted = openssl_encrypt($padded_data, $method, $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv); +$output = openssl_decrypt($encrypted, $method, $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv); +var_dump(rtrim($output)); +?> +--EXPECT-- +string(45) "openssl_encrypt() and openssl_decrypt() tests" +string(45) "openssl_encrypt() and openssl_decrypt() tests" +string(45) "openssl_encrypt() and openssl_decrypt() tests" diff --git a/ext/openssl/tests/012.phpt b/ext/openssl/tests/012.phpt new file mode 100644 index 0000000..dbd03ac --- /dev/null +++ b/ext/openssl/tests/012.phpt @@ -0,0 +1,27 @@ +--TEST-- +openssl_seal() error tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "openssl_open() test"; +$pub_key = "file://" . dirname(__FILE__) . "/public.key"; +$wrong = "wrong"; + +openssl_seal($data, $sealed, $ekeys, array($pub_key)); // no output +openssl_seal($data, $sealed, $ekeys, array($pub_key, $pub_key)); // no output +openssl_seal($data, $sealed, $ekeys, array($pub_key, $wrong)); +openssl_seal($data, $sealed, $ekeys, $pub_key); +openssl_seal($data, $sealed, $ekeys, array()); +openssl_seal($data, $sealed, $ekeys, array($wrong)); +?> +--EXPECTF-- + +Warning: openssl_seal(): not a public key (2th member of pubkeys) in %s on line %d + +Warning: openssl_seal() expects parameter 4 to be array, string given in %s on line %d + +Warning: openssl_seal(): Fourth argument to openssl_seal() must be a non-empty array in %s on line %d + +Warning: openssl_seal(): not a public key (1th member of pubkeys) in %s on line %d + diff --git a/ext/openssl/tests/013.phpt b/ext/openssl/tests/013.phpt new file mode 100644 index 0000000..91bb73d --- /dev/null +++ b/ext/openssl/tests/013.phpt @@ -0,0 +1,28 @@ +--TEST-- +openssl_open() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "openssl_open() test"; +$pub_key = "file://" . dirname(__FILE__) . "/public.key"; +$priv_key = "file://" . dirname(__FILE__) . "/private.key"; +$wrong = "wrong"; + +openssl_seal($data, $sealed, $ekeys, array($pub_key, $pub_key, $pub_key)); +openssl_open($sealed, $output, $ekeys[0], $priv_key); +var_dump($output); +openssl_open($sealed, $output2, $ekeys[1], $wrong); +var_dump($output2); +openssl_open($sealed, $output3, $ekeys[2], $priv_key); +var_dump($output3); +openssl_open($sealed, $output4, $wrong, $priv_key); +var_dump($output4); +?> +--EXPECTF-- +string(19) "openssl_open() test" + +Warning: openssl_open(): unable to coerce parameter 4 into a private key in %s on line %d +NULL +string(19) "openssl_open() test" +NULL diff --git a/ext/openssl/tests/014.phpt b/ext/openssl/tests/014.phpt new file mode 100644 index 0000000..6123964 --- /dev/null +++ b/ext/openssl/tests/014.phpt @@ -0,0 +1,38 @@ +--TEST-- +openssl_private_encrypt() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "Testing openssl_private_encrypt()"; +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$pubkey = "file://" . dirname(__FILE__) . "/public.key"; +$wrong = "wrong"; +class test { + function __toString() { + return "test"; + } +} +$obj = new test; + +var_dump(openssl_private_encrypt($data, $encrypted, $privkey)); +var_dump(openssl_private_encrypt($data, $encrypted, $pubkey)); +var_dump(openssl_private_encrypt($data, $encrypted, $wrong)); +var_dump(openssl_private_encrypt($data, $encrypted, $obj)); +var_dump(openssl_private_encrypt($obj, $encrypted, $privkey)); +openssl_public_decrypt($encrypted, $output, $pubkey); +var_dump($output); +?> +--EXPECTF-- +bool(true) + +Warning: openssl_private_encrypt(): key param is not a valid private key in %s on line %d +bool(false) + +Warning: openssl_private_encrypt(): key param is not a valid private key in %s on line %d +bool(false) + +Warning: openssl_private_encrypt(): key param is not a valid private key in %s on line %d +bool(false) +bool(true) +string(4) "test" diff --git a/ext/openssl/tests/015.phpt b/ext/openssl/tests/015.phpt new file mode 100644 index 0000000..a89121d --- /dev/null +++ b/ext/openssl/tests/015.phpt @@ -0,0 +1,39 @@ +--TEST-- +openssl_public_encrypt() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "Testing openssl_public_encrypt()"; +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$pubkey = "file://" . dirname(__FILE__) . "/public.key"; +$wrong = "wrong"; +class test { + function __toString() { + return "test"; + } +} +$obj = new test; + +var_dump(openssl_public_encrypt($data, $encrypted, $pubkey)); +var_dump(openssl_public_encrypt($data, $encrypted, $privkey)); +var_dump(openssl_public_encrypt($data, $encrypted, $wrong)); +var_dump(openssl_public_encrypt($data, $encrypted, $obj)); +var_dump(openssl_public_encrypt($obj, $encrypted, $pubkey)); +openssl_private_decrypt($encrypted, $output, $privkey); +var_dump($output); +?> +--EXPECTF-- +bool(true) + +Warning: openssl_public_encrypt(): key parameter is not a valid public key in %s on line %d +bool(false) + +Warning: openssl_public_encrypt(): key parameter is not a valid public key in %s on line %d +bool(false) + +Warning: openssl_public_encrypt(): key parameter is not a valid public key in %s on line %d +bool(false) +bool(true) +string(4) "test" + diff --git a/ext/openssl/tests/016.phpt b/ext/openssl/tests/016.phpt new file mode 100644 index 0000000..2d772e7 --- /dev/null +++ b/ext/openssl/tests/016.phpt @@ -0,0 +1,48 @@ +--TEST-- +openssl_public_decrypt() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "Testing openssl_public_decrypt()"; +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$pubkey = "file://" . dirname(__FILE__) . "/public.key"; +$wrong = "wrong"; + +openssl_private_encrypt($data, $encrypted, $privkey); +var_dump(openssl_public_decrypt($encrypted, $output, $pubkey)); +var_dump($output); +var_dump(openssl_public_decrypt($encrypted, $output2, $wrong)); +var_dump($output2); +var_dump(openssl_public_decrypt($wrong, $output3, $pubkey)); +var_dump($output3); +var_dump(openssl_public_decrypt($encrypted, $output4, array())); +var_dump($output4); +var_dump(openssl_public_decrypt($encrypted, $output5, array($pubkey))); +var_dump($output5); +var_dump(openssl_public_decrypt($encrypted, $output6, array($pubkey, ""))); +var_dump($output6); +?> +--EXPECTF-- +bool(true) +string(32) "Testing openssl_public_decrypt()" + +Warning: openssl_public_decrypt(): key parameter is not a valid public key in %s on line %d +bool(false) +NULL +bool(false) +NULL + +Warning: openssl_public_decrypt(): key array must be of the form array(0 => key, 1 => phrase) in %s on line %d + +Warning: openssl_public_decrypt(): key parameter is not a valid public key in %s on line %d +bool(false) +NULL + +Warning: openssl_public_decrypt(): key array must be of the form array(0 => key, 1 => phrase) in %s on line %d + +Warning: openssl_public_decrypt(): key parameter is not a valid public key in %s on line %d +bool(false) +NULL +bool(true) +string(32) "Testing openssl_public_decrypt()" diff --git a/ext/openssl/tests/017.phpt b/ext/openssl/tests/017.phpt new file mode 100644 index 0000000..65a7cd1 --- /dev/null +++ b/ext/openssl/tests/017.phpt @@ -0,0 +1,40 @@ +--TEST-- +openssl_private_decrypt() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "Testing openssl_public_decrypt()"; +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$pubkey = "file://" . dirname(__FILE__) . "/public.key"; +$wrong = "wrong"; + +openssl_public_encrypt($data, $encrypted, $pubkey); +var_dump(openssl_private_decrypt($encrypted, $output, $privkey)); +var_dump($output); +var_dump(openssl_private_decrypt($encrypted, $output2, $wrong)); +var_dump($output2); +var_dump(openssl_private_decrypt($wrong, $output3, $privkey)); +var_dump($output3); +var_dump(openssl_private_decrypt($encrypted, $output4, array($privkey))); +var_dump($output4); +var_dump(openssl_private_decrypt($encrypted, $output5, array($privkey, ""))); +var_dump($output5); +?> +--EXPECTF-- +bool(true) +string(32) "Testing openssl_public_decrypt()" + +Warning: openssl_private_decrypt(): key parameter is not a valid private key in %s on line %d +bool(false) +NULL +bool(false) +NULL + +Warning: openssl_private_decrypt(): key array must be of the form array(0 => key, 1 => phrase) in %s on line %d + +Warning: openssl_private_decrypt(): key parameter is not a valid private key in %s on line %d +bool(false) +NULL +bool(true) +string(32) "Testing openssl_public_decrypt()" diff --git a/ext/openssl/tests/018.phpt b/ext/openssl/tests/018.phpt new file mode 100644 index 0000000..230c0a8 --- /dev/null +++ b/ext/openssl/tests/018.phpt @@ -0,0 +1,22 @@ +--TEST-- +openssl_sign() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "Testing openssl_sign()"; +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$wrong = "wrong"; + +var_dump(openssl_sign($data, $sign, $privkey)); // no output +var_dump(openssl_sign($data, $sign, $wrong)); +var_dump(openssl_sign(array(), $sign, $privkey)); +?> +--EXPECTF-- +bool(true) + +Warning: openssl_sign(): supplied key param cannot be coerced into a private key in %s on line %d +bool(false) + +Warning: openssl_sign() expects parameter 1 to be string, array given in %s on line %d +NULL diff --git a/ext/openssl/tests/019.phpt b/ext/openssl/tests/019.phpt new file mode 100644 index 0000000..c1f186c --- /dev/null +++ b/ext/openssl/tests/019.phpt @@ -0,0 +1,28 @@ +--TEST-- +openssl_verify() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "Testing openssl_verify()"; +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$pubkey = "file://" . dirname(__FILE__) . "/public.key"; +$wrong = "wrong"; + +openssl_sign($data, $sign, $privkey); +var_dump(openssl_verify($data, $sign, $pubkey)); +var_dump(openssl_verify($data, $sign, $privkey)); +var_dump(openssl_verify($data, $sign, $wrong)); +var_dump(openssl_verify($data, $wrong, $pubkey)); +var_dump(openssl_verify($wrong, $sign, $pubkey)); +?> +--EXPECTF-- +int(1) + +Warning: openssl_verify(): supplied key param cannot be coerced into a public key in %s on line %d +bool(false) + +Warning: openssl_verify(): supplied key param cannot be coerced into a public key in %s on line %d +bool(false) +int(0) +int(0) diff --git a/ext/openssl/tests/021.phpt b/ext/openssl/tests/021.phpt new file mode 100644 index 0000000..391b6a5 --- /dev/null +++ b/ext/openssl/tests/021.phpt @@ -0,0 +1,80 @@ +--TEST-- +openssl_csr_sign() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$cert = "file://" . dirname(__FILE__) . "/cert.crt"; +$priv = "file://" . dirname(__FILE__) . "/private.key"; +$wrong = "wrong"; +$pub = "file://" . dirname(__FILE__) . "/public.key"; +$config = __DIR__ . DIRECTORY_SEPARATOR . 'openssl.cnf'; +$config_arg = array('config' => $config); + +$dn = array( + "countryName" => "BR", + "stateOrProvinceName" => "Rio Grande do Sul", + "localityName" => "Porto Alegre", + "commonName" => "Henrique do N. Angelo", + "emailAddress" => "hnangelo@php.net" + ); + +$args = array( + "digest_alg" => "sha1", + "private_key_bits" => 2048, + "private_key_type" => OPENSSL_KEYTYPE_DSA, + "encrypt_key" => true, + "config" => $config + ); + +$privkey = openssl_pkey_new($config_arg); +$csr = openssl_csr_new($dn, $privkey, $args); +var_dump(openssl_csr_sign($csr, null, $privkey, 365, $args)); +var_dump(openssl_csr_sign($csr, null, $privkey, 365, $config_arg)); +var_dump(openssl_csr_sign($csr, $cert, $priv, 365, $config_arg)); +var_dump(openssl_csr_sign($csr, $wrong, $privkey, 365)); +var_dump(openssl_csr_sign($csr, null, $wrong, 365)); +var_dump(openssl_csr_sign($csr, null, $privkey, $wrong)); +var_dump(openssl_csr_sign($csr, null, $privkey, 365, $wrong)); +var_dump(openssl_csr_sign($wrong, null, $privkey, 365)); +var_dump(openssl_csr_sign(array(), null, $privkey, 365)); +var_dump(openssl_csr_sign($csr, array(), $privkey, 365)); +var_dump(openssl_csr_sign($csr, null, array(), 365)); +var_dump(openssl_csr_sign($csr, null, $privkey, array())); +var_dump(openssl_csr_sign($csr, null, $privkey, 365, $config_arg)); +?> +--EXPECTF-- +resource(%d) of type (OpenSSL X.509) +resource(%d) of type (OpenSSL X.509) +resource(%d) of type (OpenSSL X.509) + +Warning: openssl_csr_sign(): cannot get cert from parameter 2 in %s on line %d +bool(false) + +Warning: openssl_csr_sign(): cannot get private key from parameter 3 in %s on line %d +bool(false) + +Warning: openssl_csr_sign() expects parameter 4 to be long, string given in %s on line %d +NULL + +Warning: openssl_csr_sign() expects parameter 5 to be array, string given in %s on line %d +NULL + +Warning: openssl_csr_sign(): cannot get CSR from parameter 1 in %s on line %d +bool(false) + +Warning: openssl_csr_sign(): cannot get CSR from parameter 1 in %s on line %d +bool(false) + +Warning: openssl_csr_sign(): cannot get cert from parameter 2 in %s on line %d +bool(false) + +Warning: openssl_csr_sign(): key array must be of the form array(0 => key, 1 => phrase) in %s on line %d + +Warning: openssl_csr_sign(): cannot get private key from parameter 3 in %s on line %d +bool(false) + +Warning: openssl_csr_sign() expects parameter 4 to be long, array given in %s on line %d +NULL +resource(%d) of type (OpenSSL X.509) + diff --git a/ext/openssl/tests/022.phpt b/ext/openssl/tests/022.phpt new file mode 100644 index 0000000..1fa84d9 --- /dev/null +++ b/ext/openssl/tests/022.phpt @@ -0,0 +1,48 @@ +--TEST-- +openssl_csr_export() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$wrong = "wrong"; +$config = __DIR__ . DIRECTORY_SEPARATOR . 'openssl.cnf'; +$config_arg = array('config' => $config); + +$dn = array( + "countryName" => "BR", + "stateOrProvinceName" => "Rio Grande do Sul", + "localityName" => "Porto Alegre", + "commonName" => "Henrique do N. Angelo", + "emailAddress" => "hnangelo@php.net" + ); + +$args = array( + "digest_alg" => "sha1", + "private_key_bits" => 2048, + "private_key_type" => OPENSSL_KEYTYPE_DSA, + "encrypt_key" => true, + "config" => $config, + ); + +$privkey = openssl_pkey_new($config_arg); +$csr = openssl_csr_new($dn, $privkey, $args); +var_dump(openssl_csr_export($csr, $output)); +var_dump(openssl_csr_export($wrong, $output)); +var_dump(openssl_csr_export($privkey, $output)); +var_dump(openssl_csr_export(array(), $output)); +var_dump(openssl_csr_export($csr, $output, false)); +?> +--EXPECTF-- +bool(true) + +Warning: openssl_csr_export() expects parameter 1 to be resource, string given in %s on line %d +NULL + +Warning: openssl_csr_export(): supplied resource is not a valid OpenSSL X.509 CSR resource in %s on line %d + +Warning: openssl_csr_export(): cannot get CSR from parameter 1 in %s on line %d +bool(false) + +Warning: openssl_csr_export() expects parameter 1 to be resource, array given in %s on line %d +NULL +bool(true) diff --git a/ext/openssl/tests/023.phpt b/ext/openssl/tests/023.phpt new file mode 100644 index 0000000..1489613 --- /dev/null +++ b/ext/openssl/tests/023.phpt @@ -0,0 +1,64 @@ +--TEST-- +openssl_pkcs7_encrypt() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$infile = dirname(__FILE__) . "/cert.crt"; +$outfile = tempnam("/tmp", "ssl"); +if ($outfile === false) + die("failed to get a temporary filename!"); +$outfile2 = tempnam("/tmp", "ssl"); +if ($outfile2 === false) + die("failed to get a temporary filename!"); + +$single_cert = "file://" . dirname(__FILE__) . "/cert.crt"; +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$multi_certs = array($single_cert, $single_cert); +$assoc_headers = array("To" => "test@test", "Subject" => "testing openssl_pkcs7_encrypt()"); +$headers = array("test@test", "testing openssl_pkcs7_encrypt()"); +$empty_headers = array(); +$wrong = "wrong"; +$empty = ""; + +var_dump(openssl_pkcs7_encrypt($infile, $outfile, $single_cert, $headers)); +var_dump(openssl_pkcs7_decrypt($outfile, $outfile2, $single_cert, $privkey)); +var_dump(openssl_pkcs7_encrypt($infile, $outfile, $single_cert, $assoc_headers)); +var_dump(openssl_pkcs7_encrypt($infile, $outfile, $single_cert, $empty_headers)); +var_dump(openssl_pkcs7_encrypt($infile, $outfile, $single_cert, $wrong)); +var_dump(openssl_pkcs7_encrypt($wrong, $outfile, $single_cert, $headers)); +var_dump(openssl_pkcs7_encrypt($empty, $outfile, $single_cert, $headers)); +var_dump(openssl_pkcs7_encrypt($infile, $empty, $single_cert, $headers)); +var_dump(openssl_pkcs7_encrypt($infile, $outfile, $wrong, $headers)); +var_dump(openssl_pkcs7_encrypt($infile, $outfile, $empty, $headers)); +var_dump(openssl_pkcs7_encrypt($infile, $outfile, $single_cert, $empty)); +var_dump(openssl_pkcs7_encrypt($infile, $outfile, $multi_certs, $headers)); + +if (file_exists($outfile)) { + echo "true\n"; + unlink($outfile); +} +if (file_exists($outfile2)) { + echo "true\n"; + unlink($outfile2); +} +?> +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) + +Warning: openssl_pkcs7_encrypt() expects parameter 4 to be array, string given in %s on line %d +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) + +Warning: openssl_pkcs7_encrypt() expects parameter 4 to be array, string given in %s on line %d +bool(false) +bool(true) +true +true diff --git a/ext/openssl/tests/024.phpt b/ext/openssl/tests/024.phpt new file mode 100644 index 0000000..0a61840 --- /dev/null +++ b/ext/openssl/tests/024.phpt @@ -0,0 +1,64 @@ +--TEST-- +openssl_pkcs7_decrypt() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$infile = dirname(__FILE__) . "/cert.crt"; +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$encrypted = tempnam("/tmp", "ssl"); +if ($encrypted === false) + die("failed to get a temporary filename!"); +$outfile = tempnam("/tmp", "ssl"); +if ($outfile === false) { + unlink($outfile); + die("failed to get a temporary filename!"); +} + +$single_cert = "file://" . dirname(__FILE__) . "/cert.crt"; +$headers = array("test@test", "testing openssl_pkcs7_encrypt()"); +$wrong = "wrong"; +$empty = ""; + +openssl_pkcs7_encrypt($infile, $encrypted, $single_cert, $headers); +var_dump(openssl_pkcs7_decrypt($encrypted, $outfile, $single_cert, $privkey)); +var_dump(openssl_pkcs7_decrypt($encrypted, $outfile, $single_cert, $wrong)); +var_dump(openssl_pkcs7_decrypt($encrypted, $outfile, $wrong, $privkey)); +var_dump(openssl_pkcs7_decrypt($encrypted, $outfile, null, $privkey)); +var_dump(openssl_pkcs7_decrypt($wrong, $outfile, $single_cert, $privkey)); +var_dump(openssl_pkcs7_decrypt($empty, $outfile, $single_cert, $privkey)); +var_dump(openssl_pkcs7_decrypt($encrypted, $empty, $single_cert, $privkey)); +var_dump(openssl_pkcs7_decrypt($encrypted, $outfile, $empty, $privkey)); +var_dump(openssl_pkcs7_decrypt($encrypted, $outfile, $single_cert, $empty)); + +if (file_exists($encrypted)) { + echo "true\n"; + unlink($encrypted); +} +if (file_exists($outfile)) { + echo "true\n"; + unlink($outfile); +} +?> +--EXPECTF-- +bool(true) + +Warning: openssl_pkcs7_decrypt(): unable to get private key in %s on line %d +bool(false) + +Warning: openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert in %s on line %d +bool(false) + +Warning: openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert in %s on line %d +bool(false) +bool(false) +bool(false) +bool(false) + +Warning: openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert in %s on line %d +bool(false) + +Warning: openssl_pkcs7_decrypt(): unable to get private key in %s on line %d +bool(false) +true +true diff --git a/ext/openssl/tests/025.phpt b/ext/openssl/tests/025.phpt new file mode 100644 index 0000000..ac567a5 --- /dev/null +++ b/ext/openssl/tests/025.phpt @@ -0,0 +1,65 @@ +--TEST-- +openssl_pkcs7_sign() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$infile = dirname(__FILE__) . "/cert.crt"; +$outfile = tempnam("/tmp", "ssl"); +if ($outfile === false) + die("failed to get a temporary filename!"); + +$privkey = "file://" . dirname(__FILE__) . "/private.key"; +$single_cert = "file://" . dirname(__FILE__) . "/cert.crt"; +$assoc_headers = array("To" => "test@test", "Subject" => "testing openssl_pkcs7_sign()"); +$headers = array("test@test", "testing openssl_pkcs7_sign()"); +$empty_headers = array(); +$wrong = "wrong"; +$empty = ""; + +var_dump(openssl_pkcs7_sign($infile, $outfile, $single_cert, $privkey, $headers)); +var_dump(openssl_pkcs7_sign($infile, $outfile, $single_cert, $privkey, $assoc_headers)); +var_dump(openssl_pkcs7_sign($infile, $outfile, $single_cert, $privkey, $empty_headers)); +var_dump(openssl_pkcs7_sign($infile, $outfile, $single_cert, $privkey, $wrong)); +var_dump(openssl_pkcs7_sign($wrong, $outfile, $single_cert, $privkey, $headers)); +var_dump(openssl_pkcs7_sign($empty, $outfile, $single_cert, $privkey, $headers)); +var_dump(openssl_pkcs7_sign($infile, $empty, $single_cert, $privkey, $headers)); +var_dump(openssl_pkcs7_sign($infile, $outfile, $wrong, $privkey, $headers)); +var_dump(openssl_pkcs7_sign($infile, $outfile, $empty, $privkey, $headers)); +var_dump(openssl_pkcs7_sign($infile, $outfile, $single_cert, $privkey, $empty)); +var_dump(openssl_pkcs7_sign($infile, $outfile, $single_cert, $wrong, $headers)); + +if (file_exists($outfile)) { + echo "true\n"; + unlink($outfile); +} +?> +--EXPECTF-- +bool(true) +bool(true) +bool(true) + +Warning: openssl_pkcs7_sign() expects parameter 5 to be array, string given in %s on line %d +NULL + +Warning: openssl_pkcs7_sign(): error opening input file %s in %s on line %d +bool(false) + +Warning: openssl_pkcs7_sign(): error opening input file %s in %s on line %d +bool(false) + +Warning: openssl_pkcs7_sign(): error opening output file %s in %s on line %d +bool(false) + +Warning: openssl_pkcs7_sign(): error getting cert in %s on line %d +bool(false) + +Warning: openssl_pkcs7_sign(): error getting cert in %s on line %d +bool(false) + +Warning: openssl_pkcs7_sign() expects parameter 5 to be array, string given in %s on line %d +NULL + +Warning: openssl_pkcs7_sign(): error getting private key in %s on line %d +bool(false) +true diff --git a/ext/openssl/tests/bug25614.phpt b/ext/openssl/tests/bug25614.phpt new file mode 100644 index 0000000..a431307 --- /dev/null +++ b/ext/openssl/tests/bug25614.phpt @@ -0,0 +1,14 @@ +--TEST-- +openssl: get public key from generated private key +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +if (!@openssl_pkey_new()) die("skip cannot create private key"); +?> +--FILE-- +<?php +$priv = openssl_pkey_new(); +$pub = openssl_pkey_get_public($priv); +?> +--EXPECTF-- +Warning: openssl_pkey_get_public(): Don't know how to get public key from this private key %s diff --git a/ext/openssl/tests/bug28382.phpt b/ext/openssl/tests/bug28382.phpt new file mode 100644 index 0000000..2bedeb2 --- /dev/null +++ b/ext/openssl/tests/bug28382.phpt @@ -0,0 +1,46 @@ +--TEST-- +Bug #28382 (openssl_x509_parse extensions support) +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +if (OPENSSL_VERSION_NUMBER<0x009070af) die("skip"); +?> +--FILE-- +<?php +$cert = file_get_contents(dirname(__FILE__) . "/bug28382cert.txt"); +$ext = openssl_x509_parse($cert); +var_dump($ext['extensions']); +/* openssl 1.0 prepends the string "Full Name:" to the crlDistributionPoints array key. + For now, as this is the one difference only between 0.9.x and 1.x, it's handled with + placeholders to not to duplicate the test. When more diffs come, a duplication would + be probably a better solution. +*/ +?> +--EXPECTF-- +array(11) { + ["basicConstraints"]=> + string(8) "CA:FALSE" + ["nsComment"]=> + string(38) "For Grid use only; request tag userTag" + ["nsCertType"]=> + string(30) "SSL Client, SSL Server, S/MIME" + ["crlDistributionPoints"]=> + string(%d) "%AURI:http://mobile.blue-software.ro:90/ca/crl.shtml +" + ["nsCaPolicyUrl"]=> + string(38) "http://mobile.blue-software.ro:90/pub/" + ["subjectAltName"]=> + string(28) "email:sergiu@bluesoftware.ro" + ["subjectKeyIdentifier"]=> + string(59) "B0:A7:FF:F9:41:15:DE:23:39:BD:DD:31:0F:97:A0:B2:A2:74:E0:FC" + ["authorityKeyIdentifier"]=> + string(115) "DirName:/C=RO/ST=Romania/L=Craiova/O=Sergiu/OU=Sergiu SRL/CN=Sergiu CA/emailAddress=n_sergiu@hotmail.com +serial:00 +" + ["keyUsage"]=> + string(71) "Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment" + ["nsBaseUrl"]=> + string(20) "http://62.231.98.52/" + ["1.2.3.4"]=> + string(4) "%s" +} diff --git a/ext/openssl/tests/bug28382cert.txt b/ext/openssl/tests/bug28382cert.txt new file mode 100644 index 0000000..cce8d42 --- /dev/null +++ b/ext/openssl/tests/bug28382cert.txt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEoDCCBAmgAwIBAgIBJzANBgkqhkiG9w0BAQQFADCBkDELMAkGA1UEBhMCUk8x +EDAOBgNVBAgTB1JvbWFuaWExEDAOBgNVBAcTB0NyYWlvdmExDzANBgNVBAoTBlNl +cmdpdTETMBEGA1UECxMKU2VyZ2l1IFNSTDESMBAGA1UEAxMJU2VyZ2l1IENBMSMw +IQYJKoZIhvcNAQkBFhRuX3NlcmdpdUBob3RtYWlsLmNvbTAeFw0wNDA1MTQxMzM0 +NTZaFw0wNTA1MTQxMzM0NTZaMIGaMQswCQYDVQQGEwJSTzEQMA4GA1UECBMHUm9t +YW5pYTEQMA4GA1UEBxMHQ3JhaW92YTETMBEGA1UEChMKU2VyZ2l1IFNSTDETMBEG +A1UECxMKU2VyZ2l1IFNSTDEYMBYGA1UEAxMPU2VyZ2l1IHBlcnNvbmFsMSMwIQYJ +KoZIhvcNAQkBFhRuX3NlcmdpdUBob3RtYWlsLmNvbTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEApNj7XXz8T8FcLIWpBniPYom3QcT6T7u0xRPHqtqzj5oboBYp +DJe5d354/y0gJTpiLt8+fTrPgWXnbHm3pOHgXzTcX6Arani0GDU0/xDi4VkCRGcS +YqX2sJpcDzAbmK9UDMt3xf/O1B8AJan3RfO0Bm3ozTEPziLMkmsiYr5b/L8CAwEA +AaOCAfwwggH4MAkGA1UdEwQCMAAwNQYJYIZIAYb4QgENBCgWJkZvciBHcmlkIHVz +ZSBvbmx5OyByZXF1ZXN0IHRhZyB1c2VyVGFnMBEGCWCGSAGG+EIBAQQEAwIF4DA/ +BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vbW9iaWxlLmJsdWUtc29mdHdhcmUucm86 +OTAvY2EvY3JsLnNodG1sMDUGCWCGSAGG+EIBCAQoFiZodHRwOi8vbW9iaWxlLmJs +dWUtc29mdHdhcmUucm86OTAvcHViLzAhBgNVHREEGjAYgRZzZXJnaXVAYmx1ZXNv +ZnR3YXJlLnJvMB0GA1UdDgQWBBSwp//5QRXeIzm93TEPl6CyonTg/DCBpwYDVR0j +BIGfMIGcoYGWpIGTMIGQMQswCQYDVQQGEwJSTzEQMA4GA1UECBMHUm9tYW5pYTEQ +MA4GA1UEBxMHQ3JhaW92YTEPMA0GA1UEChMGU2VyZ2l1MRMwEQYDVQQLEwpTZXJn +aXUgU1JMMRIwEAYDVQQDEwlTZXJnaXUgQ0ExIzAhBgkqhkiG9w0BCQEWFG5fc2Vy +Z2l1QGhvdG1haWwuY29tggEAMAsGA1UdDwQEAwIE8DAjBglghkgBhvhCAQIEFhYU +aHR0cDovLzYyLjIzMS45OC41Mi8wCwYDKgMEBAQ+52I0MA0GCSqGSIb3DQEBBAUA +A4GBAIBIOJ+iiLyQfNJEY+IMefayQea0nmuXYY+F+L1DFjSC7xChytgYoPNnKkhh +3dWPtxbswiqKYUnGi6y3Hi4UhDsOaDW29t2S305hSc2qgjOiNtRYQIVYQ8EHG1k7 +Fl63S7uCOhnVJt+4MnUK1N6/pwgsp+Z2GvEsDG1qCKnvNpf6 +-----END CERTIFICATE----- diff --git a/ext/openssl/tests/bug36732.phpt b/ext/openssl/tests/bug36732.phpt new file mode 100644 index 0000000..ec8fedb --- /dev/null +++ b/ext/openssl/tests/bug36732.phpt @@ -0,0 +1,41 @@ +--TEST-- +Bug #36732 (add support for req_extensions in openss_csr_new and sign) +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +if (OPENSSL_VERSION_NUMBER < 0x009070af) die("skip"); +?> +--FILE-- +<?php +$configargs = array( + "req_extensions" => "v3_req", + "x509_extensions" => "usr_cert", + "config" => __DIR__."/openssl.cnf", +); + +$dn = array( + "countryName" => "GB", + "stateOrProvinceName" => "Berkshire", + "localityName" => "Newbury", + "organizationName" => "My Company Ltd", + "commonName" => "Demo Cert" +); + +$key = openssl_pkey_new(); +$csr = openssl_csr_new($dn, $key, $configargs); +$crt = openssl_csr_sign($csr, NULL, $key, 365, $configargs); + +$str = ''; +openssl_csr_export($csr, $str, false); + +if (strpos($str, 'Requested Extensions:')) { + echo "Ok\n"; +} +openssl_x509_export($crt, $str, false); +if (strpos($str, 'X509v3 extensions:')) { + echo "Ok\n"; +} +?> +--EXPECTF-- +Ok +Ok diff --git a/ext/openssl/tests/bug37820.phpt b/ext/openssl/tests/bug37820.phpt new file mode 100644 index 0000000..2eef8c5 --- /dev/null +++ b/ext/openssl/tests/bug37820.phpt @@ -0,0 +1,36 @@ +--TEST-- +openssl_sign/verify: accept different algos +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +if (OPENSSL_VERSION_NUMBER < 0x009070af) die("skip"); +?> +--FILE-- +<?php +$dir = dirname(__FILE__); +$file_pub = $dir . '/bug37820cert.pem'; +$file_key = $dir . '/bug37820key.pem'; + +$priv_key = file_get_contents($file_key); +$priv_key_id = openssl_get_privatekey($priv_key); + + + +$pub_key = file_get_contents($file_pub); +$pub_key_id = openssl_get_publickey($pub_key); +$data = "some custom data"; +if (!openssl_sign($data, $signature, $priv_key_id, OPENSSL_ALGO_MD5)) { + echo "openssl_sign failed."; +} + +$ok = openssl_verify($data, $signature, $pub_key_id, OPENSSL_ALGO_MD5); +if ($ok == 1) { + echo "Ok"; +} elseif ($ok == 0) { + echo "openssl_verify failed."; +} + + +?> +--EXPECTF-- +Ok diff --git a/ext/openssl/tests/bug37820cert.pem b/ext/openssl/tests/bug37820cert.pem new file mode 100644 index 0000000..9d7ac23 --- /dev/null +++ b/ext/openssl/tests/bug37820cert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICLDCCAdYCAQAwDQYJKoZIhvcNAQEEBQAwgaAxCzAJBgNVBAYTAlBUMRMwEQYD +VQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5ldXJv +bmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMTEmJy +dXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZpMB4X +DTk2MDkwNTAzNDI0M1oXDTk2MTAwNTAzNDI0M1owgaAxCzAJBgNVBAYTAlBUMRMw +EQYDVQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5l +dXJvbmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMT +EmJydXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZp +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL7+aty3S1iBA/+yxjxv4q1MUTd1kjNw +L4lYKbpzzlmC5beaQXeQ2RmGMTXU+mDvuqItjVHOK3DvPK7lTcSGftUCAwEAATAN +BgkqhkiG9w0BAQQFAANBAFqPEKFjk6T6CKTHvaQeEAsX0/8YHPHqH/9AnhSjrwuX +9EBc0n6bVGhN7XaXd6sJ7dym9sbsWxb+pJdurnkxjx4= +-----END CERTIFICATE----- diff --git a/ext/openssl/tests/bug37820key.pem b/ext/openssl/tests/bug37820key.pem new file mode 100644 index 0000000..239ad66 --- /dev/null +++ b/ext/openssl/tests/bug37820key.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAL7+aty3S1iBA/+yxjxv4q1MUTd1kjNwL4lYKbpzzlmC5beaQXeQ +2RmGMTXU+mDvuqItjVHOK3DvPK7lTcSGftUCAwEAAQJBALjkK+jc2+iihI98riEF +oudmkNziSRTYjnwjx8mCoAjPWviB3c742eO3FG4/soi1jD9A5alihEOXfUzloenr +8IECIQD3B5+0l+68BA/6d76iUNqAAV8djGTzvxnCxycnxPQydQIhAMXt4trUI3nc +a+U8YL2HPFA3gmhBsSICbq2OptOCnM7hAiEA6Xi3JIQECob8YwkRj29DU3/4WYD7 +WLPgsQpwo1GuSpECICGsnWH5oaeD9t9jbFoSfhJvv0IZmxdcLpRcpslpeWBBAiEA +6/5B8J0GHdJq89FHwEG/H2eVVUYu5y/aD6sgcm+0Avg= +-----END RSA PRIVATE KEY----- diff --git a/ext/openssl/tests/bug38255.phpt b/ext/openssl/tests/bug38255.phpt new file mode 100644 index 0000000..4872605 --- /dev/null +++ b/ext/openssl/tests/bug38255.phpt @@ -0,0 +1,55 @@ +--TEST-- +openssl key from zval leaks +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +?> +--FILE-- +<?php +$pub_key_id = false; +$signature = ''; +$ok = openssl_verify("foo", $signature, $pub_key_id, OPENSSL_ALGO_MD5); + +class test { + function __toString() { + return "test object"; + } +} +$t = new test; + + +var_dump(openssl_verify("foo", $signature, $pub_key_id, OPENSSL_ALGO_MD5)); +var_dump(openssl_verify("foo", $t, $pub_key_id, OPENSSL_ALGO_MD5)); +var_dump(openssl_verify("foo", new stdClass, $pub_key_id, OPENSSL_ALGO_MD5)); +var_dump(openssl_verify("foo", new stdClass, array(), OPENSSL_ALGO_MD5)); +var_dump(openssl_verify("foo", array(), array(), OPENSSL_ALGO_MD5)); +var_dump(openssl_verify()); +var_dump(openssl_verify(new stdClass, new stdClass, array(), 10000)); + +echo "Done\n"; + +?> +--EXPECTF-- +Warning: openssl_verify(): supplied key param cannot be coerced into a public key in %s on line %d + +Warning: openssl_verify(): supplied key param cannot be coerced into a public key in %s on line %d +bool(false) + +Warning: openssl_verify(): supplied key param cannot be coerced into a public key in %s on line %d +bool(false) + +Warning: openssl_verify() expects parameter 2 to be string, object given in %s on line %d +NULL + +Warning: openssl_verify() expects parameter 2 to be string, object given in %s on line %d +NULL + +Warning: openssl_verify() expects parameter 2 to be string, array given in %s on line %d +NULL + +Warning: openssl_verify() expects at least 3 parameters, 0 given in %s on line %d +NULL + +Warning: openssl_verify() expects parameter 1 to be string, object given in %s on line %d +NULL +Done diff --git a/ext/openssl/tests/bug38261.phpt b/ext/openssl/tests/bug38261.phpt new file mode 100644 index 0000000..b06fa4f --- /dev/null +++ b/ext/openssl/tests/bug38261.phpt @@ -0,0 +1,34 @@ +--TEST-- +openssl key from zval leaks +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +?> +--FILE-- +<?php +$cert = false; +class test { + function __toString() { + return "test object"; + } +} +$t = new test; + +var_dump(openssl_x509_parse("foo")); +var_dump(openssl_x509_parse($t)); +var_dump(openssl_x509_parse(array())); +var_dump(openssl_x509_parse()); +var_dump(openssl_x509_parse($cert)); +var_dump(openssl_x509_parse(new stdClass)); + +?> +--EXPECTF-- +bool(false) +bool(false) +bool(false) + +Warning: openssl_x509_parse() expects at least 1 parameter, 0 given in %sbug38261.php on line %d +NULL +bool(false) + +Catchable fatal error: Object of class stdClass could not be converted to string in %sbug38261.php on line %d diff --git a/ext/openssl/tests/bug39217.phpt b/ext/openssl/tests/bug39217.phpt new file mode 100644 index 0000000..7895e2f --- /dev/null +++ b/ext/openssl/tests/bug39217.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug #39217 (Large serial number return -1) +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +?> +--FILE-- +<?php +$dir = dirname(__FILE__); +$certs = array('bug39217cert2.txt', 'bug39217cert1.txt'); +foreach($certs as $cert) { + $res = openssl_x509_parse(file_get_contents($dir . '/' . $cert)); + print_r($res['serialNumber']); + echo "\n"; +} +?> +--EXPECTF-- +163040343498260435477161879008842183802 +15 diff --git a/ext/openssl/tests/bug39217cert1.txt b/ext/openssl/tests/bug39217cert1.txt new file mode 100644 index 0000000..c3ddfb4 --- /dev/null +++ b/ext/openssl/tests/bug39217cert1.txt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICvzCCAiigAwIBAgIBDzANBgkqhkiG9w0BAQUFADBbMRkwFwYDVQQKExBET0Ug +U2NpZW5jZSBHcmlkMSAwHgYDVQQLExdDZXJ0aWZpY2F0ZSBBdXRob3JpdGllczEc +MBoGA1UEAxMTQ2VydGlmaWNhdGUgTWFuYWdlcjAeFw0wMDA4MjkyMjI4MDJaFw0w +MTA4MjkyMjI4MDJaMHgxDTALBgNVBAoTBEdyaWQxLjAsBgNVBAoTJUxhd3JlbmNl +IEJlcmtlbGV5IE5hdGlvbmFsIExhYm9yYXRvcnkxIDAeBgNVBAsTF0NlcnRpZmlj +YXRlIEF1dGhvcml0aWVzMRUwEwYDVQQDEwxMQk5MLUdyaWQtQ0EwgZ8wDQYJKoZI +hvcNAQEBBQADgY0AMIGJAoGBAL2t4aX933WXYlofuY+L+16Tdl/KxpAammyfcW8u +kHHT6RYDjaQdfV1FpNEqfSrRjKNwGGGkrG4XHZWiUO0Di0AlBN04lsRY6jB68l6B +5byujfZv+8EeCI2c1ObBLYZYi4lToJf0sm0Hpn3GD7PZBv6BVHLOuwEFDl9z9Dnc +DFDdAgMBAAGjdjB0MBEGCWCGSAGG+EIBAQQEAwIAhzAOBgNVHQ8BAf8EBAMCAcYw +HQYDVR0OBBYEFIn+csPVyp+iprpYUIu1SziMQiDxMA8GA1UdEwEB/wQFMAMBAf8w +HwYDVR0jBBgwFoAUm85P8ry9WHAx1fIyDn6eveJRFOcwDQYJKoZIhvcNAQEFBQAD +gYEAHindWQ4P4VUmJVt5sUGA05hSAZriDJDDnkvkm/9AR7xgGxtsy21QruhUVe2E +eVFBws85zbwRqMpfUQyE/xHhUcka2GQTaKlBlcEjZTMnsh27Si2PMYU/UPr/PIpq +kBkoxVV1bMWRK57mG2tzzTy9j0wkct4G5IjEsrYNDzW6U3E= +-----END CERTIFICATE----- diff --git a/ext/openssl/tests/bug39217cert2.txt b/ext/openssl/tests/bug39217cert2.txt new file mode 100644 index 0000000..399618c --- /dev/null +++ b/ext/openssl/tests/bug39217cert2.txt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC3DCCAkWgAwIBAgIQeqhtj1pzHCrTTq2AldV0ejANBgkqhkiG9w0BAQQFADAy +MRcwFQYDVQQKEw5FLUFDSEFUIE1JTkRFRjEXMBUGA1UEAxMORS1BQ0hBVCBNSU5E +RUYwHhcNMDQwMTA1MDAwMDAwWhcNMDYwMTA0MjM1OTU5WjCB5DEOMAwGA1UEBxQF +UEFSSVMxCzAJBgNVBAYTAkZSMRcwFQYDVQQKFA5FLUFDSEFUIE1JTkRFRjEtMCsG +A1UECxQkRW50LiAtIENhcCBHZW1pbmkgRXJuc3QgWW91bmcgRnJhbmNlMR8wHQYD +VQQLFBZTSVJFTiAtIDMyODc4MTc4NjAwMDUzMTQwMgYDVQQDEytDR0VZIEZyYW5j +ZSAtIENhcCBHZW1pbmkgRXJuc3QgWW91bmcgRnJhbmNlMSYwJAYJKoZIhvcNAQkB +FhdkZ2FlbWEtbWNvQGNhcGdlbWluaS5mcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEApFgcuVTuUe0z+iGTaPw7yVxhZsPq6aIqGHsCvU9fqUcymbmg9l4oTfAk +gR5bvDo+JTQb1/OPlQCKqyVa7wn6lPs97dMOZMobjCRcvw7z0jVphortA1NS8FRH +6LsWELZ13uC57IIakpW726Vz3tST9qHHbQoWbX/n8NjHcwL4zUECAwEAAaNAMD4w +CQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwEQYJYIZIAYb4QgEBBAQDAgeAMBEGCmCG +SAGG+EUBBgkEAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAWdmEu8TkFdgqA/xN6llo9 +zZR3EUH0X5HstxJRYgofcQyfumJHhgvaNB8vkDhZ3iJORVVxcJ27W36TAJ6b4jcr +yWjO/nc42XdgknS8r9NIV7VKzmjY7Ip2+9N6JOAWFkjGrnF1G69nrerIJavJTzrb +PYlQnzJO6SHAoi5j6WsKPw== +-----END CERTIFICATE----- diff --git a/ext/openssl/tests/bug41033.pem b/ext/openssl/tests/bug41033.pem new file mode 100644 index 0000000..4ae74b9 --- /dev/null +++ b/ext/openssl/tests/bug41033.pem @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQCrQ/By/Y5OQRmmc/e+W+eFVoeR5y8WPOkykwS2hc21aSNY5X3q +8ZHdV467thFd/QCoR55hHTRGRbYmfOkXSiscotU08ISlxIH39EEhFSzwqzkxFfak +cgHEu41AUOIfJ2Dz+vcmuasME159pDP0d0gt55pKRPcXoh916p2VS/FBiQIVAMnQ +C6W+K1brelHqpUqwQ1cdNJklAoGAN858gG/UIF+U3CYTcgl5/OUAqOzvitMV2ue+ +AkDEkGNEZs3KUAjpqHduf1E3znl7hJJIRr+33sul9USxn0vczDBkEJPralQjNX2C +dnYKDDhJ+UKlAFG2JZint4CBKPFiZC0tVo04iDQQUUfDC4c8K3cS5uzypebJyoLo +e5b8rScCgYBedJg6vklhMWv2wZD10hbQaXEX5r8T6EQujbfO0RcKpuaJziPPrXO8 +QwPtLt0f40yjTmPxN3LcpgMymiun9UCSTZ3MhVKekCmSNzs5+lQpCm1VlDrCg+jn +djw0VCX8Cm0lOPIyQ4eCNAB6nQLtBnXFWaqYuUS8iVDE7wmT0iwnkAIVAMKogWVA +ZOKwjTj9Yztv3lGj7VTa +-----END DSA PRIVATE KEY----- diff --git a/ext/openssl/tests/bug41033.phpt b/ext/openssl/tests/bug41033.phpt new file mode 100644 index 0000000..4aeae66 --- /dev/null +++ b/ext/openssl/tests/bug41033.phpt @@ -0,0 +1,27 @@ +--TEST-- +#41033, enable signing with DSA keys +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip, openssl required"); +if (OPENSSL_VERSION_NUMBER < 0x009070af) die("skip"); +?> +--FILE-- +<?php +$prv = 'file://' . dirname(__FILE__) . '/' . 'bug41033.pem'; +$pub = 'file://' . dirname(__FILE__) . '/' . 'bug41033pub.pem'; + + +$prkeyid = openssl_get_privatekey($prv, "1234"); +$ct = "Hello I am some text!"; +openssl_sign($ct, $signature, $prkeyid, OPENSSL_ALGO_DSS1); +echo "Signature: ".base64_encode($signature) . "\n"; + +$pukeyid = openssl_get_publickey($pub); +$valid = openssl_verify($ct, $signature, $pukeyid, OPENSSL_ALGO_DSS1); +echo "Signature validity: " . $valid . "\n"; + + +?> +--EXPECTF-- +Signature: %s +Signature validity: 1 diff --git a/ext/openssl/tests/bug41033pub.pem b/ext/openssl/tests/bug41033pub.pem new file mode 100644 index 0000000..0d4ab27 --- /dev/null +++ b/ext/openssl/tests/bug41033pub.pem @@ -0,0 +1,12 @@ +-----BEGIN PUBLIC KEY----- +MIIBtjCCASsGByqGSM44BAEwggEeAoGBAKtD8HL9jk5BGaZz975b54VWh5HnLxY8 +6TKTBLaFzbVpI1jlferxkd1Xjru2EV39AKhHnmEdNEZFtiZ86RdKKxyi1TTwhKXE +gff0QSEVLPCrOTEV9qRyAcS7jUBQ4h8nYPP69ya5qwwTXn2kM/R3SC3nmkpE9xei +H3XqnZVL8UGJAhUAydALpb4rVut6UeqlSrBDVx00mSUCgYA3znyAb9QgX5TcJhNy +CXn85QCo7O+K0xXa574CQMSQY0RmzcpQCOmod25/UTfOeXuEkkhGv7fey6X1RLGf +S9zMMGQQk+tqVCM1fYJ2dgoMOEn5QqUAUbYlmKe3gIEo8WJkLS1WjTiINBBRR8ML +hzwrdxLm7PKl5snKguh7lvytJwOBhAACgYBedJg6vklhMWv2wZD10hbQaXEX5r8T +6EQujbfO0RcKpuaJziPPrXO8QwPtLt0f40yjTmPxN3LcpgMymiun9UCSTZ3MhVKe +kCmSNzs5+lQpCm1VlDrCg+jndjw0VCX8Cm0lOPIyQ4eCNAB6nQLtBnXFWaqYuUS8 +iVDE7wmT0iwnkA== +-----END PUBLIC KEY----- diff --git a/ext/openssl/tests/bug41353.phpt b/ext/openssl/tests/bug41353.phpt new file mode 100644 index 0000000..47fda89 --- /dev/null +++ b/ext/openssl/tests/bug41353.phpt @@ -0,0 +1,16 @@ +--TEST-- +Bug #41353 (openssl_pkcs12_read() does not verify the type of the first arg) +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +?> +--FILE-- +<?php + +$a = 2; +openssl_pkcs12_read(1, $a, 1); + +echo "Done\n"; +?> +--EXPECTF-- +Done diff --git a/ext/openssl/tests/bug46127.pem b/ext/openssl/tests/bug46127.pem new file mode 100644 index 0000000..9d754d4 --- /dev/null +++ b/ext/openssl/tests/bug46127.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIC5jCCAk+gAwIBAgIBADANBgkqhkiG9w0BAQQFADBcMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKUXVlZW5zbGFuZDEaMBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQx +HDAaBgNVBAMTE1Rlc3QgUENBICgxMDI0IGJpdCkwHhcNOTkxMjAyMjEzNTQ4WhcN +MDUwNzExMjEzNTQ4WjBcMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFu +ZDEaMBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxHDAaBgNVBAMTE1Rlc3QgUENB +ICgxMDI0IGJpdCkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ2haT/f5Zwy +V+MiuSDjSR62adBoSiBB7Usty44lXqsp9RICw+DCCxpsn/CfxPEDXLLd4olsWXc6 +JRcxGynbYmnzk+Z6aIPPJQhK3CTvaqGnWKZsA1m+WaUIUqJCuNTK4N+7hMAGaf6S +S3e9HVgEQ4a34gXJ7VQFVIBNV1EnZRWHAgMBAAGjgbcwgbQwHQYDVR0OBBYEFE0R +aEcrj18q1dw+G6nJbsTWR213MIGEBgNVHSMEfTB7gBRNEWhHK49fKtXcPhupyW7E +1kdtd6FgpF4wXDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClF1ZWVuc2xhbmQxGjAY +BgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRwwGgYDVQQDExNUZXN0IFBDQSAoMTAy +NCBiaXQpggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAUa8B3pho ++Mvxeq9HsEzJxHIFQla05S5J/e/V+DQTYoKiRFchKPrDAdrzYSEvP3h4QJEtsNqQ +JfOxg5M42uLFq7aPGWkF6ZZqZsYS+zA9IVT14g7gNA6Ne+5QtJqQtH9HA24st0T0 +Tga/lZ9M2ovImovaxSL/kRHbpCWcqWVxpOw= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCdoWk/3+WcMlfjIrkg40ketmnQaEogQe1LLcuOJV6rKfUSAsPg +wgsabJ/wn8TxA1yy3eKJbFl3OiUXMRsp22Jp85PmemiDzyUIStwk72qhp1imbANZ +vlmlCFKiQrjUyuDfu4TABmn+kkt3vR1YBEOGt+IFye1UBVSATVdRJ2UVhwIDAQAB +AoGAba4fTtuap5l7/8ZsbE7Z1O32KJY4ZcOZukLOLUUhXxXduT+FTgGWujc0/rgc +z9qYCLlNZHOouMYTgtSfYvuMuLZ11VIt0GYH+nRioLShE59Yy+zCRyC+gPigS1kz +xvo14AsOIPYV14Tk/SsHyq6E0eTk7VzaIE197giiINUERPECQQDSKmtPTh/lRKw7 +HSZSM0I1mFWn/1zqrAbontRQY5w98QWIOe5qmzYyFbPXYT3d9BzlsMyhgiRNoBbD +yvohSHXJAkEAwAHx6ezAZeWWzD5yXD36nyjpkVCw7Tk7TSmOceLJMWt1QcrCfqlS +xA5jjpQ6Z8suU5DdtWAryM2sAir1WisYzwJAd6Zcx56jvAQ3xcPXsE6scBTVFzrj +7FqZ6E+cclPzfLQ+QQsyOBE7bpI6e/FJppY26XGZXo3YGzV8IGXrt40oOQJALETG +h86EFXo3qGOFbmsDy4pdP5nBERCu8X1xUCSfintiD4c2DInxgS5oGclnJeMcjTvL +QjQoJCX3UJCi/OUO1QJBAKgcDHWjMvt+l1pjJBsSEZ0HX9AAIIVx0RQmbFGS+F2Q +hhu5l77WnnZOQ9vvhV5u7NPCUF9nhU3jh60qWWO8mkc= +-----END RSA PRIVATE KEY----- diff --git a/ext/openssl/tests/bug46127.phpt b/ext/openssl/tests/bug46127.phpt new file mode 100644 index 0000000..a3bfd3a --- /dev/null +++ b/ext/openssl/tests/bug46127.phpt @@ -0,0 +1,58 @@ +--TEST-- +#46127, openssl_sign/verify: accept different algos +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip, openssl required"); +if (!extension_loaded("pcntl")) die("skip, pcntl required"); +if (OPENSSL_VERSION_NUMBER < 0x009070af) die("skip"); +?> +--FILE-- +<?php + +function ssl_server($port) { + $pem = dirname(__FILE__) . '/bug46127.pem'; + $ssl = array( + 'verify_peer' => false, + 'allow_self_signed' => true, + 'local_cert' => $pem, + // 'passphrase' => '', + ); + $context = stream_context_create(array('ssl' => $ssl)); + $sock = stream_socket_server('ssl://127.0.0.1:'.$port, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context); + if (!$sock) return false; + + $link = stream_socket_accept($sock); + if (!$link) return false; // bad link? + + fputs($link, "Sending bug 46127\n"); + + // close stuff + fclose($link); + fclose($sock); + + exit; +} + +echo "Running bug46127\n"; + +$port = rand(15000, 32000); + +$pid = pcntl_fork(); +if ($pid == 0) { // child + ssl_server($port); + exit; +} + +// client or failed +sleep(1); +$sock = fsockopen('ssl://127.0.0.1', $port, $errno, $errstr); +if (!$sock) exit; + +echo fgets($sock); + +pcntl_waitpid($pid, $status); + +?> +--EXPECTF-- +Running bug46127 +Sending bug 46127 diff --git a/ext/openssl/tests/bug47828.phpt b/ext/openssl/tests/bug47828.phpt new file mode 100644 index 0000000..6f7ee39 --- /dev/null +++ b/ext/openssl/tests/bug47828.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #47828 (segfaults when a UTF-8 conversion fails openssl_x509_parse()) +--SKIPIF-- +<?php if (!extension_loaded("openssl")) die("skip"); ?> +--FILE-- +<?php +$csr = "-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgICAtUwDQYJKoZIhvcNAQEFBQAwgewxFjAUBgNVBC0DDQBT +UFI5NjEyMTdOSzkxETAPBgNVBAcTCENveW9hY+FuMQswCQYDVQQIEwJERjELMAkG +A1UEBhMCTVgxDjAMBgNVBBETBTA0MDAwMR8wHQYDVQQJExZQYW56YWNvbGEgIzYy +IDFlciBwaXNvMSgwJgYDVQQDEx9BdXRvcmlkYWQgY2VydGlmaWNhZG9yYSBJbnRl +cm5hMRMwEQYDVQQLEwpUZWNub2xvZ+1hMRMwEQYDVQQKEwpTZWd1cmlEYXRhMSAw +HgYJKoZIhvcNAQkBFhFhY0BzZWd1cmlkYXRhLmNvbTAeFw0wNzAyMTIwMDAwMDBa +Fw0xMjAyMjkwMDAwMDBaMIIBDDEWMBQGA1UELQMNAFNQUjk2MTIxN05LOTEXMBUG +A1UEBxMOQWx2YXJvIE9icmVnb24xDTALBgNVBAgTBEQuRi4xCzAJBgNVBAYTAk1Y +MQ4wDAYDVQQREwUwMTAwMDEoMCYGA1UECRMfSW5zdXJnZW50ZXMgU3VyIDIzNzUs +IDNlci4gUGlzbzEbMBkGA1UEAxMSd3d3LnNlZ3VyaWRhdGEuY29tMREwDwYDVQQL +EwhJbnRlcm5ldDEpMCcGA1UEChMgU2VndXJpRGF0YSBQcml2YWRhLCBTLkEuIGRl +IEMuVi4xKDAmBgkqhkiG9w0BCQEWGXBvc3RtYXN0ZXJAc2VndXJpZGF0YS5jb20w +gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANG/rb52Ou//dnkHysR5m7T4r8QM +KOM/CP0OEXTOC+a+47RsZjqNiZsBkSeR92OFPpkw5bJ85IAD/Tgx7Tli3ryJfrdk +WMfkXpzWW0YmeTrghL0DMNd8nYc9voVv+OGnIZ0W4Mhz31eiThmyy7Fs8ZlFyfkR +REj5OQvq+z+NP/n/AgMBAAGjODA2MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1Ud +DwQFAwMH6AAwEQYJYIZIAYb4QgEBBAQDAgBAMA0GCSqGSIb3DQEBBQUAA4IBAQCq +nBqQEb7H6Gxi4KXBn1lrPd5KWO40iSD7BREU8e0eI1ZLZvi4IEAlmyG81Le037jo +irMUDS2Ue5WI61QnGw4LhnYlCIuffU7fTs+UbrOE4qNU67G+XBfjk0gHkXHmEYbb +EOR9OHeDcYFgcl3j4SLg/ff6oRYbMkQRCrgQzrl/MNkuqDWJrcigS9OD6OTgRyEo +7Zvf7/ofWIzTIvINbfjQzSTr8AbI4SbuU9iKgVGDQQF6cfpBmOYgnr3QPuoTQCoU +pz9H9wBlz/Nmw12YtfCmGqpIFAxpRGFQTGPNJWr4FdZkUM792lm7Sf3zzSvi8Ruz +M3dwifRsZyZyruy4tMsu +-----END CERTIFICATE----- +"; +$cert = str_replace("\\n", "\n", $csr); +$arr = openssl_x509_parse($cert); +var_dump($arr['hash']); +echo "Done"; +?> +--EXPECTF-- +string(8) "%s" +Done diff --git a/ext/openssl/tests/bug48182.phpt b/ext/openssl/tests/bug48182.phpt new file mode 100644 index 0000000..146c4c9 --- /dev/null +++ b/ext/openssl/tests/bug48182.phpt @@ -0,0 +1,92 @@ +--TEST-- +#48182,ssl handshake fails during asynchronous socket connection +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip, openssl required"); +if (!extension_loaded("pcntl")) die("skip, pcntl required"); +if (OPENSSL_VERSION_NUMBER < 0x009070af) die("skip"); +?> +--FILE-- +<?php + +function ssl_server($port) { + $host = 'ssl://127.0.0.1'.':'.$port; + $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; + $data = "Sending bug48182\n"; + + $pem = dirname(__FILE__) . '/bug46127.pem'; + $ssl_params = array( 'verify_peer' => false, 'allow_self_signed' => true, 'local_cert' => $pem); + $ssl = array('ssl' => $ssl_params); + + $context = stream_context_create($ssl); + $sock = stream_socket_server($host, $errno, $errstr, $flags, $context); + if (!$sock) return false; + + $link = stream_socket_accept($sock); + if (!$link) return false; // bad link? + + $r = array($link); + $w = array(); + $e = array(); + if (stream_select($r, $w, $e, 1, 0) != 0) + $data .= fread($link, 8192); + + $r = array(); + $w = array($link); + if (stream_select($r, $w, $e, 1, 0) != 0) + $wrote = fwrite($link, $data, strlen($data)); + + // close stuff + fclose($link); + fclose($sock); + + exit; +} + +function ssl_async_client($port) { + $host = 'ssl://127.0.0.1'.':'.$port; + $flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT; + $data = "Sending data over to SSL server in async mode with contents like Hello World\n"; + + $socket = stream_socket_client($host, $errno, $errstr, 10, $flags); + stream_set_blocking($socket, 0); + + while ($socket && $data) { + $wrote = fwrite($socket, $data, strlen($data)); + $data = substr($data, $wrote); + } + + $r = array($socket); + $w = array(); + $e = array(); + if (stream_select($r, $w, $e, 1, 0) != 0) + { + $data .= fread($socket, 1024); + } + + echo "$data"; + + fclose($socket); +} + +echo "Running bug48182\n"; + +$port = rand(15000, 32000); + +$pid = pcntl_fork(); +if ($pid == 0) { // child + ssl_server($port); + exit; +} + +// client or failed +sleep(1); +ssl_async_client($port); + +pcntl_waitpid($pid, $status); + +?> +--EXPECTF-- +Running bug48182 +Sending bug48182 +Sending data over to SSL server in async mode with contents like Hello World diff --git a/ext/openssl/tests/bug54060.phpt b/ext/openssl/tests/bug54060.phpt new file mode 100644 index 0000000..88f1f94 --- /dev/null +++ b/ext/openssl/tests/bug54060.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #54060 (Memory leak in openssl_encrypt) +--SKIPIF-- +<?php if (!extension_loaded("openssl")) die("skip"); ?> +--FILE-- +<?php + +$data = "jfdslkjvflsdkjvlkfjvlkjfvlkdm,4w 043920r 9234r 32904r 09243 +r7-89437 r892374 r894372 r894 7289r7 f frwerfh i iurf iuryw uyrfouiwy ruy +972439 8478942 yrhfjkdhls"; +$pass = "r23498rui324hjbnkj"; + +openssl_encrypt($data, 'des3', $pass, 0, '1qazxsw2'); +echo "Done"; +?> +--EXPECT-- +Done diff --git a/ext/openssl/tests/bug54061.phpt b/ext/openssl/tests/bug54061.phpt new file mode 100644 index 0000000..f1d2e24 --- /dev/null +++ b/ext/openssl/tests/bug54061.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #54061 (Memory leak in openssl_decrypt) +--SKIPIF-- +<?php if (!extension_loaded("openssl")) die("skip"); ?> +--FILE-- +<?php +$data = "jfdslkjvflsdkjvlkfjvlkjfvlkdm,4w 043920r 9234r 32904r 09243 +r7-89437 r892374 r894372 r894 7289r7 f frwerfh i iurf iuryw uyrfouiwy ruy +972439 8478942 yrhfjkdhls"; +$pass = "r23498rui324hjbnkj"; + +$cr = openssl_encrypt($data, 'des3', $pass, 0, '1qazxsw2'); +$dcr = openssl_decrypt($cr, 'des3', $pass, 0, '1qazxsw2'); +echo "Done"; +?> +--EXPECT-- +Done diff --git a/ext/openssl/tests/bug54992-ca.pem b/ext/openssl/tests/bug54992-ca.pem new file mode 100644 index 0000000..0fdbb2f --- /dev/null +++ b/ext/openssl/tests/bug54992-ca.pem @@ -0,0 +1,42 @@ +-----BEGIN CERTIFICATE----- +MIIHZzCCBU+gAwIBAgIBATANBgkqhkiG9w0BAQQFADCByzEpMCcGA1UEAxMgQ2F0 +YXBocmFjdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxCzAJBgNVBAYTAlBUMQ8wDQYD +VQQHEwZMaXNib2ExETAPBgNVBAgTCFBvcnR1Z2FsMSkwJwYDVQQKEyBDYXRhcGhy +YWN0IENlcnRpZmljYXRlIEF1dGhvcml0eTEcMBoGA1UECxMTQ2VydGlmaWNhdGUg +U2lnbmluZzEkMCIGCSqGSIb3DQEJARYVQ2F0YXBocmFjdEBuZXRjYWJvLnB0MB4X +DTAzMTIwNTAwMTExOVoXDTE4MTIwMTAwMTExOVowgcsxKTAnBgNVBAMTIENhdGFw +aHJhY3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQswCQYDVQQGEwJQVDEPMA0GA1UE +BxMGTGlzYm9hMREwDwYDVQQIEwhQb3J0dWdhbDEpMCcGA1UEChMgQ2F0YXBocmFj +dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHDAaBgNVBAsTE0NlcnRpZmljYXRlIFNp +Z25pbmcxJDAiBgkqhkiG9w0BCQEWFUNhdGFwaHJhY3RAbmV0Y2Fiby5wdDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANg+noZuxtWdxmZjxanJGEpzmDYu +Uko9OHdmhVr3UU+z04a9JFT7aH5wuwrnpadNy1u9CqrSHVWFEtSmOMOH8QYzIy4C +qCjFPSJR5UQjxpxTZeXaTvfhKI9n0LMSqc7I68HkP5MF64N3Z2cRdYvM4U6R5ERD +Xw2LiRpii/+J2cezgi/Nw3vS4hZlWDWMkttfBd0HKSwxxN7OlPcjyzoTVhQgSISV +Zvd3kwENTWD7s3EnnPRtMiW8Vzcjd8eSTCSjuGBG/8NnI44amLo7gSWocCJ2os69 +CJgiqMpp0tLT8cJm0mQUBk0o9gBS7l1GPpgq5fwWG+DmoLIHrKjxpuI5v2DW23gx +yimXSyiD1GX0JLlTqZ+klM7Mv7ptnigRXA8F5f4GbVzBlGM1L1EERd8orsSmzPEA +S6puHdlNzjcx00glp1UoAs6+tV39eW/fjiP493biPcar0pNO8QWfRSqPsgy6/qKN +m7x2DoSdTbRgCalBMp57xYCUHIETZvlewGKnQD1Tj9FlbzvOnH6r52gj5U/5r3pn +E4DshILn/qtdRwd/2Dwx/KSyBJznU7Yu0vEeMwQioZ6YFH1FnC4229lHYCN6ByVw +UE7OMH7n0A8SUN8flxr2X7MmWpQsMrgVfrAjufmFwUaeIRq9X3wihDYw0MYP0brU +x5ONmY+VA93gLdStAgMBAAGjggFSMIIBTjASBgNVHRMBAf8ECDAGAQH/AgEBMB0G +A1UdDgQWBBQj+82/Y4YWpR8kIi0obJULkqmBwTCB+AYDVR0jBIHwMIHtgBQj+82/ +Y4YWpR8kIi0obJULkqmBwaGB0aSBzjCByzEpMCcGA1UEAxMgQ2F0YXBocmFjdCBD +ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxCzAJBgNVBAYTAlBUMQ8wDQYDVQQHEwZMaXNi +b2ExETAPBgNVBAgTCFBvcnR1Z2FsMSkwJwYDVQQKEyBDYXRhcGhyYWN0IENlcnRp +ZmljYXRlIEF1dGhvcml0eTEcMBoGA1UECxMTQ2VydGlmaWNhdGUgU2lnbmluZzEk +MCIGCSqGSIb3DQEJARYVQ2F0YXBocmFjdEBuZXRjYWJvLnB0ggEBMAsGA1UdDwQE +AwIBBjARBglghkgBhvhCAQEEBAMCAAcwDQYJKoZIhvcNAQEEBQADggIBAKN6pRY1 +8GwQx378ukmw4pzvODlee5IKSPRT92hfLKNGNUAMu2LFo+bjItpilhSvR4aklRvh +5RBoVE8ejEdZXsz0HobMUUcL9IemaRwBCWHPii7Y3zX2J1FUiS/KmWhrYvw5hb1P +P83f/kxdWhxD+MbwuGc2I/6WgfsRyzevQsxdJgElQvNGkOXsC56pEXm2ChVoLbZL +sZX0zPa5ZzXByQGwXl9eqOkV7fdNKulJPcLPOs/y1cAfcxXrDYHpqBGf9nb14p3C +NaWXFhvq9Khk/QiWKSO4QarPlYS4H0Sl6tp7zBaE+dZHAjci2mSTraUf7q61kqoJ +g/ZA3qupd1rR67NzN+6x/TJmIq0G3GUxnDNNqNAHvS4YJx8g4Ji0F3Qoz3CgKnc4 +HsneYQ/LCLq2pDjsffnLI88MBGbfHZDjdj2nowwX76W/6PPutD5IR/kOmHEaX1TJ +/Ff99bVV4HwNF3GPwmKPmHpw3hB9A/xG9aiQRcYs0reXoYeQ+8nyCGmu41LweFyV +1WVwWJ/MHgdtzJZHdPjeXKMWQzOx3AS3TCc31oi4IEo4NgNigcuvl0qgUcwDRXBI +HZm4f7npm7xiES8BSoq5PIVCj8EXJd4b7Gk6dHGJGO+APaw3kYKqfqg5+AN1e4a5 +x3onNvWhjcwDGgcs/xAfVJIUucEqpC5h0pZq +-----END CERTIFICATE----- diff --git a/ext/openssl/tests/bug54992.pem b/ext/openssl/tests/bug54992.pem new file mode 100644 index 0000000..0675450 --- /dev/null +++ b/ext/openssl/tests/bug54992.pem @@ -0,0 +1,47 @@ +-----BEGIN CERTIFICATE----- +MIIFizCCA3OgAwIBAgIBGTANBgkqhkiG9w0BAQUFADCByzEpMCcGA1UEAxMgQ2F0 +YXBocmFjdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxCzAJBgNVBAYTAlBUMQ8wDQYD +VQQHEwZMaXNib2ExETAPBgNVBAgTCFBvcnR1Z2FsMSkwJwYDVQQKEyBDYXRhcGhy +YWN0IENlcnRpZmljYXRlIEF1dGhvcml0eTEcMBoGA1UECxMTQ2VydGlmaWNhdGUg +U2lnbmluZzEkMCIGCSqGSIb3DQEJARYVQ2F0YXBocmFjdEBuZXRjYWJvLnB0MB4X +DTExMDYwNzIzNTIwM1oXDTE4MTIwMTAwMTExOVowWjEXMBUGA1UEAxMOYnVnNTQ5 +OTIubG9jYWwxCzAJBgNVBAYTAlBUMQ8wDQYDVQQHEwZMaXNib2ExDzANBgNVBAgT +Bkxpc2JvYTEQMA4GA1UEChMHcGhwLm5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEAtUAVQKTgpUPgtFOJ3w3kDJETS45tWeT96kUg1NeYLKW+jNbFhxPoPJv7 +XhfemCaqh2tbq1cdYW906Wp1L+eNQvdTYA2IQG4EQBUlmfyIakOIMsN/RizVkF09 +vlNQwTpaMpqTv7wB8vvwbxb9jbC2ZhQUBEg6PIn18dSstbM9FZ0CAwEAAaOCAWww +ggFoMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFCysG9r7vXtfHa38AUZeCM6tgH9c +MIH4BgNVHSMEgfAwge2AFCP7zb9jhhalHyQiLShslQuSqYHBoYHRpIHOMIHLMSkw +JwYDVQQDEyBDYXRhcGhyYWN0IENlcnRpZmljYXRlIEF1dGhvcml0eTELMAkGA1UE +BhMCUFQxDzANBgNVBAcTBkxpc2JvYTERMA8GA1UECBMIUG9ydHVnYWwxKTAnBgNV +BAoTIENhdGFwaHJhY3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRwwGgYDVQQLExND +ZXJ0aWZpY2F0ZSBTaWduaW5nMSQwIgYJKoZIhvcNAQkBFhVDYXRhcGhyYWN0QG5l +dGNhYm8ucHSCAQEwCwYDVR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglg +hkgBhvhCAQ0EERYPeGNhIGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4ICAQAT +M7Id7nBSvaDXuStLunfeV0WPAh3DkKWCxw9YK0MjK7E/K5xEiYaWWbz9zuHEcKrN +MuflSdYVPXTqvD6mHLFNptOgzG6YMOO+rAAEYB5HZ/PYTO6UWAdSLlS96DpA4SS3 +Qwmrc0eXe1p4U8noEN+N3+rAbetjOuvnLG/cpoQGcA8Mws84B/elzjRne5C8N1rF +Tvdb3bqIqvP1thuPfyh/uIKSQb5ZusHvj7ZBkEs+zQLBRnCcDK4ETXFM0TcKSPar +d11tve/91BqqemwlA+ntVrVTgi/pnw4wuWxa3GOVmeEeWgtv3063wZ3lGv/72PCh +gSjxoCoVLaLPTbC/iG2a5+ca2HcF0TjfJqYNCgosgRGlm5IunvuIv+g5jLcZcDSO +hMw+HzyF8GlDF166YRRb9nUL6AtBisdEw6uQW1vQFRRQS4SGMoArSBw2EBqd7Kvf +ruCMcrkudC8vbWQHMETEvhAXdAjgsIxLeGCPh0/8mtES1Lnr0TWIrM9evPJkKACj +f6CyIASkIDZKFf5JwuUh02qvuNLr/QRELfI1NnA1aTYMQQWWOVCBffu4ce+NPdtl +Uh1vRwWAWI0Zjszw3kUk2vHLbSXeD3bU7gP3IFa1X8XsXBW2SH+BfpNWHUilHj1I +bX+zqjfaRWDJuZqB9y6iTCu8DfBtbMiTUGcI/Rs9wQ== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQC1QBVApOClQ+C0U4nfDeQMkRNLjm1Z5P3qRSDU15gspb6M1sWH +E+g8m/teF96YJqqHa1urVx1hb3TpanUv541C91NgDYhAbgRAFSWZ/IhqQ4gyw39G +LNWQXT2+U1DBOloympO/vAHy+/BvFv2NsLZmFBQESDo8ifXx1Ky1sz0VnQIDAQAB +AoGBALUEnHUkdgv4P7o5WJACAomedqPWSlYmgoVvpvuLmrq0ihuFAGAIvL+TlTgD +JNfWfiejTDlSVtCSDTR1kzZVztitfXDxRkWEjGtFjMhk/DJkql3w10SUtcqCiWqw +/XknyPHZ7A+w7Fu5KRO2LoSIze2ZLKvCfP/M/pLR2fTKGTHtAkEA2NreT1GUnvzj +u1lb2J0nTZbSQHvEkfpEej9akl0Bc5UkskenEsiXE3cJYA1TbEGSqYCmt23x3Rd2 +FYxm6MwV6wJBANX34ZuUOllsS0FJPbkEAps3M4s59daQSFiEkQc5XjPgVB0xVV7s +OEBlGkM3eqcCUOMnMI8L9wfBk49sELZCeJcCQQC/y/TL2q/EXo9c6I/faj+U1Exp +VA5rvhpKtTX6NeBOxh6Kv+z5JAja4nGcTqz2FpkM6giKO+erUFDUhjWOuNK5AkEA +xkmHnCRLxp8jRodXWeQrfigz7ixydLsVMGL5+9XgRPb5PGyBjwwePR70raH2Wls9 +FqU0zPvrnBZ6Zwlgm2cSVQJAPLYA51Z9piajbTuggpioQ5qbUEDkJjmYHbm8eJnK +h5NW/EtCk4SBxAc+8ElPrvJjtZyOPWfm4vZF5sDKtC3Fkg== +-----END RSA PRIVATE KEY----- diff --git a/ext/openssl/tests/bug54992.phpt b/ext/openssl/tests/bug54992.phpt new file mode 100644 index 0000000..768b073 --- /dev/null +++ b/ext/openssl/tests/bug54992.phpt @@ -0,0 +1,44 @@ +--TEST-- +Bug #54992: Stream not closed and error not returned when SSL CN_match fails +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +if (!function_exists('pcntl_fork')) die("skip no fork"); +--FILE-- +<?php +$context = stream_context_create(); + +stream_context_set_option($context, 'ssl', 'local_cert', __DIR__ . "/bug54992.pem"); +stream_context_set_option($context, 'ssl', 'allow_self_signed', true); +$server = stream_socket_server('ssl://127.0.0.1:64321', $errno, $errstr, + STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context); + + +$pid = pcntl_fork(); +if ($pid == -1) { + die('could not fork'); +} else if ($pid) { + $contextC = stream_context_create( + array( + 'ssl' => array( + 'verify_peer' => true, + 'cafile' => __DIR__ . '/bug54992-ca.pem', + 'CN_match' => 'buga_buga', + ) + ) + ); + var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1, + STREAM_CLIENT_CONNECT, $contextC)); +} else { + @pcntl_wait($status); + @stream_socket_accept($server, 1); +} +--EXPECTF-- +Warning: stream_socket_client(): Peer certificate CN=`bug54992.local' did not match expected CN=`buga_buga' in %s on line %d + +Warning: stream_socket_client(): Failed to enable crypto in %s on line %d + +Warning: stream_socket_client(): unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d +bool(false) + + diff --git a/ext/openssl/tests/bug55646.phpt b/ext/openssl/tests/bug55646.phpt new file mode 100644 index 0000000..c11284d --- /dev/null +++ b/ext/openssl/tests/bug55646.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #55646: textual input in openssl_csr_new() is not expected in UTF-8 +--SKIPIF-- +<?php +if (!function_exists('openssl_csr_new')) + die('skip no openssl extension'); +--FILE-- +<?php +function stringAsHex($string){$unpacked = unpack("H*", $string);return implode(" ", str_split($unpacked[1],2));} + +$config = array("digest_alg" => "sha1","x509_extensions" => "v3_ca","req_extensions" => "v3_req","private_key_bits" => 2048,"private_key_type" => OPENSSL_KEYTYPE_RSA,"encrypt_key" => false,); +$csr_info = array( + "countryName" => "US", + "stateOrProvinceName" => "Utah", + "localityName" => "Lindon", + "organizationName" => "Chinese", + "organizationalUnitName" => "IT \xe4\xba\x92", + "commonName" => "www.example.com",); +$private = openssl_pkey_new($config); +while (openssl_error_string()) {} +$csr_res = openssl_csr_new($csr_info, $private, + ['config' => __DIR__."/openssl.cnf"]); +if (!$csr_res) { + while ($e = openssl_error_string()) { $err = $e; } + die("Failed; last error: $err"); +} +openssl_csr_export($csr_res, $csr); +$output = openssl_csr_get_subject($csr); + +echo "A: ".$csr_info["organizationalUnitName"]."\n"; +echo "B: ".stringAsHex($csr_info["organizationalUnitName"])."\n"; +echo "C: ".$output['OU']."\n"; +echo "D: ".stringAsHex($output['OU'])."\n"; +--EXPECT-- +A: IT 互 +B: 49 54 20 e4 ba 92 +C: IT 互 +D: 49 54 20 e4 ba 92 diff --git a/ext/openssl/tests/bug61124.phpt b/ext/openssl/tests/bug61124.phpt new file mode 100644 index 0000000..2fc192d --- /dev/null +++ b/ext/openssl/tests/bug61124.phpt @@ -0,0 +1,12 @@ +--TEST--
+Bug #61124: Segmentation fault with openssl_decrypt
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+--FILE--
+<?php
+var_dump(openssl_decrypt('kzo w2RMExUTYQXW2Xzxmg==', 'aes-128-cbc', 'pass', false, 'pass'));
+
+--EXPECTF--
+Warning: openssl_decrypt(): Failed to base64 decode the input in %s on line %s
+bool(false)
\ No newline at end of file diff --git a/ext/openssl/tests/bug61930.phpt b/ext/openssl/tests/bug61930.phpt new file mode 100644 index 0000000..55dc42f --- /dev/null +++ b/ext/openssl/tests/bug61930.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #61930: openssl corrupts ssl key resource when using openssl_get_publickey() +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +?> +--FILE-- +<?php +$cert = file_get_contents(__DIR__.'/cert.crt'); + +$data = <<<DATA +Please verify me +DATA; + +$sig = 'f9Gyb6NV/ENn7GUa37ygTLcF93XHf5fbFTnoYF/O+fXbq3iChGUbET0RuhOsptlAODi6JsDLnJO4ikcVZo0tC1fFTj3LyCuPy3ZdgJbbVxQ/rviROCmuMFTqUW/Xa2LQYiapeCCgLQeWTLg7TM/BoHEkKbKLG/XT5jHvep1758A='; + +$key = openssl_get_publickey($cert); +var_dump(openssl_get_publickey($key)); +var_dump(openssl_verify($data, base64_decode($sig), $key)); +?> +--EXPECTF-- +resource(%d) of type (OpenSSL key) +int(1) + diff --git a/ext/openssl/tests/cert.crt b/ext/openssl/tests/cert.crt new file mode 100644 index 0000000..2e77905 --- /dev/null +++ b/ext/openssl/tests/cert.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAtWgAwIBAgIJAK7FVsxyN1CiMA0GCSqGSIb3DQEBBQUAMIGBMQswCQYD +VQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTATBgNVBAcTDFBv +cnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5nZWxvMR8wHQYJ +KoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0MB4XDTA4MDYzMDEwMjg0M1oXDTA4 +MDczMDEwMjg0M1owgYExCzAJBgNVBAYTAkJSMRowGAYDVQQIExFSaW8gR3JhbmRl +IGRvIFN1bDEVMBMGA1UEBxMMUG9ydG8gQWxlZ3JlMR4wHAYDVQQDExVIZW5yaXF1 +ZSBkbyBOLiBBbmdlbG8xHzAdBgkqhkiG9w0BCQEWEGhuYW5nZWxvQHBocC5uZXQw +gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMteno+QK1ulX4/WDAVBYfoTPRTz +e4SZLwgael4jwWTytj+8c5nNllrFELD6WjJzfjaoIMhCF4w4I2bkWR6/PTqrvnv+ +iiiItHfKvJgYqIobUhkiKmWa2wL3mgqvNRIqTrTC4jWZuCkxQ/ksqL9O/F6zk+aR +S1d+KbPaqCR5Rw+lAgMBAAGjgekwgeYwHQYDVR0OBBYEFNt+QHK9XDWF7CkpgRLo +Ymhqtz99MIG2BgNVHSMEga4wgauAFNt+QHK9XDWF7CkpgRLoYmhqtz99oYGHpIGE +MIGBMQswCQYDVQQGEwJCUjEaMBgGA1UECBMRUmlvIEdyYW5kZSBkbyBTdWwxFTAT +BgNVBAcTDFBvcnRvIEFsZWdyZTEeMBwGA1UEAxMVSGVucmlxdWUgZG8gTi4gQW5n +ZWxvMR8wHQYJKoZIhvcNAQkBFhBobmFuZ2Vsb0BwaHAubmV0ggkArsVWzHI3UKIw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCP1GUnStC0TBqngr3Kx+zS +UW8KutKO0ORc5R8aV/x9LlaJrzPyQJgiPpu5hXogLSKRIHxQS3X2+Y0VvIpW72LW +PVKPhYlNtO3oKnfoJGKin0eEhXRZMjfEW/kznY+ZZmNifV2r8s+KhNAqI4PbClvn +4vh8xF/9+eVEj+hM+0OflA== +-----END CERTIFICATE----- diff --git a/ext/openssl/tests/openssl.cnf b/ext/openssl/tests/openssl.cnf new file mode 100644 index 0000000..10e6907 --- /dev/null +++ b/ext/openssl/tests/openssl.cnf @@ -0,0 +1,43 @@ +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert +string_mask = MASK:4294967295 + + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State +localityName = Locality Name (eg, city) +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd +organizationalUnitName = Organizational Unit Name (eg, section) +commonName = Common Name (eg, YOUR name) +commonName_max = 64 +emailAddress = Email Address +emailAddress_max = 64 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 +unstructuredName = An optional company name + +[ v3_req ] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints = CA:true + +[ usr_cert ] +basicConstraints=CA:FALSE + diff --git a/ext/openssl/tests/openssl_decrypt_error.phpt b/ext/openssl/tests/openssl_decrypt_error.phpt new file mode 100644 index 0000000..40debbd --- /dev/null +++ b/ext/openssl/tests/openssl_decrypt_error.phpt @@ -0,0 +1,53 @@ +--TEST-- +openssl_decrypt() error tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "openssl_decrypt() tests"; +$method = "AES-128-CBC"; +$password = "openssl"; +$wrong = "wrong"; +$iv = str_repeat("\0", openssl_cipher_iv_length($method)); + +$encrypted = openssl_encrypt($data, $method, $password); +var_dump($encrypted); /* Not passing $iv should be the same as all-NULL iv, but with a warning */ +var_dump(openssl_encrypt($data, $method, $password, 0, $iv)); +var_dump(openssl_decrypt($encrypted, $method, $wrong)); +var_dump(openssl_decrypt($encrypted, $wrong, $password)); +var_dump(openssl_decrypt($wrong, $method, $password)); +var_dump(openssl_decrypt($wrong, $wrong, $password)); +var_dump(openssl_decrypt($encrypted, $wrong, $wrong)); +var_dump(openssl_decrypt($wrong, $wrong, $wrong)); +var_dump(openssl_decrypt(array(), $method, $password)); +var_dump(openssl_decrypt($encrypted, array(), $password)); +var_dump(openssl_decrypt($encrypted, $method, array())); +?> +--EXPECTF-- + +Warning: openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended in %s on line %d +string(44) "yof6cPPH4mLee6TOc0YQSrh4dvywMqxGUyjp0lV6+aM=" +string(44) "yof6cPPH4mLee6TOc0YQSrh4dvywMqxGUyjp0lV6+aM=" +bool(false) + +Warning: openssl_decrypt(): Unknown cipher algorithm in %s on line %d +bool(false) +bool(false) + +Warning: openssl_decrypt(): Unknown cipher algorithm in %s on line %d +bool(false) + +Warning: openssl_decrypt(): Unknown cipher algorithm in %s on line %d +bool(false) + +Warning: openssl_decrypt(): Unknown cipher algorithm in %s on line %d +bool(false) + +Warning: openssl_decrypt() expects parameter 1 to be string, array given in %s on line %d +NULL + +Warning: openssl_decrypt() expects parameter 2 to be string, array given in %s on line %d +NULL + +Warning: openssl_decrypt() expects parameter 3 to be string, array given in %s on line %d +NULL diff --git a/ext/openssl/tests/openssl_digest_basic.phpt b/ext/openssl/tests/openssl_digest_basic.phpt new file mode 100644 index 0000000..a924c06 --- /dev/null +++ b/ext/openssl/tests/openssl_digest_basic.phpt @@ -0,0 +1,16 @@ +--TEST-- +openssl_digest() basic test +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "openssl_digest() basic test"; +$method = "md5"; +$method2 = "sha1"; + +var_dump(openssl_digest($data, $method)); +var_dump(openssl_digest($data, $method2)); +?> +--EXPECT-- +string(32) "f0045b6c41d9ec835cb8948c7fec4955" +string(40) "aa6e750fef05c2414c18860ad31f2c35e79bf3dc" diff --git a/ext/openssl/tests/openssl_encrypt_crash.phpt b/ext/openssl/tests/openssl_encrypt_crash.phpt new file mode 100644 index 0000000..b88782b --- /dev/null +++ b/ext/openssl/tests/openssl_encrypt_crash.phpt @@ -0,0 +1,13 @@ +--TEST-- +openssl_encrypt() crash with old OpenSSL +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +openssl_encrypt('', 'AES-128-CBC', 'foo'); +var_dump("done"); +?> +--EXPECTF-- + +Warning: openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended in %s on line %d +string(4) "done"
\ No newline at end of file diff --git a/ext/openssl/tests/openssl_encrypt_error.phpt b/ext/openssl/tests/openssl_encrypt_error.phpt new file mode 100644 index 0000000..7376f48 --- /dev/null +++ b/ext/openssl/tests/openssl_encrypt_error.phpt @@ -0,0 +1,43 @@ +--TEST-- +openssl_encrypt() error tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +$data = "openssl_encrypt() tests"; +$method = "AES-128-CBC"; +$password = "openssl"; +$wrong = "wrong"; +$object = new stdclass; +$arr = array(1); + +var_dump(openssl_encrypt($data, $wrong, $password)); +var_dump(openssl_encrypt($object, $method, $password)); +var_dump(openssl_encrypt($data, $object, $password)); +var_dump(openssl_encrypt($data, $method, $object)); +var_dump(openssl_encrypt($arr, $method, $object)); +var_dump(openssl_encrypt($data, $arr, $object)); +var_dump(openssl_encrypt($data, $method, $arr)); +?> +--EXPECTF-- +Warning: openssl_encrypt(): Unknown cipher algorithm in %s on line %d +bool(false) + +Warning: openssl_encrypt() expects parameter 1 to be string, object given in %s on line %d +NULL + +Warning: openssl_encrypt() expects parameter 2 to be string, object given in %s on line %d +NULL + +Warning: openssl_encrypt() expects parameter 3 to be string, object given in %s on line %d +NULL + +Warning: openssl_encrypt() expects parameter 1 to be string, array given in %s on line %d +NULL + +Warning: openssl_encrypt() expects parameter 2 to be string, array given in %s on line %d +NULL + +Warning: openssl_encrypt() expects parameter 3 to be string, array given in %s on line %d +NULL + diff --git a/ext/openssl/tests/openssl_random_pseudo_bytes.phpt b/ext/openssl/tests/openssl_random_pseudo_bytes.phpt new file mode 100644 index 0000000..ac5a307 --- /dev/null +++ b/ext/openssl/tests/openssl_random_pseudo_bytes.phpt @@ -0,0 +1,22 @@ +--TEST-- +openssl_random_pseudo_bytes() tests +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; ?> +--FILE-- +<?php +for ($i = 0; $i < 10; $i++) { + var_dump(bin2hex(openssl_random_pseudo_bytes($i, $strong))); +} + +?> +--EXPECTF-- +string(0) "" +string(2) "%s" +string(4) "%s" +string(6) "%s" +string(8) "%s" +string(10) "%s" +string(12) "%s" +string(14) "%s" +string(16) "%s" +string(18) "%s"
\ No newline at end of file diff --git a/ext/openssl/tests/openssl_x509_parse_basic.phpt b/ext/openssl/tests/openssl_x509_parse_basic.phpt new file mode 100644 index 0000000..fb0bbc7 --- /dev/null +++ b/ext/openssl/tests/openssl_x509_parse_basic.phpt @@ -0,0 +1,294 @@ +--TEST-- +openssl_x509_parse() basic test +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; +if (OPENSSL_VERSION_NUMBER < 0x10000000) die("skip Output requires OpenSSL 1.0"); +?> +--FILE-- +<?php +$cert = "file://" . dirname(__FILE__) . "/cert.crt"; + +var_dump(openssl_x509_parse($cert)); +var_dump(openssl_x509_parse($cert, false)); +?> +--EXPECTF-- +array(12) { + ["name"]=> + string(96) "/C=BR/ST=Rio Grande do Sul/L=Porto Alegre/CN=Henrique do N. Angelo/emailAddress=hnangelo@php.net" + ["subject"]=> + array(5) { + ["C"]=> + string(2) "BR" + ["ST"]=> + string(17) "Rio Grande do Sul" + ["L"]=> + string(12) "Porto Alegre" + ["CN"]=> + string(21) "Henrique do N. Angelo" + ["emailAddress"]=> + string(16) "hnangelo@php.net" + } + ["hash"]=> + string(8) "%s" + ["issuer"]=> + array(5) { + ["C"]=> + string(2) "BR" + ["ST"]=> + string(17) "Rio Grande do Sul" + ["L"]=> + string(12) "Porto Alegre" + ["CN"]=> + string(21) "Henrique do N. Angelo" + ["emailAddress"]=> + string(16) "hnangelo@php.net" + } + ["version"]=> + int(2) + ["serialNumber"]=> + string(20) "12593567369101004962" + ["validFrom"]=> + string(13) "080630102843Z" + ["validTo"]=> + string(13) "080730102843Z" + ["validFrom_time_t"]=> + int(1214821723) + ["validTo_time_t"]=> + int(1217413723) + ["purposes"]=> + array(9) { + [1]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(9) "sslclient" + } + [2]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(9) "sslserver" + } + [3]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(11) "nssslserver" + } + [4]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(9) "smimesign" + } + [5]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(12) "smimeencrypt" + } + [6]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(7) "crlsign" + } + [7]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(3) "any" + } + [8]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(10) "ocsphelper" + } + [9]=> + array(3) { + [0]=> + bool(false) + [1]=> + bool(true) + [2]=> + string(13) "timestampsign" + } + } + ["extensions"]=> + array(3) { + ["subjectKeyIdentifier"]=> + string(59) "DB:7E:40:72:BD:5C:35:85:EC:29:29:81:12:E8:62:68:6A:B7:3F:7D" + ["authorityKeyIdentifier"]=> + string(202) "keyid:DB:7E:40:72:BD:5C:35:85:EC:29:29:81:12:E8:62:68:6A:B7:3F:7D +DirName:/C=BR/ST=Rio Grande do Sul/L=Porto Alegre/CN=Henrique do N. Angelo/emailAddress=hnangelo@php.net +serial:AE:C5:56:CC:72:37:50:A2 +" + ["basicConstraints"]=> + string(7) "CA:TRUE" + } +} +array(12) { + ["name"]=> + string(96) "/C=BR/ST=Rio Grande do Sul/L=Porto Alegre/CN=Henrique do N. Angelo/emailAddress=hnangelo@php.net" + ["subject"]=> + array(5) { + ["countryName"]=> + string(2) "BR" + ["stateOrProvinceName"]=> + string(17) "Rio Grande do Sul" + ["localityName"]=> + string(12) "Porto Alegre" + ["commonName"]=> + string(21) "Henrique do N. Angelo" + ["emailAddress"]=> + string(16) "hnangelo@php.net" + } + ["hash"]=> + string(8) "%s" + ["issuer"]=> + array(5) { + ["countryName"]=> + string(2) "BR" + ["stateOrProvinceName"]=> + string(17) "Rio Grande do Sul" + ["localityName"]=> + string(12) "Porto Alegre" + ["commonName"]=> + string(21) "Henrique do N. Angelo" + ["emailAddress"]=> + string(16) "hnangelo@php.net" + } + ["version"]=> + int(2) + ["serialNumber"]=> + string(20) "12593567369101004962" + ["validFrom"]=> + string(13) "080630102843Z" + ["validTo"]=> + string(13) "080730102843Z" + ["validFrom_time_t"]=> + int(1214821723) + ["validTo_time_t"]=> + int(1217413723) + ["purposes"]=> + array(9) { + [1]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(10) "SSL client" + } + [2]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(10) "SSL server" + } + [3]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(19) "Netscape SSL server" + } + [4]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(14) "S/MIME signing" + } + [5]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(17) "S/MIME encryption" + } + [6]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(11) "CRL signing" + } + [7]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(11) "Any Purpose" + } + [8]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(11) "OCSP helper" + } + [9]=> + array(3) { + [0]=> + bool(false) + [1]=> + bool(true) + [2]=> + string(18) "Time Stamp signing" + } + } + ["extensions"]=> + array(3) { + ["subjectKeyIdentifier"]=> + string(59) "DB:7E:40:72:BD:5C:35:85:EC:29:29:81:12:E8:62:68:6A:B7:3F:7D" + ["authorityKeyIdentifier"]=> + string(202) "keyid:DB:7E:40:72:BD:5C:35:85:EC:29:29:81:12:E8:62:68:6A:B7:3F:7D +DirName:/C=BR/ST=Rio Grande do Sul/L=Porto Alegre/CN=Henrique do N. Angelo/emailAddress=hnangelo@php.net +serial:AE:C5:56:CC:72:37:50:A2 +" + ["basicConstraints"]=> + string(7) "CA:TRUE" + } +} diff --git a/ext/openssl/tests/openssl_x509_parse_basic_v9.phpt b/ext/openssl/tests/openssl_x509_parse_basic_v9.phpt new file mode 100644 index 0000000..89862ef --- /dev/null +++ b/ext/openssl/tests/openssl_x509_parse_basic_v9.phpt @@ -0,0 +1,276 @@ +--TEST-- +openssl_x509_parse() basic test for OpenSSL 0.9 +--SKIPIF-- +<?php if (!extension_loaded("openssl")) print "skip"; +if (OPENSSL_VERSION_NUMBER > 0x10000000) die("skip Output requires OpenSSL 0.9"); +?> +--FILE-- +<?php +$cert = "file://" . dirname(__FILE__) . "/cert.crt"; + +var_dump(openssl_x509_parse($cert)); +var_dump(openssl_x509_parse($cert, false)); +?> +--EXPECTF-- +array(12) { + ["name"]=> + string(96) "/C=BR/ST=Rio Grande do Sul/L=Porto Alegre/CN=Henrique do N. Angelo/emailAddress=hnangelo@php.net" + ["subject"]=> + array(5) { + ["C"]=> + string(2) "BR" + ["ST"]=> + string(17) "Rio Grande do Sul" + ["L"]=> + string(12) "Porto Alegre" + ["CN"]=> + string(21) "Henrique do N. Angelo" + ["emailAddress"]=> + string(16) "hnangelo@php.net" + } + ["hash"]=> + string(8) "%s" + ["issuer"]=> + array(5) { + ["C"]=> + string(2) "BR" + ["ST"]=> + string(17) "Rio Grande do Sul" + ["L"]=> + string(12) "Porto Alegre" + ["CN"]=> + string(21) "Henrique do N. Angelo" + ["emailAddress"]=> + string(16) "hnangelo@php.net" + } + ["version"]=> + int(2) + ["serialNumber"]=> + string(20) "12593567369101004962" + ["validFrom"]=> + string(13) "080630102843Z" + ["validTo"]=> + string(13) "080730102843Z" + ["validFrom_time_t"]=> + int(1214821723) + ["validTo_time_t"]=> + int(1217413723) + ["purposes"]=> + array(8) { + [1]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(9) "sslclient" + } + [2]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(9) "sslserver" + } + [3]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(11) "nssslserver" + } + [4]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(9) "smimesign" + } + [5]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(12) "smimeencrypt" + } + [6]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(7) "crlsign" + } + [7]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(3) "any" + } + [8]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(10) "ocsphelper" + } + } + ["extensions"]=> + array(3) { + ["subjectKeyIdentifier"]=> + string(59) "DB:7E:40:72:BD:5C:35:85:EC:29:29:81:12:E8:62:68:6A:B7:3F:7D" + ["authorityKeyIdentifier"]=> + string(202) "keyid:DB:7E:40:72:BD:5C:35:85:EC:29:29:81:12:E8:62:68:6A:B7:3F:7D +DirName:/C=BR/ST=Rio Grande do Sul/L=Porto Alegre/CN=Henrique do N. Angelo/emailAddress=hnangelo@php.net +serial:AE:C5:56:CC:72:37:50:A2 +" + ["basicConstraints"]=> + string(7) "CA:TRUE" + } +} +array(12) { + ["name"]=> + string(96) "/C=BR/ST=Rio Grande do Sul/L=Porto Alegre/CN=Henrique do N. Angelo/emailAddress=hnangelo@php.net" + ["subject"]=> + array(5) { + ["countryName"]=> + string(2) "BR" + ["stateOrProvinceName"]=> + string(17) "Rio Grande do Sul" + ["localityName"]=> + string(12) "Porto Alegre" + ["commonName"]=> + string(21) "Henrique do N. Angelo" + ["emailAddress"]=> + string(16) "hnangelo@php.net" + } + ["hash"]=> + string(8) "%s" + ["issuer"]=> + array(5) { + ["countryName"]=> + string(2) "BR" + ["stateOrProvinceName"]=> + string(17) "Rio Grande do Sul" + ["localityName"]=> + string(12) "Porto Alegre" + ["commonName"]=> + string(21) "Henrique do N. Angelo" + ["emailAddress"]=> + string(16) "hnangelo@php.net" + } + ["version"]=> + int(2) + ["serialNumber"]=> + string(20) "12593567369101004962" + ["validFrom"]=> + string(13) "080630102843Z" + ["validTo"]=> + string(13) "080730102843Z" + ["validFrom_time_t"]=> + int(1214821723) + ["validTo_time_t"]=> + int(1217413723) + ["purposes"]=> + array(8) { + [1]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(10) "SSL client" + } + [2]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(10) "SSL server" + } + [3]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(19) "Netscape SSL server" + } + [4]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(14) "S/MIME signing" + } + [5]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(17) "S/MIME encryption" + } + [6]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(11) "CRL signing" + } + [7]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(11) "Any Purpose" + } + [8]=> + array(3) { + [0]=> + bool(true) + [1]=> + bool(true) + [2]=> + string(11) "OCSP helper" + } + } + ["extensions"]=> + array(3) { + ["subjectKeyIdentifier"]=> + string(59) "DB:7E:40:72:BD:5C:35:85:EC:29:29:81:12:E8:62:68:6A:B7:3F:7D" + ["authorityKeyIdentifier"]=> + string(202) "keyid:DB:7E:40:72:BD:5C:35:85:EC:29:29:81:12:E8:62:68:6A:B7:3F:7D +DirName:/C=BR/ST=Rio Grande do Sul/L=Porto Alegre/CN=Henrique do N. Angelo/emailAddress=hnangelo@php.net +serial:AE:C5:56:CC:72:37:50:A2 +" + ["basicConstraints"]=> + string(7) "CA:TRUE" + } +} diff --git a/ext/openssl/tests/private.key b/ext/openssl/tests/private.key new file mode 100644 index 0000000..bce512e --- /dev/null +++ b/ext/openssl/tests/private.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDLXp6PkCtbpV+P1gwFQWH6Ez0U83uEmS8IGnpeI8Fk8rY/vHOZ +zZZaxRCw+loyc342qCDIQheMOCNm5Fkevz06q757/oooiLR3yryYGKiKG1IZIipl +mtsC95oKrzUSKk60wuI1mbgpMUP5LKi/Tvxes5PmkUtXfimz2qgkeUcPpQIDAQAB +AoGBAMcP/dp+fsI9FFYBaVC3mASlUjOwxKWdH3kqGb8N9p4uKRAoEWtp3hNJM7ZX +x3P8sn0jgrsiXlRFGvn65/T9shp8hj+CdJKg2jKCs7S58v60TLfSvOQSIYsw9Qm9 +Bsx4hKfz+d52ptuJRbv8tDxsYP3D/KjQfpX1OysiP/WBfeg9AkEA+AGT0goqjWOM +YgFtZGrefIegF31XSCQTaLIml6/2JwF+oBKjJUQFar2Rwn6qUwrsGtSPMM0Iz8ry +9uvUbs8PPwJBANHsuTVWzLf8TJNGc+xIlhvzKFkF0nJIWx4ozhlMNDQMMF/3FRSo +zvHIgUnpG9Vwa2GtjTDnD8jHtzTauAZmjBsCQCGDVQ5VAVsJ0LaNqtKe/mGlkiSa +c2j0Nws2x7BHvuOWeB35ZsJqZrD93OyDYVDHcRBPGOpnSoGJ0zs6swImSNECQHSH +0BgH4wSPDYMDrP4RHSLOzCr+zF+cQthvFll8r83kpkXfRth9DMOy5fI9cLH/Adzr +FmF7Iov2MYEpmNYUvtkCQHfW0ntkVY9xS2/VTs57F5tUkfNG2hG74pJM6vSfTNWn +R/oI5m2sDtRWQ88LCYJMEmIZhN00Ys4xOSoTs+SUakY= +-----END RSA PRIVATE KEY----- diff --git a/ext/openssl/tests/public.key b/ext/openssl/tests/public.key new file mode 100644 index 0000000..92021c1 --- /dev/null +++ b/ext/openssl/tests/public.key @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLXp6PkCtbpV+P1gwFQWH6Ez0U +83uEmS8IGnpeI8Fk8rY/vHOZzZZaxRCw+loyc342qCDIQheMOCNm5Fkevz06q757 +/oooiLR3yryYGKiKG1IZIiplmtsC95oKrzUSKk60wuI1mbgpMUP5LKi/Tvxes5Pm +kUtXfimz2qgkeUcPpQIDAQAB +-----END PUBLIC KEY----- diff --git a/ext/openssl/tests/sni_001.phpt b/ext/openssl/tests/sni_001.phpt new file mode 100644 index 0000000..3d7798c --- /dev/null +++ b/ext/openssl/tests/sni_001.phpt @@ -0,0 +1,178 @@ +--TEST-- +SNI 001 +--SKIPIF-- +<?php + if (!extension_loaded('openssl')) die("skip openssl extension not available"); + if (!getenv('SNI_TESTS')) die("skip Set SNI_TESTS to enable this test (uses remote resources)"); +?> +--FILE-- +<?php +/* Server Name Indication (SNI) tests + * + * This test relies on https://sni.velox.ch/ and thus is disabled by default. + * + * sni.velox.ch uses 3 certificates : + * - CN=alice.sni.velox.ch (sent in response to server_name = alice.sni.velox.ch or not set) + * - CN=bob.sni.velox.ch (sent in response to server_name = bob.sni.velox.ch) + * - CN=*.sni.velox.ch (sent in response to server_name = mallory.sni.velox.ch or *.sni.velox.ch or sni.velox.ch) + * + * The test sends requests to the server, sending different names, and checks which certificate + * the server returned. + */ + +function context() { + return stream_context_create(array( + 'ssl' => array( + 'capture_peer_cert' => true, + ), + )); +} + +function get_CN($context) { + + $ary = stream_context_get_options($context); + assert($ary); + + $cert = $ary['ssl']['peer_certificate']; + assert($cert); + + $cert_ary = openssl_x509_parse($cert); + return $cert_ary['subject']['CN']; +} + +function do_http_test($url, $context) { + + $fh = fopen($url, 'r', false, $context); + assert($fh); + + var_dump(get_CN($context)); +} + +function do_ssl_test($url, $context) { + + $fh = stream_socket_client($url, $errno, $errstr, + ini_get("default_socket_timeout"), STREAM_CLIENT_CONNECT, $context); + assert($fh); + + var_dump(get_CN($context)); +} + +function do_enable_crypto_test($url, $context) { + + $fh = stream_socket_client($url, $errno, $errstr, + ini_get("default_socket_timeout"), STREAM_CLIENT_CONNECT, $context); + assert($fh); + + $r = stream_socket_enable_crypto($fh, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); + assert($r); + + var_dump(get_CN($context)); +} + +/* Test https:// streams */ + +echo "-- auto host name (1) --\n"; +do_http_test('https://alice.sni.velox.ch/', context()); + +echo "-- auto host name (2) --\n"; +do_http_test('https://bob.sni.velox.ch/', context()); + +echo "-- auto host name (3) --\n"; +do_http_test('https://bob.sni.velox.ch./', context()); + +echo "-- user supplied server name --\n"; + +$context = context(); +stream_context_set_option($context, 'ssl', 'SNI_server_name', 'bob.sni.velox.ch'); +stream_context_set_option($context, 'http', 'header', b'Host: bob.sni.velox.ch'); +do_http_test('https://alice.sni.velox.ch/', $context); + +echo "-- sni disabled --\n"; + +$context = context(); +stream_context_set_option($context, 'ssl', 'SNI_enabled', false); +do_http_test('https://bob.sni.velox.ch/', $context); + +/* Test ssl:// socket streams */ + +echo "-- raw SSL stream (1) --\n"; +do_ssl_test('ssl://bob.sni.velox.ch:443', context()); + +echo "-- raw SSL stream (2) --\n"; +do_ssl_test('ssl://mallory.sni.velox.ch:443', context()); + +echo "-- raw SSL stream with user supplied sni --\n"; + +$context = context(); +stream_context_set_option($context, 'ssl', 'SNI_server_name', 'bob.sni.velox.ch'); + +do_ssl_test('ssl://mallory.sni.velox.ch:443', $context); + +echo "-- raw SSL stream with sni disabled --\n"; + +$context = context(); +stream_context_set_option($context, 'ssl', 'SNI_enabled', false); + +do_ssl_test('ssl://mallory.sni.velox.ch:443', $context); + +/* Test tcp:// socket streams with SSL enabled */ + +echo "-- stream_socket_enable_crypto (1) --\n"; + +do_enable_crypto_test('tcp://bob.sni.velox.ch:443', context()); + +echo "-- stream_socket_enable_crypto (2) --\n"; + +do_enable_crypto_test('tcp://mallory.sni.velox.ch:443', context()); + +echo "-- stream_socket_enable_crypto with user supplied sni --\n"; + +$context = context(); +stream_context_set_option($context, 'ssl', 'SNI_server_name', 'bob.sni.velox.ch'); + +do_enable_crypto_test('tcp://mallory.sni.velox.ch:443', $context); + +echo "-- stream_socket_enable_crypto with sni disabled --\n"; + +$context = context(); +stream_context_set_option($context, 'ssl', 'SNI_enabled', false); + +do_enable_crypto_test('tcp://mallory.sni.velox.ch:443', $context); + +echo "-- stream_socket_enable_crypto with long name --\n"; + +$context = context(); +stream_context_set_option($context, 'ssl', 'SNI_server_name', str_repeat('a.', 500) . '.sni.velox.ch'); + +do_enable_crypto_test('tcp://mallory.sni.velox.ch:443', $context); + +?> +--EXPECTF-- +-- auto host name (1) -- +%unicode|string%(18) "alice.sni.velox.ch" +-- auto host name (2) -- +%unicode|string%(16) "bob.sni.velox.ch" +-- auto host name (3) -- +%unicode|string%(16) "bob.sni.velox.ch" +-- user supplied server name -- +%unicode|string%(16) "bob.sni.velox.ch" +-- sni disabled -- +%unicode|string%(18) "alice.sni.velox.ch" +-- raw SSL stream (1) -- +%unicode|string%(16) "bob.sni.velox.ch" +-- raw SSL stream (2) -- +%unicode|string%(14) "*.sni.velox.ch" +-- raw SSL stream with user supplied sni -- +%unicode|string%(16) "bob.sni.velox.ch" +-- raw SSL stream with sni disabled -- +%unicode|string%(18) "alice.sni.velox.ch" +-- stream_socket_enable_crypto (1) -- +%unicode|string%(16) "bob.sni.velox.ch" +-- stream_socket_enable_crypto (2) -- +%unicode|string%(14) "*.sni.velox.ch" +-- stream_socket_enable_crypto with user supplied sni -- +%unicode|string%(16) "bob.sni.velox.ch" +-- stream_socket_enable_crypto with sni disabled -- +%unicode|string%(18) "alice.sni.velox.ch" +-- stream_socket_enable_crypto with long name -- +%unicode|string%(18) "alice.sni.velox.ch" diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c new file mode 100644 index 0000000..0e15238 --- /dev/null +++ b/ext/openssl/xp_ssl.c @@ -0,0 +1,971 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong <wez@thebrainroom.com> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "ext/standard/file.h" +#include "ext/standard/url.h" +#include "streams/php_streams_int.h" +#include "ext/standard/php_smart_str.h" +#include "php_network.h" +#include "php_openssl.h" +#include <openssl/ssl.h> +#include <openssl/x509.h> +#include <openssl/err.h> + +#ifdef PHP_WIN32 +#include "win32/time.h" +#endif + +#ifdef NETWARE +#include <sys/select.h> +#endif + +int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC); +SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC); +int php_openssl_get_x509_list_id(void); + +/* This implementation is very closely tied to the that of the native + * sockets implemented in the core. + * Don't try this technique in other extensions! + * */ + +typedef struct _php_openssl_netstream_data_t { + php_netstream_data_t s; + SSL *ssl_handle; + SSL_CTX *ctx; + struct timeval connect_timeout; + int enable_on_connect; + int is_client; + int ssl_active; + php_stream_xport_crypt_method_t method; + char *sni; + unsigned state_set:1; + unsigned _spare:31; +} php_openssl_netstream_data_t; + +php_stream_ops php_openssl_socket_ops; + +/* it doesn't matter that we do some hash traversal here, since it is done only + * in an error condition arising from a network connection problem */ +static int is_http_stream_talking_to_iis(php_stream *stream TSRMLS_DC) +{ + if (stream->wrapperdata && stream->wrapper && strcasecmp(stream->wrapper->wops->label, "HTTP") == 0) { + /* the wrapperdata is an array zval containing the headers */ + zval **tmp; + +#define SERVER_MICROSOFT_IIS "Server: Microsoft-IIS" +#define SERVER_GOOGLE "Server: GFE/" + + zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream->wrapperdata)); + while (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(stream->wrapperdata), (void**)&tmp)) { + + if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_MICROSOFT_IIS, sizeof(SERVER_MICROSOFT_IIS)-1) == 0) { + return 1; + } else if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_GOOGLE, sizeof(SERVER_GOOGLE)-1) == 0) { + return 1; + } + + zend_hash_move_forward(Z_ARRVAL_P(stream->wrapperdata)); + } + } + return 0; +} + +static int handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init TSRMLS_DC) +{ + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; + int err = SSL_get_error(sslsock->ssl_handle, nr_bytes); + char esbuf[512]; + smart_str ebuf = {0}; + unsigned long ecode; + int retry = 1; + + switch(err) { + case SSL_ERROR_ZERO_RETURN: + /* SSL terminated (but socket may still be active) */ + retry = 0; + break; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* re-negotiation, or perhaps the SSL layer needs more + * packets: retry in next iteration */ + errno = EAGAIN; + retry = is_init ? 1 : sslsock->s.is_blocked; + break; + case SSL_ERROR_SYSCALL: + if (ERR_peek_error() == 0) { + if (nr_bytes == 0) { + if (!is_http_stream_talking_to_iis(stream TSRMLS_CC) && ERR_get_error() != 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "SSL: fatal protocol error"); + } + SSL_set_shutdown(sslsock->ssl_handle, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); + stream->eof = 1; + retry = 0; + } else { + char *estr = php_socket_strerror(php_socket_errno(), NULL, 0); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "SSL: %s", estr); + + efree(estr); + retry = 0; + } + break; + } + + + /* fall through */ + default: + /* some other error */ + ecode = ERR_get_error(); + + switch (ERR_GET_REASON(ecode)) { + case SSL_R_NO_SHARED_CIPHER: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL_R_NO_SHARED_CIPHER: no suitable shared cipher could be used. This could be because the server is missing an SSL certificate (local_cert context option)"); + retry = 0; + break; + + default: + do { + /* NULL is automatically added */ + ERR_error_string_n(ecode, esbuf, sizeof(esbuf)); + if (ebuf.c) { + smart_str_appendc(&ebuf, '\n'); + } + smart_str_appends(&ebuf, esbuf); + } while ((ecode = ERR_get_error()) != 0); + + smart_str_0(&ebuf); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "SSL operation failed with code %d. %s%s", + err, + ebuf.c ? "OpenSSL Error messages:\n" : "", + ebuf.c ? ebuf.c : ""); + if (ebuf.c) { + smart_str_free(&ebuf); + } + } + + retry = 0; + errno = 0; + } + return retry; +} + + +static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; + int didwrite; + + if (sslsock->ssl_active) { + int retry = 1; + + do { + didwrite = SSL_write(sslsock->ssl_handle, buf, count); + + if (didwrite <= 0) { + retry = handle_ssl_error(stream, didwrite, 0 TSRMLS_CC); + } else { + break; + } + } while(retry); + + if (didwrite > 0) { + php_stream_notify_progress_increment(stream->context, didwrite, 0); + } + } else { + didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC); + } + + if (didwrite < 0) { + didwrite = 0; + } + + return didwrite; +} + +static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; + int nr_bytes = 0; + + if (sslsock->ssl_active) { + int retry = 1; + + do { + nr_bytes = SSL_read(sslsock->ssl_handle, buf, count); + + if (nr_bytes <= 0) { + retry = handle_ssl_error(stream, nr_bytes, 0 TSRMLS_CC); + stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle)); + + } else { + /* we got the data */ + break; + } + } while (retry); + + if (nr_bytes > 0) { + php_stream_notify_progress_increment(stream->context, nr_bytes, 0); + } + } + else + { + nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC); + } + + if (nr_bytes < 0) { + nr_bytes = 0; + } + + return nr_bytes; +} + + +static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; +#ifdef PHP_WIN32 + int n; +#endif + if (close_handle) { + if (sslsock->ssl_active) { + SSL_shutdown(sslsock->ssl_handle); + sslsock->ssl_active = 0; + } + if (sslsock->ssl_handle) { + SSL_free(sslsock->ssl_handle); + sslsock->ssl_handle = NULL; + } + if (sslsock->ctx) { + SSL_CTX_free(sslsock->ctx); + sslsock->ctx = NULL; + } +#ifdef PHP_WIN32 + if (sslsock->s.socket == -1) + sslsock->s.socket = SOCK_ERR; +#endif + if (sslsock->s.socket != SOCK_ERR) { +#ifdef PHP_WIN32 + /* prevent more data from coming in */ + shutdown(sslsock->s.socket, SHUT_RD); + + /* try to make sure that the OS sends all data before we close the connection. + * Essentially, we are waiting for the socket to become writeable, which means + * that all pending data has been sent. + * We use a small timeout which should encourage the OS to send the data, + * but at the same time avoid hanging indefintely. + * */ + do { + n = php_pollfd_for_ms(sslsock->s.socket, POLLOUT, 500); + } while (n == -1 && php_socket_errno() == EINTR); +#endif + closesocket(sslsock->s.socket); + sslsock->s.socket = SOCK_ERR; + } + } + + if (sslsock->sni) { + pefree(sslsock->sni, php_stream_is_persistent(stream)); + } + pefree(sslsock, php_stream_is_persistent(stream)); + + return 0; +} + +static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC) +{ + return php_stream_socket_ops.flush(stream TSRMLS_CC); +} + +static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) +{ + return php_stream_socket_ops.stat(stream, ssb TSRMLS_CC); +} + + +static inline int php_openssl_setup_crypto(php_stream *stream, + php_openssl_netstream_data_t *sslsock, + php_stream_xport_crypto_param *cparam + TSRMLS_DC) +{ + SSL_METHOD *method; + long ssl_ctx_options = SSL_OP_ALL; + + if (sslsock->ssl_handle) { + if (sslsock->s.is_blocked) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream"); + return -1; + } else { + return 0; + } + } + + /* need to do slightly different things, based on client/server method, + * so lets remember which method was selected */ + + switch (cparam->inputs.method) { + case STREAM_CRYPTO_METHOD_SSLv23_CLIENT: + sslsock->is_client = 1; + method = SSLv23_client_method(); + break; + case STREAM_CRYPTO_METHOD_SSLv2_CLIENT: +#ifdef OPENSSL_NO_SSL2 + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); + return -1; +#else + sslsock->is_client = 1; + method = SSLv2_client_method(); + break; +#endif + case STREAM_CRYPTO_METHOD_SSLv3_CLIENT: + sslsock->is_client = 1; + method = SSLv3_client_method(); + break; + case STREAM_CRYPTO_METHOD_TLS_CLIENT: + sslsock->is_client = 1; + method = TLSv1_client_method(); + break; + case STREAM_CRYPTO_METHOD_SSLv23_SERVER: + sslsock->is_client = 0; + method = SSLv23_server_method(); + break; + case STREAM_CRYPTO_METHOD_SSLv3_SERVER: + sslsock->is_client = 0; + method = SSLv3_server_method(); + break; + case STREAM_CRYPTO_METHOD_SSLv2_SERVER: +#ifdef OPENSSL_NO_SSL2 + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); + return -1; +#else + sslsock->is_client = 0; + method = SSLv2_server_method(); + break; +#endif + case STREAM_CRYPTO_METHOD_TLS_SERVER: + sslsock->is_client = 0; + method = TLSv1_server_method(); + break; + default: + return -1; + + } + + sslsock->ctx = SSL_CTX_new(method); + if (sslsock->ctx == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context"); + return -1; + } + +#if OPENSSL_VERSION_NUMBER >= 0x0090605fL + ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; +#endif + SSL_CTX_set_options(sslsock->ctx, ssl_ctx_options); + +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL + { + zval **val; + + if (stream->context && SUCCESS == php_stream_context_get_option( + stream->context, "ssl", "no_ticket", &val) && + zval_is_true(*val)) { + SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_TICKET); + } + } +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + { + zval **val; + + if (stream->context && SUCCESS == php_stream_context_get_option( + stream->context, "ssl", "disable_compression", &val) && + zval_is_true(*val)) { + SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_COMPRESSION); + } + } +#endif + + sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC); + if (sslsock->ssl_handle == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle"); + SSL_CTX_free(sslsock->ctx); + sslsock->ctx = NULL; + return -1; + } + + if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) { + handle_ssl_error(stream, 0, 1 TSRMLS_CC); + } + + if (cparam->inputs.session) { + if (cparam->inputs.session->ops != &php_openssl_socket_ops) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream"); + } else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied SSL session stream is not initialized"); + } else { + SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle); + } + } + return 0; +} + +static inline int php_openssl_enable_crypto(php_stream *stream, + php_openssl_netstream_data_t *sslsock, + php_stream_xport_crypto_param *cparam + TSRMLS_DC) +{ + int n, retry = 1; + + if (cparam->inputs.activate && !sslsock->ssl_active) { + struct timeval start_time, + *timeout; + int blocked = sslsock->s.is_blocked, + has_timeout = 0; + +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) + if (sslsock->is_client && sslsock->sni) { + SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni); + } +#endif + + if (!sslsock->state_set) { + if (sslsock->is_client) { + SSL_set_connect_state(sslsock->ssl_handle); + } else { + SSL_set_accept_state(sslsock->ssl_handle); + } + sslsock->state_set = 1; + } + + if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) { + sslsock->s.is_blocked = 0; + } + + timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout; + has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec); + /* gettimeofday is not monotonic; using it here is not strictly correct */ + if (has_timeout) { + gettimeofday(&start_time, NULL); + } + + do { + struct timeval cur_time, + elapsed_time; + + if (sslsock->is_client) { + n = SSL_connect(sslsock->ssl_handle); + } else { + n = SSL_accept(sslsock->ssl_handle); + } + + if (has_timeout) { + gettimeofday(&cur_time, NULL); + elapsed_time.tv_sec = cur_time.tv_sec - start_time.tv_sec; + elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec; + if (cur_time.tv_usec < start_time.tv_usec) { + elapsed_time.tv_sec -= 1L; + elapsed_time.tv_usec += 1000000L; + } + + if (elapsed_time.tv_sec > timeout->tv_sec || + (elapsed_time.tv_sec == timeout->tv_sec && + elapsed_time.tv_usec > timeout->tv_usec)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: crypto enabling timeout"); + return -1; + } + } + + if (n <= 0) { + /* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */ + retry = handle_ssl_error(stream, n, blocked TSRMLS_CC); + if (retry) { + /* wait until something interesting happens in the socket. It may be a + * timeout. Also consider the unlikely of possibility of a write block */ + int err = SSL_get_error(sslsock->ssl_handle, n); + struct timeval left_time; + + if (has_timeout) { + left_time.tv_sec = timeout->tv_sec - elapsed_time.tv_sec; + left_time.tv_usec = timeout->tv_usec - elapsed_time.tv_usec; + if (timeout->tv_usec < elapsed_time.tv_usec) { + left_time.tv_sec -= 1L; + left_time.tv_usec += 1000000L; + } + } + php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ? + (POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL); + } + } else { + retry = 0; + } + } while (retry); + + if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) { + sslsock->s.is_blocked = blocked; + } + + if (n == 1) { + X509 *peer_cert; + + peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle); + + if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) { + SSL_shutdown(sslsock->ssl_handle); + n = -1; + } else { + sslsock->ssl_active = 1; + + /* allow the script to capture the peer cert + * and/or the certificate chain */ + if (stream->context) { + zval **val, *zcert; + + if (SUCCESS == php_stream_context_get_option( + stream->context, "ssl", + "capture_peer_cert", &val) && + zval_is_true(*val)) { + MAKE_STD_ZVAL(zcert); + ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, + php_openssl_get_x509_list_id() TSRMLS_CC)); + php_stream_context_set_option(stream->context, + "ssl", "peer_certificate", + zcert); + peer_cert = NULL; + FREE_ZVAL(zcert); + } + + if (SUCCESS == php_stream_context_get_option( + stream->context, "ssl", + "capture_peer_cert_chain", &val) && + zval_is_true(*val)) { + zval *arr; + STACK_OF(X509) *chain; + + MAKE_STD_ZVAL(arr); + chain = SSL_get_peer_cert_chain( + sslsock->ssl_handle); + + if (chain && sk_X509_num(chain) > 0) { + int i; + array_init(arr); + + for (i = 0; i < sk_X509_num(chain); i++) { + X509 *mycert = X509_dup( + sk_X509_value(chain, i)); + MAKE_STD_ZVAL(zcert); + ZVAL_RESOURCE(zcert, + zend_list_insert(mycert, + php_openssl_get_x509_list_id() TSRMLS_CC)); + add_next_index_zval(arr, zcert); + } + + } else { + ZVAL_NULL(arr); + } + + php_stream_context_set_option(stream->context, + "ssl", "peer_certificate_chain", + arr); + zval_dtor(arr); + efree(arr); + } + } + } + + if (peer_cert) { + X509_free(peer_cert); + } + } else { + n = errno == EAGAIN ? 0 : -1; + } + + return n; + + } else if (!cparam->inputs.activate && sslsock->ssl_active) { + /* deactivate - common for server/client */ + SSL_shutdown(sslsock->ssl_handle); + sslsock->ssl_active = 0; + } + return -1; +} + +static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock, + php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC) +{ + int clisock; + + xparam->outputs.client = NULL; + + clisock = php_network_accept_incoming(sock->s.socket, + xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, + xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL, + xparam->want_addr ? &xparam->outputs.addr : NULL, + xparam->want_addr ? &xparam->outputs.addrlen : NULL, + xparam->inputs.timeout, + xparam->want_errortext ? &xparam->outputs.error_text : NULL, + &xparam->outputs.error_code + TSRMLS_CC); + + if (clisock >= 0) { + php_openssl_netstream_data_t *clisockdata; + + clisockdata = emalloc(sizeof(*clisockdata)); + + if (clisockdata == NULL) { + closesocket(clisock); + /* technically a fatal error */ + } else { + /* copy underlying tcp fields */ + memset(clisockdata, 0, sizeof(*clisockdata)); + memcpy(clisockdata, sock, sizeof(clisockdata->s)); + + clisockdata->s.socket = clisock; + + xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+"); + if (xparam->outputs.client) { + xparam->outputs.client->context = stream->context; + if (stream->context) { + zend_list_addref(stream->context->rsrc_id); + } + } + } + + if (xparam->outputs.client && sock->enable_on_connect) { + /* apply crypto */ + switch (sock->method) { + case STREAM_CRYPTO_METHOD_SSLv23_CLIENT: + sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER; + break; + case STREAM_CRYPTO_METHOD_SSLv2_CLIENT: + sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER; + break; + case STREAM_CRYPTO_METHOD_SSLv3_CLIENT: + sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER; + break; + case STREAM_CRYPTO_METHOD_TLS_CLIENT: + sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER; + break; + default: + break; + } + + clisockdata->method = sock->method; + + if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method, + NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable( + xparam->outputs.client, 1 TSRMLS_CC) < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto"); + + php_stream_close(xparam->outputs.client); + xparam->outputs.client = NULL; + xparam->outputs.returncode = -1; + } + } + } + + return xparam->outputs.client == NULL ? -1 : 0; +} +static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) +{ + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; + php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam; + php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam; + + switch (option) { + case PHP_STREAM_OPTION_CHECK_LIVENESS: + { + struct timeval tv; + char buf; + int alive = 1; + + if (value == -1) { + if (sslsock->s.timeout.tv_sec == -1) { + tv.tv_sec = FG(default_socket_timeout); + tv.tv_usec = 0; + } else { + tv = sslsock->connect_timeout; + } + } else { + tv.tv_sec = value; + tv.tv_usec = 0; + } + + if (sslsock->s.socket == -1) { + alive = 0; + } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) { + if (sslsock->ssl_active) { + int n; + + do { + n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf)); + if (n <= 0) { + int err = SSL_get_error(sslsock->ssl_handle, n); + + if (err == SSL_ERROR_SYSCALL) { + alive = php_socket_errno() == EAGAIN; + break; + } + + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + /* re-negotiate */ + continue; + } + + /* any other problem is a fatal error */ + alive = 0; + } + /* either peek succeeded or there was an error; we + * have set the alive flag appropriately */ + break; + } while (1); + } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) { + alive = 0; + } + } + return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + } + + case PHP_STREAM_OPTION_CRYPTO_API: + + switch(cparam->op) { + + case STREAM_XPORT_CRYPTO_OP_SETUP: + cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + break; + case STREAM_XPORT_CRYPTO_OP_ENABLE: + cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + break; + default: + /* fall through */ + break; + } + + break; + + case PHP_STREAM_OPTION_XPORT_API: + switch(xparam->op) { + + case STREAM_XPORT_OP_CONNECT: + case STREAM_XPORT_OP_CONNECT_ASYNC: + /* TODO: Async connects need to check the enable_on_connect option when + * we notice that the connect has actually been established */ + php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC); + + if ((sslsock->enable_on_connect) && + ((xparam->outputs.returncode == 0) || + (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && + xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS))) + { + if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 || + php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto"); + xparam->outputs.returncode = -1; + } + } + return PHP_STREAM_OPTION_RETURN_OK; + + case STREAM_XPORT_OP_ACCEPT: + /* we need to copy the additional fields that the underlying tcp transport + * doesn't know about */ + xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC); + + + return PHP_STREAM_OPTION_RETURN_OK; + + default: + /* fall through */ + break; + } + } + + return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC); +} + +static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) +{ + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; + + switch(castas) { + case PHP_STREAM_AS_STDIO: + if (sslsock->ssl_active) { + return FAILURE; + } + if (ret) { + *ret = fdopen(sslsock->s.socket, stream->mode); + if (*ret) { + return SUCCESS; + } + return FAILURE; + } + return SUCCESS; + + case PHP_STREAM_AS_FD_FOR_SELECT: + if (ret) { + *(int *)ret = sslsock->s.socket; + } + return SUCCESS; + + case PHP_STREAM_AS_FD: + case PHP_STREAM_AS_SOCKETD: + if (sslsock->ssl_active) { + return FAILURE; + } + if (ret) { + *(int *)ret = sslsock->s.socket; + } + return SUCCESS; + default: + return FAILURE; + } +} + +php_stream_ops php_openssl_socket_ops = { + php_openssl_sockop_write, php_openssl_sockop_read, + php_openssl_sockop_close, php_openssl_sockop_flush, + "tcp_socket/ssl", + NULL, /* seek */ + php_openssl_sockop_cast, + php_openssl_sockop_stat, + php_openssl_sockop_set_option, +}; + +static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) { + + php_url *url; + + if (ctx) { + zval **val = NULL; + + if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) { + return NULL; + } + if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) { + convert_to_string_ex(val); + return pestrdup(Z_STRVAL_PP(val), is_persistent); + } + } + + if (!resourcename) { + return NULL; + } + + url = php_url_parse_ex(resourcename, resourcenamelen); + if (!url) { + return NULL; + } + + if (url->host) { + const char * host = url->host; + char * sni = NULL; + size_t len = strlen(host); + + /* skip trailing dots */ + while (len && host[len-1] == '.') { + --len; + } + + if (len) { + sni = pestrndup(host, len, is_persistent); + } + + php_url_free(url); + return sni; + } + + php_url_free(url); + return NULL; +} + +php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen, + char *resourcename, long resourcenamelen, + const char *persistent_id, int options, int flags, + struct timeval *timeout, + php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + php_stream *stream = NULL; + php_openssl_netstream_data_t *sslsock = NULL; + + sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0); + memset(sslsock, 0, sizeof(*sslsock)); + + sslsock->s.is_blocked = 1; + /* this timeout is used by standard stream funcs, therefor it should use the default value */ + sslsock->s.timeout.tv_sec = FG(default_socket_timeout); + sslsock->s.timeout.tv_usec = 0; + + /* use separate timeout for our private funcs */ + sslsock->connect_timeout.tv_sec = timeout->tv_sec; + sslsock->connect_timeout.tv_usec = timeout->tv_usec; + + /* we don't know the socket until we have determined if we are binding or + * connecting */ + sslsock->s.socket = -1; + + /* Initialize context as NULL */ + sslsock->ctx = NULL; + + stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+"); + + if (stream == NULL) { + pefree(sslsock, persistent_id ? 1 : 0); + return NULL; + } + + sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC); + + if (strncmp(proto, "ssl", protolen) == 0) { + sslsock->enable_on_connect = 1; + sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT; + } else if (strncmp(proto, "sslv2", protolen) == 0) { +#ifdef OPENSSL_NO_SSL2 + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); + return NULL; +#else + sslsock->enable_on_connect = 1; + sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT; +#endif + } else if (strncmp(proto, "sslv3", protolen) == 0) { + sslsock->enable_on_connect = 1; + sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT; + } else if (strncmp(proto, "tls", protolen) == 0) { + sslsock->enable_on_connect = 1; + sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + } + + return stream; +} + + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ |