/ext/openssl/openssl.c
http://github.com/infusion/PHP · C · 4921 lines · 3809 code · 704 blank · 408 comment · 1032 complexity · def9cf9a3ae9c4308d994129835e2aaa MD5 · raw file
Large files are truncated click here to view the full file
- /*
- +----------------------------------------------------------------------+
- | PHP Version 5 |
- +----------------------------------------------------------------------+
- | Copyright (c) 1997-2011 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: openssl.c 308534 2011-02-21 12:47:38Z pajoye $ */
- #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"
- /* 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
- #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_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, raw_output)
- 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, raw_input)
- 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)
- {NULL, NULL, NULL}
- };
- /* }}} */
- /* {{{ 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 safe_mode & open_basedir checks */
- inline static int php_openssl_safe_mode_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;
- };
- /* }}} */
- 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 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_safe_mode_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;
- }
- }
-
- /* 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) /* {{{ */
- {
- char buffer[MAXPATHLEN];
- TSRMLS_FETCH();
- *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;
- 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
- 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();
- ERR_load_ERR_strings();
- ERR_load_crypto_strings();
- ERR_load_EVP_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);
- /* 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
- /* 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
- #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);
- php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory TSRMLS_CC);
- 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);
- php_stream_xport_unregister("sslv2" TSRMLS_CC);
- 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_safe_mode_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);
- }
- 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, "Zs|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_safe_mode_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_safe_mode_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…