mirror of
https://github.com/php/php-src.git
synced 2024-10-06 17:26:11 +00:00
601 lines
14 KiB
C
601 lines
14 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP version 4.0 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 2.02 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available at through the world-wide-web at |
|
|
| http://www.php.net/license/2_02.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> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
#include "php.h"
|
|
#include "php_openssl.h"
|
|
|
|
/* PHP Includes */
|
|
#include "ext/standard/file.h"
|
|
#include "ext/standard/info.h"
|
|
|
|
/* OpenSSL includes */
|
|
#include <openssl/evp.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/pem.h>
|
|
|
|
static unsigned char arg2of3_force_ref[] =
|
|
{ 3, BYREF_NONE, BYREF_FORCE, BYREF_NONE };
|
|
static unsigned char arg2of4_force_ref[] =
|
|
{ 4, BYREF_NONE, BYREF_FORCE, BYREF_NONE, BYREF_NONE };
|
|
static unsigned char arg2and3of4_force_ref[] =
|
|
{ 4, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_NONE };
|
|
|
|
|
|
function_entry openssl_functions[] = {
|
|
PHP_FE(openssl_get_privatekey, NULL)
|
|
PHP_FE(openssl_get_publickey, NULL)
|
|
PHP_FE(openssl_free_key, NULL)
|
|
#if 0
|
|
PHP_FE(openssl_read_publickey, NULL)
|
|
PHP_FE(openssl_read_x509, NULL)
|
|
PHP_FE(openssl_free_x509, NULL)
|
|
#endif
|
|
PHP_FE(openssl_sign, arg2of3_force_ref)
|
|
PHP_FE(openssl_verify, NULL)
|
|
PHP_FE(openssl_seal, arg2and3of4_force_ref)
|
|
PHP_FE(openssl_open, arg2of4_force_ref)
|
|
{NULL, NULL, NULL}
|
|
};
|
|
|
|
zend_module_entry openssl_module_entry = {
|
|
"openssl",
|
|
openssl_functions,
|
|
PHP_MINIT(openssl),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
PHP_MINFO(openssl),
|
|
STANDARD_MODULE_PROPERTIES
|
|
};
|
|
|
|
#ifdef COMPILE_DL_OPENSSL
|
|
ZEND_GET_MODULE(openssl)
|
|
#endif
|
|
|
|
static void _php_pkey_free(zend_rsrc_list_entry *rsrc);
|
|
static int le_key;
|
|
|
|
#if 0
|
|
static void _php_x509_free(zend_rsrc_list_entry *rsrc);
|
|
static int le_x509;
|
|
#endif
|
|
|
|
PHP_MINIT_FUNCTION(openssl)
|
|
{
|
|
le_key = zend_register_list_destructors_ex(_php_pkey_free, NULL,
|
|
"OpenSSL key",
|
|
module_number);
|
|
#if 0
|
|
le_x509 = zend_register_list_destructors_ex(_php_x509_free, NULL,
|
|
"OpenSSL X.509",
|
|
module_number);
|
|
#endif
|
|
OpenSSL_add_all_ciphers();
|
|
return SUCCESS;
|
|
}
|
|
|
|
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 Version", OPENSSL_VERSION_TEXT);
|
|
php_info_print_table_end();
|
|
}
|
|
|
|
PHP_MSHUTDOWN_FUNCTION(openssl)
|
|
{
|
|
EVP_cleanup();
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* {{{ proto int openssl_get_privatekey(string key [, string passphrase])
|
|
Get private key */
|
|
PHP_FUNCTION(openssl_get_privatekey)
|
|
{
|
|
zval **key, **passphrase;
|
|
BIO *b;
|
|
EVP_PKEY *pkey;
|
|
int argc;
|
|
|
|
argc = ZEND_NUM_ARGS();
|
|
if (argc < 1 || argc > 2 ||
|
|
zend_get_parameters_ex(argc, &key, &passphrase) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
convert_to_string_ex(key);
|
|
if (argc == 2) {
|
|
convert_to_string_ex(passphrase);
|
|
}
|
|
|
|
b = BIO_new_mem_buf(Z_STRVAL_PP(key), Z_STRLEN_PP(key));
|
|
if (b == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
pkey = (EVP_PKEY *) PEM_ASN1_read_bio((char *(*)())d2i_PrivateKey,
|
|
PEM_STRING_EVP_PKEY, b,
|
|
NULL, NULL, argc == 2 ?
|
|
Z_STRVAL_PP(passphrase) : NULL);
|
|
BIO_free(b);
|
|
|
|
if (pkey == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
ZEND_REGISTER_RESOURCE(return_value, pkey, le_key);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int openssl_get_publickey(string cert)
|
|
Get public key from X.509 certificate */
|
|
PHP_FUNCTION(openssl_get_publickey)
|
|
{
|
|
zval **cert;
|
|
X509 *x509;
|
|
BIO *b;
|
|
EVP_PKEY *pkey;
|
|
|
|
if (ZEND_NUM_ARGS() != 1 ||
|
|
zend_get_parameters_ex(1, &cert) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
convert_to_string_ex(cert);
|
|
|
|
b = BIO_new_mem_buf(Z_STRVAL_PP(cert), -1);
|
|
if (b == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
x509 = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509,
|
|
PEM_STRING_X509, b,
|
|
NULL, NULL, NULL);
|
|
BIO_free(b);
|
|
|
|
if (x509 == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
pkey = (EVP_PKEY *) X509_get_pubkey(x509);
|
|
X509_free(x509);
|
|
|
|
if (pkey == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
ZEND_REGISTER_RESOURCE(return_value, pkey, le_key);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void openssl_free_key(int key)
|
|
Free key */
|
|
PHP_FUNCTION(openssl_free_key)
|
|
{
|
|
zval **key;
|
|
EVP_PKEY *pkey;
|
|
|
|
if (ZEND_NUM_ARGS() != 1 ||
|
|
zend_get_parameters_ex(1, &key) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
|
|
ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, key, -1, "OpenSSL key", le_key);
|
|
zend_list_delete(Z_LVAL_PP(key));
|
|
}
|
|
/* }}} */
|
|
|
|
#if 0
|
|
/* {{{ proto int openssl_read_publickey(int x509)
|
|
Read public key */
|
|
PHP_FUNCTION(openssl_read_publickey)
|
|
{
|
|
zval **x509;
|
|
X509 *cert;
|
|
EVP_PKEY *pkey;
|
|
|
|
if (ZEND_NUM_ARGS() != 1 ||
|
|
zend_get_parameters_ex(1, &x509) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
ZEND_FETCH_RESOURCE(cert, X509 *, x509, -1, "OpenSSL X.509", le_x509);
|
|
|
|
pkey = (EVP_PKEY *) X509_get_pubkey(cert);
|
|
if (pkey == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
ZEND_REGISTER_RESOURCE(return_value, pkey, le_key);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int openssl_read_x509(string cert)
|
|
Read X.509 certificate */
|
|
PHP_FUNCTION(openssl_read_x509)
|
|
{
|
|
zval **cert;
|
|
X509 *x509;
|
|
BIO *b;
|
|
|
|
if (ZEND_NUM_ARGS() != 1 ||
|
|
zend_get_parameters_ex(1, &cert) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
convert_to_string_ex(cert);
|
|
|
|
b = BIO_new_mem_buf(Z_STRVAL_PP(cert), -1);
|
|
if (b == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
x509 = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509,
|
|
PEM_STRING_X509, b,
|
|
NULL, NULL, NULL);
|
|
BIO_free(b);
|
|
|
|
if (x509 == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
ZEND_REGISTER_RESOURCE(return_value, x509, le_x509);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void openssl_free_x509(int x509)
|
|
Free X.509 certificate */
|
|
PHP_FUNCTION(openssl_free_x509)
|
|
{
|
|
zval **x509;
|
|
X509 *cert;
|
|
|
|
if (ZEND_NUM_ARGS() != 1 ||
|
|
zend_get_parameters_ex(1, &x509) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
ZEND_FETCH_RESOURCE(cert, X509 *, x509, -1, "OpenSSL X.509", le_x509);
|
|
zend_list_delete(Z_LVAL_PP(x509));
|
|
}
|
|
/* }}} */
|
|
#endif
|
|
|
|
/* {{{ proto bool openssl_sign(string data, string signature, int key)
|
|
Sign data */
|
|
PHP_FUNCTION(openssl_sign)
|
|
{
|
|
zval **key, **data, **signature;
|
|
EVP_PKEY *pkey;
|
|
int siglen;
|
|
unsigned char *sigbuf;
|
|
EVP_MD_CTX md_ctx;
|
|
|
|
if (ZEND_NUM_ARGS() != 3 ||
|
|
zend_get_parameters_ex(3, &data, &signature, &key) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
convert_to_string_ex(data);
|
|
|
|
ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, key, -1, "OpenSSL key", le_key);
|
|
siglen = EVP_PKEY_size(pkey);
|
|
sigbuf = emalloc(siglen + 1);
|
|
if (sigbuf == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
EVP_SignInit(&md_ctx, EVP_sha1());
|
|
EVP_SignUpdate(&md_ctx, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
|
|
if (EVP_SignFinal (&md_ctx, sigbuf, &siglen, pkey)) {
|
|
zval_dtor(*signature);
|
|
sigbuf[siglen] = '\0';
|
|
ZVAL_STRINGL(*signature, sigbuf, siglen, 0);
|
|
RETURN_TRUE;
|
|
} else {
|
|
RETURN_FALSE;
|
|
efree(sigbuf);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int openssl_verify(string data, string signature, int key)
|
|
Verify data */
|
|
PHP_FUNCTION(openssl_verify)
|
|
{
|
|
zval **key, **data, **signature;
|
|
EVP_PKEY *pkey;
|
|
int err;
|
|
EVP_MD_CTX md_ctx;
|
|
|
|
if (ZEND_NUM_ARGS() != 3 ||
|
|
zend_get_parameters_ex(3, &data, &signature, &key) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
convert_to_string_ex(data);
|
|
convert_to_string_ex(signature);
|
|
|
|
ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, key, -1, "OpenSSL key", le_key);
|
|
|
|
EVP_VerifyInit (&md_ctx, EVP_sha1());
|
|
EVP_VerifyUpdate (&md_ctx, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
|
|
err = EVP_VerifyFinal (&md_ctx, Z_STRVAL_PP(signature),
|
|
Z_STRLEN_PP(signature), pkey);
|
|
RETURN_LONG(err);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int openssl_seal(string data, string sealdata, array ekeys,
|
|
array pubkeys)
|
|
Seal data */
|
|
PHP_FUNCTION(openssl_seal)
|
|
{
|
|
zval **pubkeys, **pubkey, **data, **sealdata, **ekeys;
|
|
HashTable *pubkeysht;
|
|
HashPosition pos;
|
|
EVP_PKEY **pkeys;
|
|
int i, len1, len2, *eksl, nkeys;
|
|
unsigned char *buf, **eks;
|
|
|
|
EVP_CIPHER_CTX ctx;
|
|
|
|
if (ZEND_NUM_ARGS() != 4 ||
|
|
zend_get_parameters_ex(4, &data, &sealdata, &ekeys,
|
|
&pubkeys) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
|
|
SEPARATE_ZVAL(pubkeys);
|
|
pubkeysht = HASH_OF(*pubkeys);
|
|
nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
|
|
if (!nkeys) {
|
|
php_error(E_WARNING,
|
|
"Fifth argument to openssl_seal() must be a non-empty array");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
pkeys = emalloc(nkeys * sizeof(*pkeys));
|
|
if (pkeys == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
eksl = emalloc(nkeys * sizeof(*eksl));
|
|
if (eksl == NULL) {
|
|
efree(pkeys);
|
|
RETURN_FALSE;
|
|
}
|
|
eks = emalloc(nkeys * sizeof(*eks));
|
|
if (eks == NULL) {
|
|
efree(eksl);
|
|
efree(pkeys);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
convert_to_string_ex(data);
|
|
|
|
zend_hash_internal_pointer_reset_ex(pubkeysht, &pos);
|
|
i = 0;
|
|
while (zend_hash_get_current_data_ex(pubkeysht, (void **) &pubkey,
|
|
&pos) == SUCCESS) {
|
|
ZEND_FETCH_RESOURCE(pkeys[i], EVP_PKEY *, pubkey, -1,
|
|
"OpenSSL key", le_key);
|
|
eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
|
|
if (eks[i] == NULL) {
|
|
while (i--) {
|
|
efree(eks[i]);
|
|
}
|
|
efree(eks);
|
|
efree(eksl);
|
|
efree(pkeys);
|
|
RETURN_FALSE;
|
|
}
|
|
zend_hash_move_forward_ex(pubkeysht, &pos);
|
|
i++;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x0090600fL
|
|
if (!EVP_EncryptInit(&ctx,EVP_rc4(),NULL,NULL)) {
|
|
for (i=0; i<nkeys; i++) {
|
|
efree(eks[i]);
|
|
}
|
|
efree(eks);
|
|
efree(eksl);
|
|
efree(pkeys);
|
|
RETURN_FALSE;
|
|
}
|
|
#else
|
|
EVP_EncryptInit(&ctx,EVP_rc4(),NULL,NULL);
|
|
#endif
|
|
|
|
#if 0
|
|
/* Need this if allow ciphers that require initialization vector */
|
|
ivlen = EVP_CIPHER_CTX_iv_length(&ctx);
|
|
if (ivlen) {
|
|
iv = emalloc(ivlen + 1);
|
|
if (iv == NULL) {
|
|
for (i=0; i<nkeys; i++) {
|
|
efree(eks[i]);
|
|
}
|
|
efree(eks);
|
|
efree(eksl);
|
|
efree(pkeys);
|
|
RETURN_FALSE;
|
|
}
|
|
} else {
|
|
iv = NULL;
|
|
}
|
|
#endif
|
|
/* allocate one byte extra to make room for \0 */
|
|
buf = emalloc(Z_STRLEN_PP(data) + EVP_CIPHER_CTX_block_size(&ctx));
|
|
if (buf == NULL) {
|
|
for (i=0; i<nkeys; i++) {
|
|
efree(eks[i]);
|
|
}
|
|
efree(eks);
|
|
efree(eksl);
|
|
efree(pkeys);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (!EVP_SealInit(&ctx, EVP_rc4(), eks, eksl, NULL, pkeys, nkeys)
|
|
#if OPENSSL_VERSION_NUMBER >= 0x0090600fL
|
|
|| !EVP_SealUpdate(&ctx, buf, &len1, Z_STRVAL_PP(data),
|
|
Z_STRLEN_PP(data))
|
|
#endif
|
|
) {
|
|
efree(buf);
|
|
for (i=0; i<nkeys; i++) {
|
|
efree(eks[i]);
|
|
}
|
|
efree(eks);
|
|
efree(eksl);
|
|
efree(pkeys);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x0090600fL
|
|
EVP_SealUpdate(&ctx, buf, &len1, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
|
|
#endif
|
|
EVP_SealFinal(&ctx, buf + len1, &len2);
|
|
|
|
efree(pkeys);
|
|
|
|
if (len1 + len2 > 0) {
|
|
zval_dtor(*sealdata);
|
|
buf[len1 + len2] = '\0';
|
|
ZVAL_STRINGL(*sealdata, erealloc(buf, len1 + len2 + 1),
|
|
len1 + len2, 0);
|
|
|
|
zval_dtor(*ekeys);
|
|
if (array_init(*ekeys) == FAILURE) {
|
|
php_error(E_ERROR, "Cannot initialize return value");
|
|
for (i=0; i<nkeys; i++) {
|
|
efree(eks[i]);
|
|
}
|
|
efree(eks);
|
|
efree(eksl);
|
|
RETURN_FALSE;
|
|
}
|
|
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);
|
|
}
|
|
efree(eks);
|
|
efree(eksl);
|
|
|
|
#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);
|
|
for (i=0; i<nkeys; i++) {
|
|
efree(eks[i]);
|
|
}
|
|
efree(eks);
|
|
efree(eksl);
|
|
}
|
|
|
|
RETURN_LONG(len1 + len2);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool openssl_open(string data, string opendata, string ekey,
|
|
int privkey)
|
|
Open data */
|
|
PHP_FUNCTION(openssl_open)
|
|
{
|
|
zval **privkey, **data, **opendata, **ekey;
|
|
EVP_PKEY *pkey;
|
|
int len1, len2, ekl;
|
|
unsigned char *buf, *ek;
|
|
|
|
EVP_CIPHER_CTX ctx;
|
|
|
|
if (ZEND_NUM_ARGS() != 4 ||
|
|
zend_get_parameters_ex(4, &data, &opendata, &ekey,
|
|
&privkey) == FAILURE) {
|
|
WRONG_PARAM_COUNT;
|
|
}
|
|
convert_to_string_ex(data);
|
|
convert_to_string_ex(ekey);
|
|
|
|
ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, privkey, -1, "OpenSSL key",
|
|
le_key);
|
|
|
|
buf = emalloc(Z_STRLEN_PP(data) + 1);
|
|
if (buf == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (EVP_OpenInit(&ctx, EVP_rc4(), Z_STRVAL_PP(ekey),
|
|
Z_STRLEN_PP(ekey), NULL, pkey)
|
|
#if OPENSSL_VERSION_NUMBER >= 0x0090600fL
|
|
&& EVP_OpenUpdate(&ctx, buf, &len1, Z_STRVAL_PP(data),
|
|
Z_STRLEN_PP(data))
|
|
#endif
|
|
) {
|
|
#if OPENSSL_VERSION_NUMBER < 0x0090600fL
|
|
EVP_OpenUpdate(&ctx, buf, &len1, Z_STRVAL_PP(data),
|
|
Z_STRLEN_PP(data));
|
|
#endif
|
|
if (!EVP_OpenFinal(&ctx, buf + len1, &len2) ||
|
|
(len1 + len2 == 0)) {
|
|
efree(buf);
|
|
RETURN_FALSE;
|
|
}
|
|
} else {
|
|
efree(buf);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
zval_dtor(*opendata);
|
|
buf[len1 + len2] = '\0';
|
|
ZVAL_STRINGL(*opendata, erealloc(buf, len1 + len2 + 1), len1 + len2, 0);
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ _php_pkey_free() */
|
|
static void _php_pkey_free(zend_rsrc_list_entry *rsrc)
|
|
{
|
|
EVP_PKEY *pkey = (EVP_PKEY *)rsrc->ptr;
|
|
EVP_PKEY_free(pkey);
|
|
}
|
|
/* }}} */
|
|
|
|
#if 0
|
|
/* {{{ _php_x509_free() */
|
|
static void _php_x509_free(zend_rsrc_list_entry *rsrc)
|
|
{
|
|
X509 *x509 = (X509 *)rsrc->ptr;
|
|
X509_free(x509);
|
|
}
|
|
/* }}} */
|
|
#endif
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 8
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|