Back to index

php5  5.3.10
openssl.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Stig Venaas <venaas@php.net>                                |
00016    |          Wez Furlong <wez@thebrainroom.com>                          |
00017    |          Sascha Kettler <kettler@gmx.net>                            |
00018    |          Pierre-Alain Joye <pierre@php.net>                          |
00019    |          Marc Delling <delling@silpion.de> (PKCS12 functions)        |         
00020    +----------------------------------------------------------------------+
00021  */
00022 
00023 /* $Id: openssl.c 321634 2012-01-01 13:15:04Z felipe $ */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include "config.h"
00027 #endif
00028 
00029 #include "php.h"
00030 #include "php_openssl.h"
00031 
00032 /* PHP Includes */
00033 #include "ext/standard/file.h"
00034 #include "ext/standard/info.h"
00035 #include "ext/standard/php_fopen_wrappers.h"
00036 #include "ext/standard/md5.h"
00037 #include "ext/standard/base64.h"
00038 
00039 /* OpenSSL includes */
00040 #include <openssl/evp.h>
00041 #include <openssl/x509.h>
00042 #include <openssl/x509v3.h>
00043 #include <openssl/crypto.h>
00044 #include <openssl/pem.h>
00045 #include <openssl/err.h>
00046 #include <openssl/conf.h>
00047 #include <openssl/rand.h>
00048 #include <openssl/ssl.h>
00049 #include <openssl/pkcs12.h>
00050 
00051 /* Common */
00052 #include <time.h>
00053 
00054 #ifdef NETWARE
00055 #define timezone _timezone  /* timezone is called _timezone in LibC */
00056 #endif
00057 
00058 #define DEFAULT_KEY_LENGTH  512
00059 #define MIN_KEY_LENGTH             384
00060 
00061 #define OPENSSL_ALGO_SHA1   1
00062 #define OPENSSL_ALGO_MD5    2
00063 #define OPENSSL_ALGO_MD4    3
00064 #ifdef HAVE_OPENSSL_MD2_H
00065 #define OPENSSL_ALGO_MD2    4
00066 #endif
00067 #define OPENSSL_ALGO_DSS1   5
00068 
00069 #define DEBUG_SMIME  0
00070 
00071 /* FIXME: Use the openssl constants instead of
00072  * enum. It is now impossible to match real values
00073  * against php constants. Also sorry to break the
00074  * enum principles here, BC...
00075  */
00076 enum php_openssl_key_type {
00077        OPENSSL_KEYTYPE_RSA,
00078        OPENSSL_KEYTYPE_DSA,
00079        OPENSSL_KEYTYPE_DH,
00080        OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA,
00081 #ifdef EVP_PKEY_EC
00082        OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1
00083 #endif
00084 };
00085 
00086 enum php_openssl_cipher_type {
00087        PHP_OPENSSL_CIPHER_RC2_40,
00088        PHP_OPENSSL_CIPHER_RC2_128,
00089        PHP_OPENSSL_CIPHER_RC2_64,
00090        PHP_OPENSSL_CIPHER_DES,
00091        PHP_OPENSSL_CIPHER_3DES,
00092 
00093        PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_RC2_40
00094 };
00095 
00096 PHP_FUNCTION(openssl_get_md_methods);
00097 PHP_FUNCTION(openssl_get_cipher_methods);
00098 
00099 PHP_FUNCTION(openssl_digest);
00100 PHP_FUNCTION(openssl_encrypt);
00101 PHP_FUNCTION(openssl_decrypt);
00102 PHP_FUNCTION(openssl_cipher_iv_length);
00103 
00104 PHP_FUNCTION(openssl_dh_compute_key);
00105 PHP_FUNCTION(openssl_random_pseudo_bytes);
00106 
00107 /* {{{ arginfo */
00108 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 0, 2)
00109     ZEND_ARG_INFO(0, x509)
00110     ZEND_ARG_INFO(0, outfilename)
00111     ZEND_ARG_INFO(0, notext)
00112 ZEND_END_ARG_INFO()
00113 
00114 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export, 0, 0, 2)
00115     ZEND_ARG_INFO(0, x509)
00116     ZEND_ARG_INFO(1, out)
00117     ZEND_ARG_INFO(0, notext)
00118 ZEND_END_ARG_INFO()
00119 
00120 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_check_private_key, 0)
00121     ZEND_ARG_INFO(0, cert)
00122     ZEND_ARG_INFO(0, key)
00123 ZEND_END_ARG_INFO()
00124 
00125 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_parse, 0)
00126     ZEND_ARG_INFO(0, x509)
00127     ZEND_ARG_INFO(0, shortname)
00128 ZEND_END_ARG_INFO()
00129 
00130 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_checkpurpose, 0, 0, 3)
00131     ZEND_ARG_INFO(0, x509cert)
00132     ZEND_ARG_INFO(0, purpose)
00133     ZEND_ARG_INFO(0, cainfo) /* array */
00134     ZEND_ARG_INFO(0, untrustedfile)
00135 ZEND_END_ARG_INFO()
00136 
00137 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_read, 0)
00138     ZEND_ARG_INFO(0, cert)
00139 ZEND_END_ARG_INFO()
00140 
00141 ZEND_BEGIN_ARG_INFO(arginfo_openssl_x509_free, 0)
00142     ZEND_ARG_INFO(0, x509)
00143 ZEND_END_ARG_INFO()
00144 
00145 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs12_export_to_file, 0, 0, 4)
00146     ZEND_ARG_INFO(0, x509)
00147     ZEND_ARG_INFO(0, filename)
00148     ZEND_ARG_INFO(0, priv_key)
00149     ZEND_ARG_INFO(0, pass)
00150     ZEND_ARG_INFO(0, args) /* array */
00151 ZEND_END_ARG_INFO()
00152 
00153 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_export, 0)
00154     ZEND_ARG_INFO(0, x509)
00155     ZEND_ARG_INFO(1, out)
00156     ZEND_ARG_INFO(0, priv_key)
00157     ZEND_ARG_INFO(0, pass)
00158     ZEND_ARG_INFO(0, args) /* array */
00159 ZEND_END_ARG_INFO()
00160 
00161 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkcs12_read, 0)
00162     ZEND_ARG_INFO(0, PKCS12)
00163     ZEND_ARG_INFO(1, certs) /* array */
00164     ZEND_ARG_INFO(0, pass)
00165 ZEND_END_ARG_INFO()
00166 
00167 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export_to_file, 0, 0, 2)
00168     ZEND_ARG_INFO(0, csr)
00169     ZEND_ARG_INFO(0, outfilename)
00170     ZEND_ARG_INFO(0, notext)
00171 ZEND_END_ARG_INFO()
00172 
00173 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_export, 0, 0, 2)
00174     ZEND_ARG_INFO(0, csr)
00175     ZEND_ARG_INFO(1, out)
00176     ZEND_ARG_INFO(0, notext)
00177 ZEND_END_ARG_INFO()
00178 
00179 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_sign, 0, 0, 4)
00180     ZEND_ARG_INFO(0, csr)
00181     ZEND_ARG_INFO(0, x509)
00182     ZEND_ARG_INFO(0, priv_key)
00183     ZEND_ARG_INFO(0, days)
00184     ZEND_ARG_INFO(0, config_args) /* array */
00185     ZEND_ARG_INFO(0, serial)
00186 ZEND_END_ARG_INFO()
00187 
00188 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_csr_new, 0, 0, 2)
00189     ZEND_ARG_INFO(0, dn) /* array */
00190     ZEND_ARG_INFO(1, privkey)
00191     ZEND_ARG_INFO(0, configargs)
00192     ZEND_ARG_INFO(0, extraattribs)
00193 ZEND_END_ARG_INFO()
00194 
00195 ZEND_BEGIN_ARG_INFO(arginfo_openssl_csr_get_subject, 0)
00196     ZEND_ARG_INFO(0, csr)
00197 ZEND_END_ARG_INFO()
00198 
00199 ZEND_BEGIN_ARG_INFO(arginfo_openssl_csr_get_public_key, 0)
00200     ZEND_ARG_INFO(0, csr)
00201 ZEND_END_ARG_INFO()
00202 
00203 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_new, 0, 0, 0)
00204     ZEND_ARG_INFO(0, configargs) /* array */
00205 ZEND_END_ARG_INFO()
00206 
00207 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export_to_file, 0, 0, 2)
00208     ZEND_ARG_INFO(0, key)
00209     ZEND_ARG_INFO(0, outfilename)
00210     ZEND_ARG_INFO(0, passphrase)
00211     ZEND_ARG_INFO(0, config_args) /* array */
00212 ZEND_END_ARG_INFO()
00213 
00214 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_export, 0, 0, 2)
00215     ZEND_ARG_INFO(0, key)
00216     ZEND_ARG_INFO(1, out)
00217     ZEND_ARG_INFO(0, passphrase)
00218     ZEND_ARG_INFO(0, config_args) /* array */
00219 ZEND_END_ARG_INFO()
00220 
00221 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_public, 0)
00222     ZEND_ARG_INFO(0, cert)
00223 ZEND_END_ARG_INFO()
00224 
00225 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_free, 0)
00226     ZEND_ARG_INFO(0, key)
00227 ZEND_END_ARG_INFO()
00228 
00229 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkey_get_private, 0, 0, 1)
00230     ZEND_ARG_INFO(0, key)
00231     ZEND_ARG_INFO(0, passphrase)
00232 ZEND_END_ARG_INFO()
00233 
00234 ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_details, 0)
00235     ZEND_ARG_INFO(0, key)
00236 ZEND_END_ARG_INFO()
00237 
00238 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2)
00239     ZEND_ARG_INFO(0, filename)
00240     ZEND_ARG_INFO(0, flags)
00241     ZEND_ARG_INFO(0, signerscerts)
00242     ZEND_ARG_INFO(0, cainfo) /* array */
00243     ZEND_ARG_INFO(0, extracerts)
00244     ZEND_ARG_INFO(0, content)
00245 ZEND_END_ARG_INFO()
00246 
00247 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_encrypt, 0, 0, 4)
00248     ZEND_ARG_INFO(0, infile)
00249     ZEND_ARG_INFO(0, outfile)
00250     ZEND_ARG_INFO(0, recipcerts)
00251     ZEND_ARG_INFO(0, headers) /* array */
00252     ZEND_ARG_INFO(0, flags)
00253     ZEND_ARG_INFO(0, cipher)
00254 ZEND_END_ARG_INFO()
00255 
00256 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_sign, 0, 0, 5)
00257     ZEND_ARG_INFO(0, infile)
00258     ZEND_ARG_INFO(0, outfile)
00259     ZEND_ARG_INFO(0, signcert)
00260     ZEND_ARG_INFO(0, signkey)
00261     ZEND_ARG_INFO(0, headers) /* array */
00262     ZEND_ARG_INFO(0, flags)
00263     ZEND_ARG_INFO(0, extracertsfilename)
00264 ZEND_END_ARG_INFO()
00265 
00266 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_decrypt, 0, 0, 3)
00267     ZEND_ARG_INFO(0, infilename)
00268     ZEND_ARG_INFO(0, outfilename)
00269     ZEND_ARG_INFO(0, recipcert)
00270     ZEND_ARG_INFO(0, recipkey)
00271 ZEND_END_ARG_INFO()
00272 
00273 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_encrypt, 0, 0, 3)
00274     ZEND_ARG_INFO(0, data)
00275     ZEND_ARG_INFO(1, crypted)
00276     ZEND_ARG_INFO(0, key)
00277     ZEND_ARG_INFO(0, padding)
00278 ZEND_END_ARG_INFO()
00279 
00280 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_private_decrypt, 0, 0, 3)
00281     ZEND_ARG_INFO(0, data)
00282     ZEND_ARG_INFO(1, crypted)
00283     ZEND_ARG_INFO(0, key)
00284     ZEND_ARG_INFO(0, padding)
00285 ZEND_END_ARG_INFO()
00286 
00287 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_encrypt, 0, 0, 3)
00288     ZEND_ARG_INFO(0, data)
00289     ZEND_ARG_INFO(1, crypted)
00290     ZEND_ARG_INFO(0, key)
00291     ZEND_ARG_INFO(0, padding)
00292 ZEND_END_ARG_INFO()
00293 
00294 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_public_decrypt, 0, 0, 3)
00295     ZEND_ARG_INFO(0, data)
00296     ZEND_ARG_INFO(1, crypted)
00297     ZEND_ARG_INFO(0, key)
00298     ZEND_ARG_INFO(0, padding)
00299 ZEND_END_ARG_INFO()
00300 
00301 ZEND_BEGIN_ARG_INFO(arginfo_openssl_error_string, 0)
00302 ZEND_END_ARG_INFO()
00303 
00304 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_sign, 0, 0, 3)
00305     ZEND_ARG_INFO(0, data)
00306     ZEND_ARG_INFO(1, signature)
00307     ZEND_ARG_INFO(0, key)
00308     ZEND_ARG_INFO(0, method)
00309 ZEND_END_ARG_INFO()
00310 
00311 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_verify, 0, 0, 3)
00312     ZEND_ARG_INFO(0, data)
00313     ZEND_ARG_INFO(0, signature)
00314     ZEND_ARG_INFO(0, key)
00315     ZEND_ARG_INFO(0, method)
00316 ZEND_END_ARG_INFO()
00317 
00318 ZEND_BEGIN_ARG_INFO(arginfo_openssl_seal, 0)
00319     ZEND_ARG_INFO(0, data)
00320     ZEND_ARG_INFO(1, sealdata)
00321     ZEND_ARG_INFO(1, ekeys) /* arary */
00322     ZEND_ARG_INFO(0, pubkeys) /* array */
00323 ZEND_END_ARG_INFO()
00324 
00325 ZEND_BEGIN_ARG_INFO(arginfo_openssl_open, 0)
00326     ZEND_ARG_INFO(0, data)
00327     ZEND_ARG_INFO(1, opendata)
00328     ZEND_ARG_INFO(0, ekey)
00329     ZEND_ARG_INFO(0, privkey)
00330 ZEND_END_ARG_INFO()
00331 
00332 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_md_methods, 0, 0, 0)
00333     ZEND_ARG_INFO(0, aliases)
00334 ZEND_END_ARG_INFO()
00335 
00336 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_cipher_methods, 0, 0, 0)
00337     ZEND_ARG_INFO(0, aliases)
00338 ZEND_END_ARG_INFO()
00339 
00340 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_digest, 0, 0, 2)
00341     ZEND_ARG_INFO(0, data)
00342     ZEND_ARG_INFO(0, method)
00343     ZEND_ARG_INFO(0, raw_output)
00344 ZEND_END_ARG_INFO()
00345 
00346 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_encrypt, 0, 0, 3)
00347     ZEND_ARG_INFO(0, data)
00348     ZEND_ARG_INFO(0, method)
00349     ZEND_ARG_INFO(0, password)
00350     ZEND_ARG_INFO(0, raw_output)
00351     ZEND_ARG_INFO(0, iv)
00352 ZEND_END_ARG_INFO()
00353 
00354 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3)
00355     ZEND_ARG_INFO(0, data)
00356     ZEND_ARG_INFO(0, method)
00357     ZEND_ARG_INFO(0, password)
00358     ZEND_ARG_INFO(0, raw_input)
00359     ZEND_ARG_INFO(0, iv)
00360 ZEND_END_ARG_INFO()
00361 
00362 ZEND_BEGIN_ARG_INFO(arginfo_openssl_cipher_iv_length, 0)
00363     ZEND_ARG_INFO(0, method)
00364 ZEND_END_ARG_INFO()
00365 
00366 ZEND_BEGIN_ARG_INFO(arginfo_openssl_dh_compute_key, 0)
00367     ZEND_ARG_INFO(0, pub_key)
00368     ZEND_ARG_INFO(0, dh_key)
00369 ZEND_END_ARG_INFO()
00370 
00371 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_random_pseudo_bytes, 0, 0, 1)
00372     ZEND_ARG_INFO(0, length)
00373     ZEND_ARG_INFO(1, result_is_strong)
00374 ZEND_END_ARG_INFO()
00375 /* }}} */
00376 
00377 /* {{{ openssl_functions[]
00378  */
00379 const zend_function_entry openssl_functions[] = {
00380 /* public/private key functions */
00381        PHP_FE(openssl_pkey_free,                 arginfo_openssl_pkey_free)
00382        PHP_FE(openssl_pkey_new,                  arginfo_openssl_pkey_new)
00383        PHP_FE(openssl_pkey_export,               arginfo_openssl_pkey_export)
00384        PHP_FE(openssl_pkey_export_to_file,       arginfo_openssl_pkey_export_to_file)
00385        PHP_FE(openssl_pkey_get_private,   arginfo_openssl_pkey_get_private)
00386        PHP_FE(openssl_pkey_get_public,           arginfo_openssl_pkey_get_public)
00387        PHP_FE(openssl_pkey_get_details,   arginfo_openssl_pkey_get_details)
00388 
00389        PHP_FALIAS(openssl_free_key,              openssl_pkey_free,                 arginfo_openssl_pkey_free)
00390        PHP_FALIAS(openssl_get_privatekey, openssl_pkey_get_private,   arginfo_openssl_pkey_get_private)
00391        PHP_FALIAS(openssl_get_publickey,  openssl_pkey_get_public,    arginfo_openssl_pkey_get_public)
00392 
00393 /* x.509 cert funcs */
00394        PHP_FE(openssl_x509_read,                        arginfo_openssl_x509_read)
00395        PHP_FE(openssl_x509_free,                        arginfo_openssl_x509_free)
00396        PHP_FE(openssl_x509_parse,                       arginfo_openssl_x509_parse)
00397        PHP_FE(openssl_x509_checkpurpose,         arginfo_openssl_x509_checkpurpose)
00398        PHP_FE(openssl_x509_check_private_key,    arginfo_openssl_x509_check_private_key)
00399        PHP_FE(openssl_x509_export,                      arginfo_openssl_x509_export)
00400        PHP_FE(openssl_x509_export_to_file,              arginfo_openssl_x509_export_to_file)
00401 
00402 /* PKCS12 funcs */
00403        PHP_FE(openssl_pkcs12_export,                    arginfo_openssl_pkcs12_export)
00404        PHP_FE(openssl_pkcs12_export_to_file,     arginfo_openssl_pkcs12_export_to_file)
00405        PHP_FE(openssl_pkcs12_read,                      arginfo_openssl_pkcs12_read)
00406 
00407 /* CSR funcs */
00408        PHP_FE(openssl_csr_new,                          arginfo_openssl_csr_new)
00409        PHP_FE(openssl_csr_export,                arginfo_openssl_csr_export)
00410        PHP_FE(openssl_csr_export_to_file, arginfo_openssl_csr_export_to_file)
00411        PHP_FE(openssl_csr_sign,                  arginfo_openssl_csr_sign)
00412        PHP_FE(openssl_csr_get_subject,           arginfo_openssl_csr_get_subject)
00413        PHP_FE(openssl_csr_get_public_key, arginfo_openssl_csr_get_public_key)
00414 
00415        PHP_FE(openssl_digest,                           arginfo_openssl_digest)
00416        PHP_FE(openssl_encrypt,                          arginfo_openssl_encrypt)
00417        PHP_FE(openssl_decrypt,                          arginfo_openssl_decrypt)
00418        PHP_FE(openssl_cipher_iv_length,   arginfo_openssl_cipher_iv_length)
00419        PHP_FE(openssl_sign,                      arginfo_openssl_sign)
00420        PHP_FE(openssl_verify,                           arginfo_openssl_verify)
00421        PHP_FE(openssl_seal,                      arginfo_openssl_seal)
00422        PHP_FE(openssl_open,                      arginfo_openssl_open)
00423 
00424 /* for S/MIME handling */
00425        PHP_FE(openssl_pkcs7_verify,              arginfo_openssl_pkcs7_verify)
00426        PHP_FE(openssl_pkcs7_decrypt,             arginfo_openssl_pkcs7_decrypt)
00427        PHP_FE(openssl_pkcs7_sign,                arginfo_openssl_pkcs7_sign)
00428        PHP_FE(openssl_pkcs7_encrypt,             arginfo_openssl_pkcs7_encrypt)
00429 
00430        PHP_FE(openssl_private_encrypt,           arginfo_openssl_private_encrypt)
00431        PHP_FE(openssl_private_decrypt,           arginfo_openssl_private_decrypt)
00432        PHP_FE(openssl_public_encrypt,            arginfo_openssl_public_encrypt)
00433        PHP_FE(openssl_public_decrypt,            arginfo_openssl_public_decrypt)
00434 
00435        PHP_FE(openssl_get_md_methods,            arginfo_openssl_get_md_methods)
00436        PHP_FE(openssl_get_cipher_methods, arginfo_openssl_get_cipher_methods)
00437 
00438        PHP_FE(openssl_dh_compute_key,      arginfo_openssl_dh_compute_key)
00439 
00440        PHP_FE(openssl_random_pseudo_bytes,    arginfo_openssl_random_pseudo_bytes)
00441        PHP_FE(openssl_error_string, arginfo_openssl_error_string)
00442        PHP_FE_END
00443 };
00444 /* }}} */
00445 
00446 /* {{{ openssl_module_entry
00447  */
00448 zend_module_entry openssl_module_entry = {
00449        STANDARD_MODULE_HEADER,
00450        "openssl",
00451        openssl_functions,
00452        PHP_MINIT(openssl),
00453        PHP_MSHUTDOWN(openssl),
00454        NULL,
00455        NULL,
00456        PHP_MINFO(openssl),
00457        NO_VERSION_YET,
00458        STANDARD_MODULE_PROPERTIES
00459 };
00460 /* }}} */
00461 
00462 #ifdef COMPILE_DL_OPENSSL
00463 ZEND_GET_MODULE(openssl)
00464 #endif
00465 
00466 static int le_key;
00467 static int le_x509;
00468 static int le_csr;
00469 static int ssl_stream_data_index;
00470 
00471 int php_openssl_get_x509_list_id(void) /* {{{ */
00472 {
00473        return le_x509;
00474 }
00475 /* }}} */
00476 
00477 /* {{{ resource destructors */
00478 static void php_pkey_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
00479 {
00480        EVP_PKEY *pkey = (EVP_PKEY *)rsrc->ptr;
00481 
00482        assert(pkey != NULL);
00483 
00484        EVP_PKEY_free(pkey);
00485 }
00486 
00487 static void php_x509_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
00488 {
00489        X509 *x509 = (X509 *)rsrc->ptr;
00490        X509_free(x509);
00491 }
00492 
00493 static void php_csr_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
00494 {
00495        X509_REQ * csr = (X509_REQ*)rsrc->ptr;
00496        X509_REQ_free(csr);
00497 }
00498 /* }}} */
00499 
00500 /* {{{ openssl safe_mode & open_basedir checks */
00501 inline static int php_openssl_safe_mode_chk(char *filename TSRMLS_DC)
00502 {
00503        if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
00504               return -1;
00505        }
00506        if (php_check_open_basedir(filename TSRMLS_CC)) {
00507               return -1;
00508        }
00509        
00510        return 0;
00511 }
00512 /* }}} */
00513 
00514 /* openssl -> PHP "bridging" */
00515 /* true global; readonly after module startup */
00516 static char default_ssl_conf_filename[MAXPATHLEN];
00517 
00518 struct php_x509_request { /* {{{ */
00519 #if OPENSSL_VERSION_NUMBER >= 0x10000002L
00520        LHASH_OF(CONF_VALUE) * global_config;     /* Global SSL config */
00521        LHASH_OF(CONF_VALUE) * req_config;        /* SSL config for this request */
00522 #else
00523        LHASH * global_config;  /* Global SSL config */
00524        LHASH * req_config;             /* SSL config for this request */
00525 #endif
00526        const EVP_MD * md_alg;
00527        const EVP_MD * digest;
00528        char   * section_name,
00529                      * config_filename,
00530                      * digest_name,
00531                      * extensions_section,
00532                      * request_extensions_section;
00533        int priv_key_bits;
00534        int priv_key_type;
00535 
00536        int priv_key_encrypt;
00537 
00538        EVP_PKEY * priv_key;
00539 };
00540 /* }}} */
00541 
00542 static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC);
00543 static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC);
00544 static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC);
00545 static X509_STORE     * setup_verify(zval * calist TSRMLS_DC);
00546 static STACK_OF(X509) * load_all_certs_from_file(char *certfile);
00547 static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC);
00548 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC);
00549 
00550 static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname TSRMLS_DC) /* {{{ */
00551 {
00552        zval *subitem, *subentries;
00553        int i, j = -1, last = -1, obj_cnt = 0;
00554        char *sname;
00555        int nid;
00556        X509_NAME_ENTRY * ne;
00557        ASN1_STRING * str = NULL;
00558        ASN1_OBJECT * obj;
00559 
00560        if (key != NULL) {
00561               MAKE_STD_ZVAL(subitem);
00562               array_init(subitem);
00563        } else {
00564               subitem = val;
00565        }
00566        
00567        for (i = 0; i < X509_NAME_entry_count(name); i++) {
00568               unsigned char *to_add;
00569               int to_add_len;
00570 
00571 
00572               ne  = X509_NAME_get_entry(name, i);
00573               obj = X509_NAME_ENTRY_get_object(ne);
00574               nid = OBJ_obj2nid(obj);
00575               obj_cnt = 0;
00576 
00577               if (shortname) {
00578                      sname = (char *) OBJ_nid2sn(nid);
00579               } else {
00580                      sname = (char *) OBJ_nid2ln(nid);
00581               }
00582 
00583               MAKE_STD_ZVAL(subentries);
00584               array_init(subentries);
00585 
00586               last = -1;
00587               for (;;) {
00588                      j = X509_NAME_get_index_by_OBJ(name, obj, last);
00589                      if (j < 0) {
00590                             if (last != -1) break;
00591                      } else {
00592                             obj_cnt++;
00593                             ne  = X509_NAME_get_entry(name, j);
00594                             str = X509_NAME_ENTRY_get_data(ne);
00595                             if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) {
00596                                    to_add_len = ASN1_STRING_to_UTF8(&to_add, str);
00597                                    if (to_add_len != -1) {
00598                                           add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1);
00599                                    }
00600                             } else {
00601                                    to_add = ASN1_STRING_data(str);
00602                                    to_add_len = ASN1_STRING_length(str);
00603                                    add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1);
00604                             }
00605                      }
00606                      last = j;
00607               }
00608               i = last;
00609               
00610               if (obj_cnt > 1) {
00611                      add_assoc_zval_ex(subitem, sname, strlen(sname) + 1, subentries);
00612               } else {
00613                      zval_dtor(subentries);
00614                      FREE_ZVAL(subentries);
00615                      if (obj_cnt && str && to_add_len > -1) {
00616                             add_assoc_stringl(subitem, sname, (char *)to_add, to_add_len, 1);
00617                      }
00618               }
00619        }
00620        if (key != NULL) {
00621               zend_hash_update(HASH_OF(val), key, strlen(key) + 1, (void *)&subitem, sizeof(subitem), NULL);
00622        }
00623 }
00624 /* }}} */
00625 
00626 static void add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) /* {{{ */
00627 {
00628        add_assoc_stringl(val, key, (char *)str->data, str->length, 1);
00629 }
00630 /* }}} */
00631 
00632 static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC) /* {{{ */
00633 {
00634 /*
00635        This is how the time string is formatted:
00636 
00637    snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
00638       ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
00639 */
00640 
00641        time_t ret;
00642        struct tm thetime;
00643        char * strbuf;
00644        char * thestr;
00645        long gmadjust = 0;
00646 
00647        if (timestr->length < 13) {
00648               php_error_docref(NULL TSRMLS_CC, E_WARNING, "extension author too lazy to parse %s correctly", timestr->data);
00649               return (time_t)-1;
00650        }
00651 
00652        strbuf = estrdup((char *)timestr->data);
00653 
00654        memset(&thetime, 0, sizeof(thetime));
00655 
00656        /* we work backwards so that we can use atoi more easily */
00657 
00658        thestr = strbuf + timestr->length - 3;
00659 
00660        thetime.tm_sec = atoi(thestr);
00661        *thestr = '\0';
00662        thestr -= 2;
00663        thetime.tm_min = atoi(thestr);
00664        *thestr = '\0';
00665        thestr -= 2;
00666        thetime.tm_hour = atoi(thestr);
00667        *thestr = '\0';
00668        thestr -= 2;
00669        thetime.tm_mday = atoi(thestr);
00670        *thestr = '\0';
00671        thestr -= 2;
00672        thetime.tm_mon = atoi(thestr)-1;
00673        *thestr = '\0';
00674        thestr -= 2;
00675        thetime.tm_year = atoi(thestr);
00676 
00677        if (thetime.tm_year < 68) {
00678               thetime.tm_year += 100;
00679        }
00680 
00681        thetime.tm_isdst = -1;
00682        ret = mktime(&thetime);
00683 
00684 #if HAVE_TM_GMTOFF
00685        gmadjust = thetime.tm_gmtoff;
00686 #else
00687        /*
00688        ** If correcting for daylight savings time, we set the adjustment to
00689        ** the value of timezone - 3600 seconds. Otherwise, we need to overcorrect and
00690        ** set the adjustment to the main timezone + 3600 seconds.
00691        */
00692        gmadjust = -(thetime.tm_isdst ? (long)timezone - 3600 : (long)timezone + 3600);
00693 #endif
00694        ret += gmadjust;
00695 
00696        efree(strbuf);
00697 
00698        return ret;
00699 }
00700 /* }}} */
00701 
00702 #if OPENSSL_VERSION_NUMBER >= 0x10000002L
00703 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) /* {{{ */
00704 #else
00705 static inline int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, LHASH * config TSRMLS_DC)
00706 #endif
00707 {
00708        X509V3_CTX ctx;
00709        
00710        X509V3_set_ctx_test(&ctx);
00711        X509V3_set_conf_lhash(&ctx, config);
00712        if (!X509V3_EXT_add_conf(config, &ctx, (char *)section, NULL)) {
00713               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading %s section %s of %s",
00714                             section_label,
00715                             section,
00716                             config_filename);
00717               return FAILURE;
00718        }
00719        return SUCCESS;
00720 }
00721 /* }}} */
00722 
00723 static int add_oid_section(struct php_x509_request * req TSRMLS_DC) /* {{{ */
00724 {
00725        char * str;
00726        STACK_OF(CONF_VALUE) * sktmp;
00727        CONF_VALUE * cnf;
00728        int i;
00729 
00730        str = CONF_get_string(req->req_config, NULL, "oid_section");
00731        if (str == NULL) {
00732               return SUCCESS;
00733        }
00734        sktmp = CONF_get_section(req->req_config, str);
00735        if (sktmp == NULL) {
00736               php_error_docref(NULL TSRMLS_CC, E_WARNING, "problem loading oid section %s", str);
00737               return FAILURE;
00738        }
00739        for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
00740               cnf = sk_CONF_VALUE_value(sktmp, i);
00741               if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) {
00742                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "problem creating object %s=%s", cnf->name, cnf->value);
00743                      return FAILURE;
00744               }
00745        }
00746        return SUCCESS;
00747 }
00748 /* }}} */
00749 
00750 #define PHP_SSL_REQ_INIT(req)             memset(req, 0, sizeof(*req))
00751 #define PHP_SSL_REQ_DISPOSE(req)   php_openssl_dispose_config(req TSRMLS_CC)
00752 #define PHP_SSL_REQ_PARSE(req, zval)      php_openssl_parse_config(req, zval TSRMLS_CC)
00753 
00754 #define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \
00755                      req->config_filename, req->var, req->req_config TSRMLS_CC) == FAILURE) return FAILURE
00756 
00757 #define SET_OPTIONAL_STRING_ARG(key, varname, defval)   \
00758        if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS) \
00759               varname = Z_STRVAL_PP(item); \
00760        else \
00761               varname = defval
00762 
00763 #define SET_OPTIONAL_LONG_ARG(key, varname, defval)     \
00764        if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), key, sizeof(key), (void**)&item) == SUCCESS) \
00765               varname = Z_LVAL_PP(item); \
00766        else \
00767               varname = defval
00768 
00769 static int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args TSRMLS_DC) /* {{{ */
00770 {
00771        char * str;
00772        zval ** item;
00773 
00774        SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename);
00775        SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req");
00776        req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL);
00777        req->req_config = CONF_load(NULL, req->config_filename, NULL);
00778 
00779        if (req->req_config == NULL) {
00780               return FAILURE;
00781        }
00782 
00783        /* read in the oids */
00784        str = CONF_get_string(req->req_config, NULL, "oid_file");
00785        if (str && !php_openssl_safe_mode_chk(str TSRMLS_CC)) {
00786               BIO *oid_bio = BIO_new_file(str, "r");
00787               if (oid_bio) {
00788                      OBJ_create_objects(oid_bio);
00789                      BIO_free(oid_bio);
00790               }
00791        }
00792        if (add_oid_section(req TSRMLS_CC) == FAILURE) {
00793               return FAILURE;
00794        }
00795        SET_OPTIONAL_STRING_ARG("digest_alg", req->digest_name,
00796               CONF_get_string(req->req_config, req->section_name, "default_md"));
00797        SET_OPTIONAL_STRING_ARG("x509_extensions", req->extensions_section,
00798               CONF_get_string(req->req_config, req->section_name, "x509_extensions"));
00799        SET_OPTIONAL_STRING_ARG("req_extensions", req->request_extensions_section,
00800               CONF_get_string(req->req_config, req->section_name, "req_extensions"));
00801        SET_OPTIONAL_LONG_ARG("private_key_bits", req->priv_key_bits,
00802               CONF_get_number(req->req_config, req->section_name, "default_bits"));
00803 
00804        SET_OPTIONAL_LONG_ARG("private_key_type", req->priv_key_type, OPENSSL_KEYTYPE_DEFAULT);
00805 
00806        if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), "encrypt_key", sizeof("encrypt_key"), (void**)&item) == SUCCESS) {
00807               req->priv_key_encrypt = Z_BVAL_PP(item);
00808        } else {
00809               str = CONF_get_string(req->req_config, req->section_name, "encrypt_rsa_key");
00810               if (str == NULL) {
00811                      str = CONF_get_string(req->req_config, req->section_name, "encrypt_key");
00812               }
00813               if (str && strcmp(str, "no") == 0) {
00814                      req->priv_key_encrypt = 0;
00815               } else {
00816                      req->priv_key_encrypt = 1;
00817               }
00818        }
00819        
00820        /* digest alg */
00821        if (req->digest_name == NULL) {
00822               req->digest_name = CONF_get_string(req->req_config, req->section_name, "default_md");
00823        }
00824        if (req->digest_name) {
00825               req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name);
00826        }
00827        if (req->md_alg == NULL) {
00828               req->md_alg = req->digest = EVP_md5();
00829        }
00830 
00831        PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section);
00832 
00833        /* set the string mask */
00834        str = CONF_get_string(req->req_config, req->section_name, "string_mask");
00835        if (str && !ASN1_STRING_set_default_mask_asc(str)) {
00836               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid global string mask setting %s", str);
00837               return FAILURE;
00838        }
00839 
00840        PHP_SSL_CONFIG_SYNTAX_CHECK(request_extensions_section);
00841        
00842        return SUCCESS;
00843 }
00844 /* }}} */
00845 
00846 static void php_openssl_dispose_config(struct php_x509_request * req TSRMLS_DC) /* {{{ */
00847 {
00848        if (req->priv_key) {
00849               EVP_PKEY_free(req->priv_key);
00850               req->priv_key = NULL;
00851        }
00852        if (req->global_config) {
00853               CONF_free(req->global_config);
00854               req->global_config = NULL;
00855        }
00856        if (req->req_config) {
00857               CONF_free(req->req_config);
00858               req->req_config = NULL;
00859        }
00860 }
00861 /* }}} */
00862 
00863 static int php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded) /* {{{ */
00864 {
00865        char buffer[MAXPATHLEN];
00866 
00867        TSRMLS_FETCH();
00868 
00869        *egdsocket = 0;
00870        *seeded = 0;
00871 
00872        if (file == NULL) {
00873               file = RAND_file_name(buffer, sizeof(buffer));
00874        } else if (RAND_egd(file) > 0) {
00875               /* if the given filename is an EGD socket, don't
00876                * write anything back to it */
00877               *egdsocket = 1;
00878               return SUCCESS;
00879        }
00880        if (file == NULL || !RAND_load_file(file, -1)) {
00881               if (RAND_status() == 0) {
00882                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to load random state; not enough random data!");
00883                      return FAILURE;
00884               }
00885               return FAILURE;
00886        }
00887        *seeded = 1;
00888        return SUCCESS;
00889 }
00890 /* }}} */
00891 
00892 static int php_openssl_write_rand_file(const char * file, int egdsocket, int seeded) /* {{{ */
00893 {
00894        char buffer[MAXPATHLEN];
00895 
00896        TSRMLS_FETCH();
00897 
00898        if (egdsocket || !seeded) {
00899               /* if we did not manage to read the seed file, we should not write
00900                * a low-entropy seed file back */
00901               return FAILURE;
00902        }
00903        if (file == NULL) {
00904               file = RAND_file_name(buffer, sizeof(buffer));
00905        }
00906        if (file == NULL || !RAND_write_file(file)) {
00907               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to write random state");
00908               return FAILURE;
00909        }
00910        return SUCCESS;
00911 }
00912 /* }}} */
00913 
00914 static EVP_MD * php_openssl_get_evp_md_from_algo(long algo) { /* {{{ */
00915        EVP_MD *mdtype;
00916 
00917        switch (algo) {
00918               case OPENSSL_ALGO_SHA1:
00919                      mdtype = (EVP_MD *) EVP_sha1();
00920                      break;
00921               case OPENSSL_ALGO_MD5:
00922                      mdtype = (EVP_MD *) EVP_md5();
00923                      break;
00924               case OPENSSL_ALGO_MD4:
00925                      mdtype = (EVP_MD *) EVP_md4();
00926                      break;
00927 #ifdef HAVE_OPENSSL_MD2_H
00928               case OPENSSL_ALGO_MD2:
00929                      mdtype = (EVP_MD *) EVP_md2();
00930                      break;
00931 #endif
00932               case OPENSSL_ALGO_DSS1:
00933                      mdtype = (EVP_MD *) EVP_dss1();
00934                      break;
00935               default:
00936                      return NULL;
00937                      break;
00938        }
00939        return mdtype;
00940 }
00941 /* }}} */
00942 
00943 static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo) { /* {{{ */
00944        switch (algo) {
00945 #ifndef OPENSSL_NO_RC2
00946               case PHP_OPENSSL_CIPHER_RC2_40:
00947                      return EVP_rc2_40_cbc();
00948                      break;
00949               case PHP_OPENSSL_CIPHER_RC2_64:
00950                      return EVP_rc2_64_cbc();
00951                      break;
00952               case PHP_OPENSSL_CIPHER_RC2_128:
00953                      return EVP_rc2_cbc();
00954                      break;
00955 #endif
00956 
00957 #ifndef OPENSSL_NO_DES
00958               case PHP_OPENSSL_CIPHER_DES:
00959                      return EVP_des_cbc();
00960                      break;
00961               case PHP_OPENSSL_CIPHER_3DES:
00962                      return EVP_des_ede3_cbc();
00963                      break;
00964 #endif
00965               default:
00966                      return NULL;
00967                      break;
00968        }
00969 }
00970 /* }}} */
00971 
00972 /* {{{ PHP_MINIT_FUNCTION
00973  */
00974 PHP_MINIT_FUNCTION(openssl)
00975 {
00976        char * config_filename;
00977 
00978        le_key = zend_register_list_destructors_ex(php_pkey_free, NULL, "OpenSSL key", module_number);
00979        le_x509 = zend_register_list_destructors_ex(php_x509_free, NULL, "OpenSSL X.509", module_number);
00980        le_csr = zend_register_list_destructors_ex(php_csr_free, NULL, "OpenSSL X.509 CSR", module_number);
00981 
00982        SSL_library_init();
00983        OpenSSL_add_all_ciphers();
00984        OpenSSL_add_all_digests();
00985        OpenSSL_add_all_algorithms();
00986 
00987        ERR_load_ERR_strings();
00988        ERR_load_crypto_strings();
00989        ERR_load_EVP_strings();
00990 
00991        /* register a resource id number with OpenSSL so that we can map SSL -> stream structures in
00992         * OpenSSL callbacks */
00993        ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL);
00994        
00995        REGISTER_STRING_CONSTANT("OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT, CONST_CS|CONST_PERSISTENT);
00996        REGISTER_LONG_CONSTANT("OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER, CONST_CS|CONST_PERSISTENT);
00997        
00998        /* purposes for cert purpose checking */
00999        REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_CLIENT", X509_PURPOSE_SSL_CLIENT, CONST_CS|CONST_PERSISTENT);
01000        REGISTER_LONG_CONSTANT("X509_PURPOSE_SSL_SERVER", X509_PURPOSE_SSL_SERVER, CONST_CS|CONST_PERSISTENT);
01001        REGISTER_LONG_CONSTANT("X509_PURPOSE_NS_SSL_SERVER", X509_PURPOSE_NS_SSL_SERVER, CONST_CS|CONST_PERSISTENT);
01002        REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_SIGN", X509_PURPOSE_SMIME_SIGN, CONST_CS|CONST_PERSISTENT);
01003        REGISTER_LONG_CONSTANT("X509_PURPOSE_SMIME_ENCRYPT", X509_PURPOSE_SMIME_ENCRYPT, CONST_CS|CONST_PERSISTENT);
01004        REGISTER_LONG_CONSTANT("X509_PURPOSE_CRL_SIGN", X509_PURPOSE_CRL_SIGN, CONST_CS|CONST_PERSISTENT);
01005 #ifdef X509_PURPOSE_ANY
01006        REGISTER_LONG_CONSTANT("X509_PURPOSE_ANY", X509_PURPOSE_ANY, CONST_CS|CONST_PERSISTENT);
01007 #endif
01008 
01009        /* signature algorithm constants */
01010        REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA1", OPENSSL_ALGO_SHA1, CONST_CS|CONST_PERSISTENT);
01011        REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD5", OPENSSL_ALGO_MD5, CONST_CS|CONST_PERSISTENT);
01012        REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD4", OPENSSL_ALGO_MD4, CONST_CS|CONST_PERSISTENT);
01013 #ifdef HAVE_OPENSSL_MD2_H
01014        REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD2", OPENSSL_ALGO_MD2, CONST_CS|CONST_PERSISTENT);
01015 #endif
01016        REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT);
01017 
01018        /* flags for S/MIME */
01019        REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT);
01020        REGISTER_LONG_CONSTANT("PKCS7_TEXT", PKCS7_TEXT, CONST_CS|CONST_PERSISTENT);
01021        REGISTER_LONG_CONSTANT("PKCS7_NOINTERN", PKCS7_NOINTERN, CONST_CS|CONST_PERSISTENT);
01022        REGISTER_LONG_CONSTANT("PKCS7_NOVERIFY", PKCS7_NOVERIFY, CONST_CS|CONST_PERSISTENT);
01023        REGISTER_LONG_CONSTANT("PKCS7_NOCHAIN", PKCS7_NOCHAIN, CONST_CS|CONST_PERSISTENT);
01024        REGISTER_LONG_CONSTANT("PKCS7_NOCERTS", PKCS7_NOCERTS, CONST_CS|CONST_PERSISTENT);
01025        REGISTER_LONG_CONSTANT("PKCS7_NOATTR", PKCS7_NOATTR, CONST_CS|CONST_PERSISTENT);
01026        REGISTER_LONG_CONSTANT("PKCS7_BINARY", PKCS7_BINARY, CONST_CS|CONST_PERSISTENT);
01027        REGISTER_LONG_CONSTANT("PKCS7_NOSIGS", PKCS7_NOSIGS, CONST_CS|CONST_PERSISTENT);
01028 
01029        REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_PADDING", RSA_PKCS1_PADDING, CONST_CS|CONST_PERSISTENT);
01030        REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING", RSA_SSLV23_PADDING, CONST_CS|CONST_PERSISTENT);
01031        REGISTER_LONG_CONSTANT("OPENSSL_NO_PADDING", RSA_NO_PADDING, CONST_CS|CONST_PERSISTENT);
01032        REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING, CONST_CS|CONST_PERSISTENT);
01033 
01034        /* Ciphers */
01035 #ifndef OPENSSL_NO_RC2
01036        REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_40", PHP_OPENSSL_CIPHER_RC2_40, CONST_CS|CONST_PERSISTENT);
01037        REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_128", PHP_OPENSSL_CIPHER_RC2_128, CONST_CS|CONST_PERSISTENT);
01038        REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_64", PHP_OPENSSL_CIPHER_RC2_64, CONST_CS|CONST_PERSISTENT);
01039 #endif
01040 #ifndef OPENSSL_NO_DES
01041        REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_DES", PHP_OPENSSL_CIPHER_DES, CONST_CS|CONST_PERSISTENT);
01042        REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_3DES", PHP_OPENSSL_CIPHER_3DES, CONST_CS|CONST_PERSISTENT);
01043 #endif
01044 
01045        /* Values for key types */
01046        REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_RSA", OPENSSL_KEYTYPE_RSA, CONST_CS|CONST_PERSISTENT);
01047 #ifndef NO_DSA
01048        REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DSA", OPENSSL_KEYTYPE_DSA, CONST_CS|CONST_PERSISTENT);
01049 #endif
01050        REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH", OPENSSL_KEYTYPE_DH, CONST_CS|CONST_PERSISTENT);
01051 #ifdef EVP_PKEY_EC
01052        REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_EC", OPENSSL_KEYTYPE_EC, CONST_CS|CONST_PERSISTENT);
01053 #endif
01054 
01055 #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
01056        /* SNI support included in OpenSSL >= 0.9.8j */
01057        REGISTER_LONG_CONSTANT("OPENSSL_TLSEXT_SERVER_NAME", 1, CONST_CS|CONST_PERSISTENT);
01058 #endif
01059 
01060        /* Determine default SSL configuration file */
01061        config_filename = getenv("OPENSSL_CONF");
01062        if (config_filename == NULL) {
01063               config_filename = getenv("SSLEAY_CONF");
01064        }
01065 
01066        /* default to 'openssl.cnf' if no environment variable is set */
01067        if (config_filename == NULL) {
01068               snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename), "%s/%s",
01069                             X509_get_default_cert_area(),
01070                             "openssl.cnf");
01071        } else {
01072               strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename));
01073        }
01074 
01075        php_stream_xport_register("ssl", php_openssl_ssl_socket_factory TSRMLS_CC);
01076        php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory TSRMLS_CC);
01077 #ifndef OPENSSL_NO_SSL2
01078        php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory TSRMLS_CC);
01079 #endif
01080        php_stream_xport_register("tls", php_openssl_ssl_socket_factory TSRMLS_CC);
01081 
01082        /* override the default tcp socket provider */
01083        php_stream_xport_register("tcp", php_openssl_ssl_socket_factory TSRMLS_CC);
01084 
01085        php_register_url_stream_wrapper("https", &php_stream_http_wrapper TSRMLS_CC);
01086        php_register_url_stream_wrapper("ftps", &php_stream_ftp_wrapper TSRMLS_CC);
01087        
01088        return SUCCESS;
01089 }
01090 /* }}} */
01091 
01092 /* {{{ PHP_MINFO_FUNCTION
01093  */
01094 PHP_MINFO_FUNCTION(openssl)
01095 {
01096        php_info_print_table_start();
01097        php_info_print_table_row(2, "OpenSSL support", "enabled");
01098        php_info_print_table_row(2, "OpenSSL Library Version", SSLeay_version(SSLEAY_VERSION));
01099        php_info_print_table_row(2, "OpenSSL Header Version", OPENSSL_VERSION_TEXT);
01100        php_info_print_table_end();
01101 }
01102 /* }}} */
01103 
01104 /* {{{ PHP_MSHUTDOWN_FUNCTION
01105  */
01106 PHP_MSHUTDOWN_FUNCTION(openssl)
01107 {
01108        EVP_cleanup();
01109 
01110        php_unregister_url_stream_wrapper("https" TSRMLS_CC);
01111        php_unregister_url_stream_wrapper("ftps" TSRMLS_CC);
01112 
01113        php_stream_xport_unregister("ssl" TSRMLS_CC);
01114 #ifndef OPENSSL_NO_SSL2
01115        php_stream_xport_unregister("sslv2" TSRMLS_CC);
01116 #endif
01117        php_stream_xport_unregister("sslv3" TSRMLS_CC);
01118        php_stream_xport_unregister("tls" TSRMLS_CC);
01119 
01120        /* reinstate the default tcp handler */
01121        php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC);
01122 
01123        return SUCCESS;
01124 }
01125 /* }}} */
01126 
01127 /* {{{ x509 cert functions */
01128 
01129 /* {{{ php_openssl_x509_from_zval
01130        Given a zval, coerce it into an X509 object.
01131        The zval can be:
01132               . X509 resource created using openssl_read_x509()
01133               . if it starts with file:// then it will be interpreted as the path to that cert
01134               . it will be interpreted as the cert data
01135        If you supply makeresource, the result will be registered as an x509 resource and
01136        it's value returned in makeresource.
01137 */
01138 static X509 * php_openssl_x509_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC)
01139 {
01140        X509 *cert = NULL;
01141 
01142        if (resourceval) {
01143               *resourceval = -1;
01144        }
01145        if (Z_TYPE_PP(val) == IS_RESOURCE) {
01146               /* is it an x509 resource ? */
01147               void * what;
01148               int type;
01149 
01150               what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509", &type, 1, le_x509);
01151               if (!what) {
01152                      return NULL;
01153               }
01154               /* this is so callers can decide if they should free the X509 */
01155               if (resourceval) {
01156                      *resourceval = Z_LVAL_PP(val);
01157               }
01158               if (type == le_x509) {
01159                      return (X509*)what;
01160               }
01161               /* other types could be used here - eg: file pointers and read in the data from them */
01162 
01163               return NULL;
01164        }
01165 
01166        if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) {
01167               return NULL;
01168        }
01169 
01170        /* force it to be a string and check if it refers to a file */
01171        convert_to_string_ex(val);
01172 
01173        if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
01174               /* read cert from the named file */
01175               BIO *in;
01176 
01177               if (php_openssl_safe_mode_chk(Z_STRVAL_PP(val) + (sizeof("file://") - 1) TSRMLS_CC)) {
01178                      return NULL;
01179               }
01180 
01181               in = BIO_new_file(Z_STRVAL_PP(val) + (sizeof("file://") - 1), "r");
01182               if (in == NULL) {
01183                      return NULL;
01184               }
01185               cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
01186               BIO_free(in);
01187        } else {
01188               BIO *in;
01189 
01190               in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
01191               if (in == NULL) {
01192                      return NULL;
01193               }
01194 #ifdef TYPEDEF_D2I_OF
01195               cert = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
01196 #else
01197               cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
01198 #endif
01199               BIO_free(in);
01200        }
01201 
01202        if (cert && makeresource && resourceval) {
01203               *resourceval = zend_list_insert(cert, le_x509);
01204        }
01205        return cert;
01206 }
01207 
01208 /* }}} */
01209 
01210 /* {{{ proto bool openssl_x509_export_to_file(mixed x509, string outfilename [, bool notext = true])
01211    Exports a CERT to file or a var */
01212 PHP_FUNCTION(openssl_x509_export_to_file)
01213 {
01214        X509 * cert;
01215        zval ** zcert;
01216        zend_bool notext = 1;
01217        BIO * bio_out;
01218        long certresource;
01219        char * filename;
01220        int filename_len;
01221 
01222        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs|b", &zcert, &filename, &filename_len, &notext) == FAILURE) {
01223               return;
01224        }
01225        RETVAL_FALSE;
01226 
01227        cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
01228        if (cert == NULL) {
01229               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
01230               return;
01231        }
01232 
01233        if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
01234               return;
01235        }
01236 
01237        bio_out = BIO_new_file(filename, "w");
01238        if (bio_out) {
01239               if (!notext) {
01240                      X509_print(bio_out, cert);
01241               }
01242               PEM_write_bio_X509(bio_out, cert);
01243 
01244               RETVAL_TRUE;
01245        } else {
01246               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename);
01247        }
01248        if (certresource == -1 && cert) {
01249               X509_free(cert);
01250        }
01251        BIO_free(bio_out);
01252 }
01253 /* }}} */
01254 
01255 /* {{{ proto bool openssl_x509_export(mixed x509, string &out [, bool notext = true])
01256    Exports a CERT to file or a var */
01257 PHP_FUNCTION(openssl_x509_export)
01258 {
01259        X509 * cert;
01260        zval ** zcert, *zout;
01261        zend_bool notext = 1;
01262        BIO * bio_out;
01263        long certresource;
01264 
01265        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|b", &zcert, &zout, &notext) == FAILURE) {
01266               return;
01267        }
01268        RETVAL_FALSE;
01269 
01270        cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
01271        if (cert == NULL) {
01272               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
01273               return;
01274        }
01275 
01276        bio_out = BIO_new(BIO_s_mem());
01277        if (!notext) {
01278               X509_print(bio_out, cert);
01279        }
01280        if (PEM_write_bio_X509(bio_out, cert))  {
01281               BUF_MEM *bio_buf;
01282 
01283               zval_dtor(zout);
01284               BIO_get_mem_ptr(bio_out, &bio_buf);
01285               ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1);
01286 
01287               RETVAL_TRUE;
01288        }
01289 
01290        if (certresource == -1 && cert) {
01291               X509_free(cert);
01292        }
01293        BIO_free(bio_out);
01294 }
01295 /* }}} */
01296 
01297 /* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key)
01298    Checks if a private key corresponds to a CERT */
01299 PHP_FUNCTION(openssl_x509_check_private_key)
01300 {
01301        zval ** zcert, **zkey;
01302        X509 * cert = NULL;
01303        EVP_PKEY * key = NULL;
01304        long certresource = -1, keyresource = -1;
01305 
01306        RETVAL_FALSE;
01307        
01308        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ", &zcert, &zkey) == FAILURE) {
01309               return;
01310        }
01311        cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
01312        if (cert == NULL) {
01313               RETURN_FALSE;
01314        }      
01315        key = php_openssl_evp_from_zval(zkey, 0, "", 1, &keyresource TSRMLS_CC);
01316        if (key) {
01317               RETVAL_BOOL(X509_check_private_key(cert, key));
01318        }
01319 
01320        if (keyresource == -1 && key) {
01321               EVP_PKEY_free(key);
01322        }
01323        if (certresource == -1 && cert) {
01324               X509_free(cert);
01325        }
01326 }
01327 /* }}} */
01328 
01329 /* {{{ proto array openssl_x509_parse(mixed x509 [, bool shortnames=true])
01330    Returns an array of the fields/values of the CERT */
01331 PHP_FUNCTION(openssl_x509_parse)
01332 {
01333        zval ** zcert;
01334        X509 * cert = NULL;
01335        long certresource = -1;
01336        int i;
01337        zend_bool useshortnames = 1;
01338        char * tmpstr;
01339        zval * subitem;
01340        X509_EXTENSION *extension;
01341        char *extname;
01342        BIO  *bio_out;
01343        BUF_MEM *bio_buf;
01344        char buf[256];
01345 
01346        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcert, &useshortnames) == FAILURE) {
01347               return;
01348        }
01349        cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
01350        if (cert == NULL) {
01351               RETURN_FALSE;
01352        }
01353        array_init(return_value);
01354 
01355        if (cert->name) {
01356               add_assoc_string(return_value, "name", cert->name, 1);
01357        }
01358 /*     add_assoc_bool(return_value, "valid", cert->valid); */
01359 
01360        add_assoc_name_entry(return_value, "subject",           X509_get_subject_name(cert), useshortnames TSRMLS_CC);
01361        /* hash as used in CA directories to lookup cert by subject name */
01362        {
01363               char buf[32];
01364               snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert));
01365               add_assoc_string(return_value, "hash", buf, 1);
01366        }
01367        
01368        add_assoc_name_entry(return_value, "issuer",            X509_get_issuer_name(cert), useshortnames TSRMLS_CC);
01369        add_assoc_long(return_value, "version",                 X509_get_version(cert));
01370 
01371        add_assoc_string(return_value, "serialNumber", i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(cert)), 1); 
01372 
01373        add_assoc_asn1_string(return_value, "validFrom",        X509_get_notBefore(cert));
01374        add_assoc_asn1_string(return_value, "validTo",          X509_get_notAfter(cert));
01375 
01376        add_assoc_long(return_value, "validFrom_time_t",        asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC));
01377        add_assoc_long(return_value, "validTo_time_t",          asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC));
01378 
01379        tmpstr = (char *)X509_alias_get0(cert, NULL);
01380        if (tmpstr) {
01381               add_assoc_string(return_value, "alias", tmpstr, 1);
01382        }
01383 /*
01384        add_assoc_long(return_value, "signaturetypeLONG", X509_get_signature_type(cert));
01385        add_assoc_string(return_value, "signaturetype", OBJ_nid2sn(X509_get_signature_type(cert)), 1);
01386        add_assoc_string(return_value, "signaturetypeLN", OBJ_nid2ln(X509_get_signature_type(cert)), 1);
01387 */
01388        MAKE_STD_ZVAL(subitem);
01389        array_init(subitem);
01390 
01391        /* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines
01392           in x509v3.h */
01393        for (i = 0; i < X509_PURPOSE_get_count(); i++) {
01394               int id, purpset;
01395               char * pname;
01396               X509_PURPOSE * purp;
01397               zval * subsub;
01398 
01399               MAKE_STD_ZVAL(subsub);
01400               array_init(subsub);
01401 
01402               purp = X509_PURPOSE_get0(i);
01403               id = X509_PURPOSE_get_id(purp);
01404 
01405               purpset = X509_check_purpose(cert, id, 0);
01406               add_index_bool(subsub, 0, purpset);
01407 
01408               purpset = X509_check_purpose(cert, id, 1);
01409               add_index_bool(subsub, 1, purpset);
01410 
01411               pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp);
01412               add_index_string(subsub, 2, pname, 1);
01413 
01414               /* NOTE: if purpset > 1 then it's a warning - we should mention it ? */
01415 
01416               add_index_zval(subitem, id, subsub);
01417        }
01418        add_assoc_zval(return_value, "purposes", subitem);
01419 
01420        MAKE_STD_ZVAL(subitem);
01421        array_init(subitem);
01422 
01423 
01424        for (i = 0; i < X509_get_ext_count(cert); i++) {
01425               extension = X509_get_ext(cert, i);
01426               if (OBJ_obj2nid(X509_EXTENSION_get_object(extension)) != NID_undef) {
01427                      extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension)));
01428               } else {
01429                      OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1);
01430                      extname = buf;
01431               }
01432               bio_out = BIO_new(BIO_s_mem());
01433               if (X509V3_EXT_print(bio_out, extension, 0, 0)) {
01434                      BIO_get_mem_ptr(bio_out, &bio_buf);
01435                      add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1);
01436               } else {
01437                      add_assoc_asn1_string(subitem, extname, X509_EXTENSION_get_data(extension));
01438               }
01439               BIO_free(bio_out);
01440        }
01441        add_assoc_zval(return_value, "extensions", subitem);
01442 
01443        if (certresource == -1 && cert) {
01444               X509_free(cert);
01445        }
01446 }
01447 /* }}} */
01448 
01449 /* {{{ load_all_certs_from_file */
01450 static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
01451 {
01452        STACK_OF(X509_INFO) *sk=NULL;
01453        STACK_OF(X509) *stack=NULL, *ret=NULL;
01454        BIO *in=NULL;
01455        X509_INFO *xi;
01456        TSRMLS_FETCH();
01457 
01458        if(!(stack = sk_X509_new_null())) {
01459               php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure");
01460               goto end;
01461        }
01462 
01463        if (php_openssl_safe_mode_chk(certfile TSRMLS_CC)) {
01464               sk_X509_free(stack);
01465               goto end;
01466        }
01467 
01468        if(!(in=BIO_new_file(certfile, "r"))) {
01469               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening the file, %s", certfile);
01470               sk_X509_free(stack);
01471               goto end;
01472        }
01473 
01474        /* This loads from a file, a stack of x509/crl/pkey sets */
01475        if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
01476               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error reading the file, %s", certfile);
01477               sk_X509_free(stack);
01478               goto end;
01479        }
01480 
01481        /* scan over it and pull out the certs */
01482        while (sk_X509_INFO_num(sk)) {
01483               xi=sk_X509_INFO_shift(sk);
01484               if (xi->x509 != NULL) {
01485                      sk_X509_push(stack,xi->x509);
01486                      xi->x509=NULL;
01487               }
01488               X509_INFO_free(xi);
01489        }
01490        if(!sk_X509_num(stack)) {
01491               php_error_docref(NULL TSRMLS_CC, E_WARNING, "no certificates in file, %s", certfile);
01492               sk_X509_free(stack);
01493               goto end;
01494        }
01495        ret=stack;
01496 end:
01497        BIO_free(in);
01498        sk_X509_INFO_free(sk);
01499 
01500        return ret;
01501 }
01502 /* }}} */
01503 
01504 /* {{{ check_cert */
01505 static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose)
01506 {
01507        int ret=0;
01508        X509_STORE_CTX *csc;
01509        TSRMLS_FETCH();
01510 
01511        csc = X509_STORE_CTX_new();
01512        if (csc == NULL) {
01513               php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure");
01514               return 0;
01515        }
01516        X509_STORE_CTX_init(csc, ctx, x, untrustedchain);
01517        if(purpose >= 0) {
01518               X509_STORE_CTX_set_purpose(csc, purpose);
01519        }
01520        ret = X509_verify_cert(csc);
01521        X509_STORE_CTX_free(csc);
01522 
01523        return ret;
01524 }
01525 /* }}} */
01526 
01527 /* {{{ proto int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo [, string untrustedfile])
01528    Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs */
01529 PHP_FUNCTION(openssl_x509_checkpurpose)
01530 {
01531        zval ** zcert, * zcainfo = NULL;
01532        X509_STORE * cainfo = NULL;
01533        X509 * cert = NULL;
01534        long certresource = -1;
01535        STACK_OF(X509) * untrustedchain = NULL;
01536        long purpose;
01537        char * untrusted = NULL;
01538        int untrusted_len = 0, ret;
01539 
01540        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zl|a!s", &zcert, &purpose, &zcainfo, &untrusted, &untrusted_len) == FAILURE) {
01541               return;
01542        }
01543 
01544        RETVAL_LONG(-1);
01545 
01546        if (untrusted) {
01547               untrustedchain = load_all_certs_from_file(untrusted);
01548               if (untrustedchain == NULL) {
01549                      goto clean_exit;
01550               }
01551        }
01552 
01553        cainfo = setup_verify(zcainfo TSRMLS_CC);
01554        if (cainfo == NULL) {
01555               goto clean_exit;
01556        }
01557        cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
01558        if (cert == NULL) {
01559               goto clean_exit;
01560        }
01561 
01562        ret = check_cert(cainfo, cert, untrustedchain, purpose);
01563        if (ret != 0 && ret != 1) {
01564               RETVAL_LONG(ret);
01565        } else {
01566               RETVAL_BOOL(ret);
01567        }
01568 
01569 clean_exit:
01570        if (certresource == 1 && cert) {
01571               X509_free(cert);
01572        }
01573        if (cainfo) { 
01574               X509_STORE_free(cainfo); 
01575        }
01576        if (untrustedchain) {
01577               sk_X509_pop_free(untrustedchain, X509_free);
01578        }
01579 }
01580 /* }}} */
01581 
01582 /* {{{ setup_verify
01583  * calist is an array containing file and directory names.  create a
01584  * certificate store and add those certs to it for use in verification.
01585 */
01586 static X509_STORE * setup_verify(zval * calist TSRMLS_DC)
01587 {
01588        X509_STORE *store;
01589        X509_LOOKUP * dir_lookup, * file_lookup;
01590        HashPosition pos;
01591        int ndirs = 0, nfiles = 0;
01592 
01593        store = X509_STORE_new();
01594 
01595        if (store == NULL) {
01596               return NULL;
01597        }
01598 
01599        if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) {
01600               zend_hash_internal_pointer_reset_ex(HASH_OF(calist), &pos);
01601               for (;; zend_hash_move_forward_ex(HASH_OF(calist), &pos)) {
01602                      zval ** item;
01603                      struct stat sb;
01604 
01605                      if (zend_hash_get_current_data_ex(HASH_OF(calist), (void**)&item, &pos) == FAILURE) {
01606                             break;
01607                      }
01608                      convert_to_string_ex(item);
01609 
01610                      if (VCWD_STAT(Z_STRVAL_PP(item), &sb) == -1) {
01611                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to stat %s", Z_STRVAL_PP(item));
01612                             continue;
01613                      }
01614 
01615                      if ((sb.st_mode & S_IFREG) == S_IFREG) {
01616                             file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
01617                             if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) {
01618                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading file %s", Z_STRVAL_PP(item));
01619                             } else {
01620                                    nfiles++;
01621                             }
01622                             file_lookup = NULL;
01623                      } else {
01624                             dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
01625                             if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) {
01626                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading directory %s", Z_STRVAL_PP(item));
01627                             } else { 
01628                                    ndirs++;
01629                             }
01630                             dir_lookup = NULL;
01631                      }
01632               }
01633        }
01634        if (nfiles == 0) {
01635               file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
01636               if (file_lookup) {
01637                      X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT);
01638               }
01639        }
01640        if (ndirs == 0) {
01641               dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
01642               if (dir_lookup) {
01643                      X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT);
01644               }
01645        }
01646        return store;
01647 }
01648 /* }}} */
01649 
01650 /* {{{ proto resource openssl_x509_read(mixed cert)
01651    Reads X.509 certificates */
01652 PHP_FUNCTION(openssl_x509_read)
01653 {
01654        zval **cert;
01655        X509 *x509;
01656 
01657        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) {
01658               return;
01659        }
01660        Z_TYPE_P(return_value) = IS_RESOURCE;
01661        x509 = php_openssl_x509_from_zval(cert, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
01662 
01663        if (x509 == NULL) {
01664               php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied parameter cannot be coerced into an X509 certificate!");
01665               RETURN_FALSE;
01666        }
01667 }
01668 /* }}} */
01669 
01670 /* {{{ proto void openssl_x509_free(resource x509)
01671    Frees X.509 certificates */
01672 PHP_FUNCTION(openssl_x509_free)
01673 {
01674        zval *x509;
01675        X509 *cert;
01676 
01677        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &x509) == FAILURE) {
01678               return;
01679        }
01680        ZEND_FETCH_RESOURCE(cert, X509 *, &x509, -1, "OpenSSL X.509", le_x509);
01681        zend_list_delete(Z_LVAL_P(x509));
01682 }
01683 /* }}} */
01684 
01685 /* }}} */
01686 
01687 /* Pop all X509 from Stack and free them, free the stack afterwards */
01688 static void php_sk_X509_free(STACK_OF(X509) * sk) /* {{{ */
01689 {
01690        for (;;) {
01691               X509* x = sk_X509_pop(sk);
01692               if (!x) break;
01693               X509_free(x);
01694        }
01695        sk_X509_free(sk);
01696 }
01697 /* }}} */
01698 
01699 static STACK_OF(X509) * php_array_to_X509_sk(zval ** zcerts TSRMLS_DC) /* {{{ */
01700 {
01701        HashPosition hpos;
01702        zval ** zcertval;
01703        STACK_OF(X509) * sk = NULL;
01704     X509 * cert;
01705     long certresource;
01706 
01707        sk = sk_X509_new_null();
01708 
01709        /* get certs */
01710        if (Z_TYPE_PP(zcerts) == IS_ARRAY) {
01711               zend_hash_internal_pointer_reset_ex(HASH_OF(*zcerts), &hpos);
01712               while(zend_hash_get_current_data_ex(HASH_OF(*zcerts), (void**)&zcertval, &hpos) == SUCCESS) {
01713 
01714                      cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC);
01715                      if (cert == NULL) {
01716                             goto clean_exit;
01717                      }
01718 
01719                      if (certresource != -1) {
01720                             cert = X509_dup(cert);
01721                             
01722                             if (cert == NULL) {
01723                                    goto clean_exit;
01724                             }
01725                             
01726                      }
01727                      sk_X509_push(sk, cert);
01728 
01729                      zend_hash_move_forward_ex(HASH_OF(*zcerts), &hpos);
01730               }
01731        } else {
01732               /* a single certificate */
01733               cert = php_openssl_x509_from_zval(zcerts, 0, &certresource TSRMLS_CC);
01734               
01735               if (cert == NULL) {
01736                      goto clean_exit;
01737               }
01738 
01739               if (certresource != -1) {
01740                      cert = X509_dup(cert);
01741                      if (cert == NULL) {
01742                             goto clean_exit;
01743                      }
01744               }
01745               sk_X509_push(sk, cert);
01746        }
01747 
01748   clean_exit:
01749     return sk;
01750 }
01751 /* }}} */
01752 
01753 /* {{{ proto bool openssl_pkcs12_export_to_file(mixed x509, string filename, mixed priv_key, string pass[, array args])
01754    Creates and exports a PKCS to file */
01755 PHP_FUNCTION(openssl_pkcs12_export_to_file)
01756 {
01757        X509 * cert = NULL;
01758        BIO * bio_out = NULL;
01759        PKCS12 * p12 = NULL;
01760        char * filename;
01761        char * friendly_name = NULL;
01762        int filename_len;
01763        char * pass;
01764        int pass_len;
01765        zval **zcert = NULL, *zpkey = NULL, *args = NULL;
01766        EVP_PKEY *priv_key = NULL;
01767        long certresource, keyresource;
01768        zval ** item;
01769        STACK_OF(X509) *ca = NULL;
01770 
01771        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zszs|a", &zcert, &filename, &filename_len, &zpkey, &pass, &pass_len, &args) == FAILURE)
01772               return;
01773 
01774        RETVAL_FALSE;
01775 
01776        if (strlen(filename) != filename_len) {
01777               return;
01778        }
01779        
01780        cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
01781        if (cert == NULL) {
01782               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
01783               return;
01784        }
01785        priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC);
01786        if (priv_key == NULL) {
01787               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
01788               goto cleanup;
01789        }
01790        if (cert && !X509_check_private_key(cert, priv_key)) {
01791               php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert");
01792               goto cleanup;
01793        }
01794        if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
01795               goto cleanup;
01796        }
01797 
01798        /* parse extra config from args array, promote this to an extra function */
01799        if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS)
01800               friendly_name = Z_STRVAL_PP(item);
01801        /* certpbe (default RC2-40)
01802           keypbe (default 3DES)
01803           friendly_caname
01804        */
01805 
01806        if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS)
01807               ca = php_array_to_X509_sk(item TSRMLS_CC);
01808        /* end parse extra config */
01809 
01810        /*PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ca,
01811                                        int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/
01812 
01813        p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
01814 
01815        bio_out = BIO_new_file(filename, "w"); 
01816        if (bio_out) {
01817               
01818               i2d_PKCS12_bio(bio_out, p12);
01819 
01820               RETVAL_TRUE;
01821        } else {
01822               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename);
01823        }
01824 
01825        BIO_free(bio_out);
01826        PKCS12_free(p12);
01827        php_sk_X509_free(ca);
01828        
01829 cleanup:
01830 
01831        if (keyresource == -1 && priv_key) {
01832               EVP_PKEY_free(priv_key);
01833        }
01834        if (certresource == -1 && cert) { 
01835               X509_free(cert);
01836        }
01837 }
01838 /* }}} */
01839 
01840 /* {{{ proto bool openssl_pkcs12_export(mixed x509, string &out, mixed priv_key, string pass[, array args])
01841    Creates and exports a PKCS12 to a var */
01842 PHP_FUNCTION(openssl_pkcs12_export)
01843 {
01844        X509 * cert = NULL;
01845        BIO * bio_out;
01846        PKCS12 * p12 = NULL;
01847        zval * zcert = NULL, *zout = NULL, *zpkey, *args = NULL;
01848        EVP_PKEY *priv_key = NULL;
01849        long certresource, keyresource;
01850        char * pass;
01851        int pass_len;
01852        char * friendly_name = NULL;
01853        zval ** item;
01854        STACK_OF(X509) *ca = NULL;
01855 
01856        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzzs|a", &zcert, &zout, &zpkey, &pass, &pass_len, &args) == FAILURE)
01857               return;
01858 
01859        RETVAL_FALSE;
01860        
01861        cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
01862        if (cert == NULL) {
01863               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
01864               return;
01865        }
01866        priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC);
01867        if (priv_key == NULL) {
01868               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
01869               goto cleanup;
01870        }
01871        if (cert && !X509_check_private_key(cert, priv_key)) {
01872               php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert");
01873               goto cleanup;
01874        }
01875 
01876        /* parse extra config from args array, promote this to an extra function */
01877        if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS)
01878               friendly_name = Z_STRVAL_PP(item);
01879 
01880        if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS)
01881               ca = php_array_to_X509_sk(item TSRMLS_CC);
01882        /* end parse extra config */
01883        
01884        p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
01885 
01886        bio_out = BIO_new(BIO_s_mem());
01887        if (i2d_PKCS12_bio(bio_out, p12))  {
01888               BUF_MEM *bio_buf;
01889 
01890               zval_dtor(zout);
01891               BIO_get_mem_ptr(bio_out, &bio_buf);
01892               ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1);
01893 
01894               RETVAL_TRUE;
01895        }
01896 
01897        BIO_free(bio_out);
01898        PKCS12_free(p12);
01899        php_sk_X509_free(ca);
01900        
01901 cleanup:
01902 
01903        if (keyresource == -1 && priv_key) {
01904               EVP_PKEY_free(priv_key);
01905        }
01906        if (certresource == -1 && cert) { 
01907               X509_free(cert);
01908        }
01909 }
01910 /* }}} */
01911 
01912 /* {{{ proto bool openssl_pkcs12_read(string PKCS12, array &certs, string pass)
01913    Parses a PKCS12 to an array */
01914 PHP_FUNCTION(openssl_pkcs12_read)
01915 {
01916        zval *zout = NULL, *zextracerts, *zcert, *zpkey;
01917        char *pass, *zp12;
01918        int pass_len, zp12_len;
01919        PKCS12 * p12 = NULL;
01920        EVP_PKEY * pkey = NULL;
01921        X509 * cert = NULL;
01922        STACK_OF(X509) * ca = NULL;
01923        BIO * bio_in = NULL;
01924        int i;
01925 
01926        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szs", &zp12, &zp12_len, &zout, &pass, &pass_len) == FAILURE)
01927               return;
01928 
01929        RETVAL_FALSE;
01930        
01931        bio_in = BIO_new(BIO_s_mem());
01932        
01933        if(!BIO_write(bio_in, zp12, zp12_len))
01934               goto cleanup;
01935        
01936        if(d2i_PKCS12_bio(bio_in, &p12)) {
01937               if(PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
01938                      BIO * bio_out;
01939 
01940                      zval_dtor(zout);
01941                      array_init(zout);
01942 
01943                      bio_out = BIO_new(BIO_s_mem());
01944                      if (PEM_write_bio_X509(bio_out, cert)) {
01945                             BUF_MEM *bio_buf;
01946                             BIO_get_mem_ptr(bio_out, &bio_buf);
01947                             MAKE_STD_ZVAL(zcert);
01948                             ZVAL_STRINGL(zcert, bio_buf->data, bio_buf->length, 1);
01949                             add_assoc_zval(zout, "cert", zcert);
01950                      }
01951                      BIO_free(bio_out);
01952 
01953                      bio_out = BIO_new(BIO_s_mem());
01954                      if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
01955                             BUF_MEM *bio_buf;
01956                             BIO_get_mem_ptr(bio_out, &bio_buf);
01957                             MAKE_STD_ZVAL(zpkey);
01958                             ZVAL_STRINGL(zpkey, bio_buf->data, bio_buf->length, 1);
01959                             add_assoc_zval(zout, "pkey", zpkey);
01960                      }
01961                      BIO_free(bio_out);
01962 
01963                      MAKE_STD_ZVAL(zextracerts);
01964                      array_init(zextracerts);
01965                      
01966                      for (i=0;;i++) {
01967                             zval * zextracert;
01968                             X509* aCA = sk_X509_pop(ca);
01969                             if (!aCA) break;
01970                             
01971                             bio_out = BIO_new(BIO_s_mem());
01972                             if (PEM_write_bio_X509(bio_out, aCA)) {
01973                                    BUF_MEM *bio_buf;
01974                                    BIO_get_mem_ptr(bio_out, &bio_buf);
01975                                    MAKE_STD_ZVAL(zextracert);
01976                                    ZVAL_STRINGL(zextracert, bio_buf->data, bio_buf->length, 1);
01977                                    add_index_zval(zextracerts, i, zextracert);
01978                                    
01979                             }
01980                             BIO_free(bio_out);
01981 
01982                             X509_free(aCA);
01983                      }
01984                      if(ca) {
01985                             sk_X509_free(ca);
01986                             add_assoc_zval(zout, "extracerts", zextracerts);
01987                      } else {
01988                             zval_dtor(zextracerts);
01989                      }
01990                      
01991                      RETVAL_TRUE;
01992                      
01993                      PKCS12_free(p12);
01994               }
01995        }
01996        
01997   cleanup:
01998        if (bio_in) {
01999               BIO_free(bio_in);
02000        }
02001        if (pkey) {
02002               EVP_PKEY_free(pkey);
02003        }
02004        if (cert) { 
02005               X509_free(cert);
02006        }
02007 }
02008 /* }}} */
02009 
02010 /* {{{ x509 CSR functions */
02011 
02012 /* {{{ php_openssl_make_REQ */
02013 static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs TSRMLS_DC)
02014 {
02015        STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
02016        char * str, *dn_sect, *attr_sect;
02017 
02018        dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name");
02019        if (dn_sect == NULL) {
02020               return FAILURE;
02021        }
02022        dn_sk = CONF_get_section(req->req_config, dn_sect);
02023        if (dn_sk == NULL) { 
02024               return FAILURE;
02025        }
02026        attr_sect = CONF_get_string(req->req_config, req->section_name, "attributes");
02027        if (attr_sect == NULL) {
02028               attr_sk = NULL;
02029        } else {
02030               attr_sk = CONF_get_section(req->req_config, attr_sect);
02031               if (attr_sk == NULL) {
02032                      return FAILURE;
02033               }
02034        }
02035        /* setup the version number: version 1 */
02036        if (X509_REQ_set_version(csr, 0L)) {
02037               int i, nid;
02038               char * type;
02039               CONF_VALUE * v;
02040               X509_NAME * subj;
02041               HashPosition hpos;
02042               zval ** item;
02043               
02044               subj = X509_REQ_get_subject_name(csr);
02045               /* apply values from the dn hash */
02046               zend_hash_internal_pointer_reset_ex(HASH_OF(dn), &hpos);
02047               while(zend_hash_get_current_data_ex(HASH_OF(dn), (void**)&item, &hpos) == SUCCESS) {
02048                      char * strindex = NULL; 
02049                      uint strindexlen = 0;
02050                      ulong intindex;
02051                      
02052                      zend_hash_get_current_key_ex(HASH_OF(dn), &strindex, &strindexlen, &intindex, 0, &hpos);
02053 
02054                      convert_to_string_ex(item);
02055 
02056                      if (strindex) {
02057                             int nid;
02058 
02059                             nid = OBJ_txt2nid(strindex);
02060                             if (nid != NID_undef) {
02061                                    if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC, 
02062                                                         (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0))
02063                                    {
02064                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item));
02065                                           return FAILURE;
02066                                    }
02067                             } else {
02068                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex);
02069                             }
02070                      }
02071                      zend_hash_move_forward_ex(HASH_OF(dn), &hpos);
02072               }
02073 
02074               /* Finally apply defaults from config file */
02075               for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
02076                      int len;
02077                      char buffer[200 + 1]; /*200 + \0 !*/
02078                      
02079                      v = sk_CONF_VALUE_value(dn_sk, i);
02080                      type = v->name;
02081                      
02082                      len = strlen(type);
02083                      if (len < sizeof("_default")) {
02084                             continue;
02085                      }
02086                      len -= sizeof("_default") - 1;
02087                      if (strcmp("_default", type + len) != 0) {
02088                             continue;
02089                      }
02090                      if (len > 200) {
02091                             len = 200;
02092                      }
02093                      memcpy(buffer, type, len);
02094                      buffer[len] = '\0';
02095                      type = buffer;
02096               
02097                      /* Skip past any leading X. X: X, etc to allow for multiple
02098                       * instances */
02099                      for (str = type; *str; str++) {
02100                             if (*str == ':' || *str == ',' || *str == '.') {
02101                                    str++;
02102                                    if (*str) {
02103                                           type = str;
02104                                    }
02105                                    break;
02106                             }
02107                      }
02108                      /* if it is already set, skip this */
02109                      nid = OBJ_txt2nid(type);
02110                      if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) {
02111                             continue;
02112                      }
02113                      if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_ASC, (unsigned char*)v->value, -1, -1, 0)) {
02114                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
02115                             return FAILURE;
02116                      }
02117                      if (!X509_NAME_entry_count(subj)) {
02118                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "no objects specified in config file");
02119                             return FAILURE;
02120                      }
02121               }
02122               if (attribs) {
02123                      zend_hash_internal_pointer_reset_ex(HASH_OF(attribs), &hpos);
02124                      while(zend_hash_get_current_data_ex(HASH_OF(attribs), (void**)&item, &hpos) == SUCCESS) {
02125                             char *strindex = NULL;
02126                             uint strindexlen;
02127                             ulong intindex;
02128 
02129                             zend_hash_get_current_key_ex(HASH_OF(attribs), &strindex, &strindexlen, &intindex, 0, &hpos);
02130                             convert_to_string_ex(item);
02131 
02132                             if (strindex) {
02133                                    int nid;
02134 
02135                                    nid = OBJ_txt2nid(strindex);
02136                                    if (nid != NID_undef) {
02137                                           if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC, (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0)) {
02138                                                  php_error_docref(NULL TSRMLS_CC, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item));
02139                                                  return FAILURE;
02140                                           }
02141                                    } else {
02142                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex);
02143                                    }
02144                             }
02145                             zend_hash_move_forward_ex(HASH_OF(attribs), &hpos);
02146                      }
02147                      for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
02148                             v = sk_CONF_VALUE_value(attr_sk, i);
02149                             /* if it is already set, skip this */
02150                             nid = OBJ_txt2nid(v->name);
02151                             if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) {
02152                                    continue;
02153                             }
02154                             if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_ASC, (unsigned char*)v->value, -1)) {
02155                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "add1_attr_by_txt %s -> %s (failed)", v->name, v->value);
02156                                    return FAILURE;
02157                             }
02158                      }
02159               }
02160        }
02161 
02162        X509_REQ_set_pubkey(csr, req->priv_key);
02163        return SUCCESS;
02164 }
02165 /* }}} */
02166 
02167 /* {{{ php_openssl_csr_from_zval */
02168 static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC)
02169 {
02170        X509_REQ * csr = NULL;
02171        char * filename = NULL;
02172        BIO * in;
02173        
02174        if (resourceval) {
02175               *resourceval = -1;
02176        }
02177        if (Z_TYPE_PP(val) == IS_RESOURCE) {
02178               void * what;
02179               int type;
02180 
02181               what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509 CSR", &type, 1, le_csr);
02182               if (what) {
02183                      if (resourceval) {
02184                             *resourceval = Z_LVAL_PP(val);
02185                      }
02186                      return (X509_REQ*)what;
02187               }
02188               return NULL;
02189        } else if (Z_TYPE_PP(val) != IS_STRING) {
02190               return NULL;
02191        }
02192 
02193        if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
02194               filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
02195        }
02196        if (filename) {
02197               if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
02198                      return NULL;
02199               }
02200               in = BIO_new_file(filename, "r");
02201        } else {
02202               in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
02203        }
02204        csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
02205        BIO_free(in);
02206 
02207        return csr;
02208 }
02209 /* }}} */
02210 
02211 /* {{{ proto bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true])
02212    Exports a CSR to file */
02213 PHP_FUNCTION(openssl_csr_export_to_file)
02214 {
02215        X509_REQ * csr;
02216        zval * zcsr = NULL;
02217        zend_bool notext = 1;
02218        char * filename = NULL; int filename_len;
02219        BIO * bio_out;
02220        long csr_resource;
02221 
02222        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|b", &zcsr, &filename, &filename_len, &notext) == FAILURE) {
02223               return;
02224        }
02225        RETVAL_FALSE;
02226 
02227        if (strlen(filename) != filename_len) {
02228               return;
02229        }
02230 
02231        csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC);
02232        if (csr == NULL) {
02233               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
02234               return;
02235        }
02236 
02237        if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
02238               return;
02239        }
02240 
02241        bio_out = BIO_new_file(filename, "w");
02242        if (bio_out) {
02243               if (!notext) {
02244                      X509_REQ_print(bio_out, csr);
02245               }
02246               PEM_write_bio_X509_REQ(bio_out, csr);
02247               RETVAL_TRUE;
02248        } else {
02249               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename);
02250        }
02251 
02252        if (csr_resource == -1 && csr) {
02253               X509_REQ_free(csr);
02254        }
02255        BIO_free(bio_out);
02256 }
02257 /* }}} */
02258 
02259 /* {{{ proto bool openssl_csr_export(resource csr, string &out [, bool notext=true])
02260    Exports a CSR to file or a var */
02261 PHP_FUNCTION(openssl_csr_export)
02262 {
02263        X509_REQ * csr;
02264        zval * zcsr = NULL, *zout=NULL;
02265        zend_bool notext = 1;
02266        BIO * bio_out;
02267 
02268        long csr_resource;
02269 
02270        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|b", &zcsr, &zout, &notext) == FAILURE) {
02271               return;
02272        }
02273        RETVAL_FALSE;
02274 
02275        csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC);
02276        if (csr == NULL) {
02277               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
02278               return;
02279        }
02280 
02281        /* export to a var */
02282 
02283        bio_out = BIO_new(BIO_s_mem());
02284        if (!notext) {
02285               X509_REQ_print(bio_out, csr);
02286        }
02287 
02288        if (PEM_write_bio_X509_REQ(bio_out, csr)) {
02289               BUF_MEM *bio_buf;
02290 
02291               BIO_get_mem_ptr(bio_out, &bio_buf);
02292               zval_dtor(zout);
02293               ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1);
02294 
02295               RETVAL_TRUE;
02296        }
02297 
02298        if (csr_resource == -1 && csr) {
02299               X509_REQ_free(csr);
02300        }
02301        BIO_free(bio_out);
02302 }
02303 /* }}} */
02304 
02305 /* {{{ proto resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]])
02306    Signs a cert with another CERT */
02307 PHP_FUNCTION(openssl_csr_sign)
02308 {
02309        zval ** zcert = NULL, **zcsr, **zpkey, *args = NULL;
02310        long num_days;
02311        long serial = 0L;
02312        X509 * cert = NULL, *new_cert = NULL;
02313        X509_REQ * csr;
02314        EVP_PKEY * key = NULL, *priv_key = NULL;
02315        long csr_resource, certresource = 0, keyresource = -1;
02316        int i;
02317        struct php_x509_request req;
02318        
02319        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ!Zl|a!l", &zcsr, &zcert, &zpkey, &num_days, &args, &serial) == FAILURE)
02320               return;
02321 
02322        RETVAL_FALSE;
02323        PHP_SSL_REQ_INIT(&req);
02324        
02325        csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
02326        if (csr == NULL) {
02327               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
02328               return;
02329        }
02330        if (zcert) {
02331               cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
02332               if (cert == NULL) {
02333                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 2");
02334                      goto cleanup;
02335               }
02336        }
02337        priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 1, &keyresource TSRMLS_CC);
02338        if (priv_key == NULL) {
02339               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
02340               goto cleanup;
02341        }
02342        if (cert && !X509_check_private_key(cert, priv_key)) {
02343               php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to signing cert");
02344               goto cleanup;
02345        }
02346        
02347        if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) {
02348               goto cleanup;
02349        }
02350        /* Check that the request matches the signature */
02351        key = X509_REQ_get_pubkey(csr);
02352        if (key == NULL) {
02353               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error unpacking public key");
02354               goto cleanup;
02355        }
02356        i = X509_REQ_verify(csr, key);
02357 
02358        if (i < 0) {
02359               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature verification problems");
02360               goto cleanup;
02361        }
02362        else if (i == 0) {
02363               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature did not match the certificate request");
02364               goto cleanup;
02365        }
02366        
02367        /* Now we can get on with it */
02368        
02369        new_cert = X509_new();
02370        if (new_cert == NULL) {
02371               php_error_docref(NULL TSRMLS_CC, E_WARNING, "No memory");
02372               goto cleanup;
02373        }
02374        /* Version 3 cert */
02375        if (!X509_set_version(new_cert, 2))
02376               goto cleanup;
02377 
02378        ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial);
02379        
02380        X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr));
02381 
02382        if (cert == NULL) {
02383               cert = new_cert;
02384        }
02385        if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
02386               goto cleanup;
02387        }
02388        X509_gmtime_adj(X509_get_notBefore(new_cert), 0);
02389        X509_gmtime_adj(X509_get_notAfter(new_cert), (long)60*60*24*num_days);
02390        i = X509_set_pubkey(new_cert, key);
02391        if (!i) {
02392               goto cleanup;
02393        }
02394        if (req.extensions_section) {
02395               X509V3_CTX ctx;
02396               
02397               X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
02398               X509V3_set_conf_lhash(&ctx, req.req_config);
02399               if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) {
02400                      goto cleanup;
02401               }
02402        }
02403 
02404        /* Now sign it */
02405        if (!X509_sign(new_cert, priv_key, req.digest)) {
02406               php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to sign it");
02407               goto cleanup;
02408        }
02409        
02410        /* Succeeded; lets return the cert */
02411        RETVAL_RESOURCE(zend_list_insert(new_cert, le_x509));
02412        new_cert = NULL;
02413        
02414 cleanup:
02415 
02416        if (cert == new_cert) {
02417               cert = NULL;
02418        }
02419        PHP_SSL_REQ_DISPOSE(&req);
02420 
02421        if (keyresource == -1 && priv_key) {
02422               EVP_PKEY_free(priv_key);
02423        }
02424        if (key) {
02425               EVP_PKEY_free(key);
02426        }
02427        if (csr_resource == -1 && csr) {
02428               X509_REQ_free(csr);
02429        }
02430        if (certresource == -1 && cert) { 
02431               X509_free(cert);
02432        }
02433        if (new_cert) {
02434               X509_free(new_cert);
02435        }
02436 }
02437 /* }}} */
02438 
02439 /* {{{ proto bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]])
02440    Generates a privkey and CSR */
02441 PHP_FUNCTION(openssl_csr_new)
02442 {
02443        struct php_x509_request req;
02444        zval * args = NULL, * dn, *attribs = NULL;
02445        zval * out_pkey;
02446        X509_REQ * csr = NULL;
02447        int we_made_the_key = 1;
02448        long key_resource;
02449        
02450        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) {
02451               return;
02452        }
02453        RETVAL_FALSE;
02454        
02455        PHP_SSL_REQ_INIT(&req);
02456 
02457        if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
02458               /* Generate or use a private key */
02459               if (Z_TYPE_P(out_pkey) != IS_NULL) {
02460                      req.priv_key = php_openssl_evp_from_zval(&out_pkey, 0, NULL, 0, &key_resource TSRMLS_CC);
02461                      if (req.priv_key != NULL) {
02462                             we_made_the_key = 0;
02463                      }
02464               }
02465               if (req.priv_key == NULL) {
02466                      php_openssl_generate_private_key(&req TSRMLS_CC);
02467               }
02468               if (req.priv_key == NULL) {
02469                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to generate a private key");
02470               } else {
02471                      csr = X509_REQ_new();
02472                      if (csr) {
02473                             if (php_openssl_make_REQ(&req, csr, dn, attribs TSRMLS_CC) == SUCCESS) {
02474                                    X509V3_CTX ext_ctx;
02475 
02476                                    X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0);
02477                                    X509V3_set_conf_lhash(&ext_ctx, req.req_config);
02478 
02479                                    /* Add extensions */
02480                                    if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
02481                                                         &ext_ctx, req.request_extensions_section, csr))
02482                                    {
02483                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
02484                                    } else {
02485                                           RETVAL_TRUE;
02486                                           
02487                                           if (X509_REQ_sign(csr, req.priv_key, req.digest)) {
02488                                                  RETVAL_RESOURCE(zend_list_insert(csr, le_csr));
02489                                                  csr = NULL;                 
02490                                           } else {
02491                                                  php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error signing request");
02492                                           }
02493 
02494                                           if (we_made_the_key) {
02495                                                  /* and a resource for the private key */
02496                                                  zval_dtor(out_pkey);
02497                                                  ZVAL_RESOURCE(out_pkey, zend_list_insert(req.priv_key, le_key));
02498                                                  req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
02499                                           } else if (key_resource != -1) {
02500                                                  req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
02501                                           }
02502                                    }
02503                             }
02504                             else {
02505                                    if (!we_made_the_key) {
02506                                           /* if we have not made the key we are not supposed to zap it by calling dispose! */
02507                                           req.priv_key = NULL;
02508                                    }
02509                             }
02510                      }
02511               }
02512        }
02513        if (csr) {
02514               X509_REQ_free(csr);
02515        }
02516        PHP_SSL_REQ_DISPOSE(&req);
02517 }
02518 /* }}} */
02519 
02520 /* {{{ proto mixed openssl_csr_get_subject(mixed csr)
02521    Returns the subject of a CERT or FALSE on error */
02522 PHP_FUNCTION(openssl_csr_get_subject)
02523 {
02524        zval ** zcsr;
02525        zend_bool use_shortnames = 1;
02526        long csr_resource;
02527        X509_NAME * subject;
02528        X509_REQ * csr;
02529 
02530        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) {
02531               return;
02532        }
02533 
02534        csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
02535 
02536        if (csr == NULL) {
02537               RETURN_FALSE;
02538        }
02539 
02540        subject = X509_REQ_get_subject_name(csr);
02541 
02542        array_init(return_value);
02543        add_assoc_name_entry(return_value, NULL, subject, use_shortnames TSRMLS_CC);
02544        return;
02545 }
02546 /* }}} */
02547 
02548 /* {{{ proto mixed openssl_csr_get_public_key(mixed csr)
02549        Returns the subject of a CERT or FALSE on error */
02550 PHP_FUNCTION(openssl_csr_get_public_key)
02551 {
02552        zval ** zcsr;
02553        zend_bool use_shortnames = 1;
02554        long csr_resource;
02555 
02556        X509_REQ * csr;
02557        EVP_PKEY *tpubkey;
02558 
02559        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) {
02560               return;
02561        }
02562 
02563        csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
02564 
02565        if (csr == NULL) {
02566               RETURN_FALSE;
02567        }
02568 
02569        tpubkey=X509_REQ_get_pubkey(csr);
02570        RETVAL_RESOURCE(zend_list_insert(tpubkey, le_key));
02571        return;
02572 }
02573 /* }}} */
02574 
02575 /* }}} */
02576 
02577 /* {{{ EVP Public/Private key functions */
02578 
02579 /* {{{ php_openssl_evp_from_zval
02580    Given a zval, coerce it into a EVP_PKEY object.
02581        It can be:
02582               1. private key resource from openssl_get_privatekey()
02583               2. X509 resource -> public key will be extracted from it
02584               3. if it starts with file:// interpreted as path to key file
02585               4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey()
02586               5. an array(0 => [items 2..4], 1 => passphrase)
02587               6. if val is a string (possibly starting with file:///) and it is not an X509 certificate, then interpret as public key
02588        NOTE: If you are requesting a private key but have not specified a passphrase, you should use an
02589        empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in
02590        the Apache error log!
02591 */
02592 static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC)
02593 {
02594        EVP_PKEY * key = NULL;
02595        X509 * cert = NULL;
02596        int free_cert = 0;
02597        long cert_res = -1;
02598        char * filename = NULL;
02599        zval tmp;
02600 
02601        Z_TYPE(tmp) = IS_NULL;
02602 
02603 #define TMP_CLEAN \
02604        if (Z_TYPE(tmp) == IS_STRING) {\
02605               zval_dtor(&tmp); \
02606        } \
02607        return NULL;
02608 
02609        if (resourceval) {
02610               *resourceval = -1;
02611        }
02612        if (Z_TYPE_PP(val) == IS_ARRAY) {
02613               zval ** zphrase;
02614               
02615               /* get passphrase */
02616 
02617               if (zend_hash_index_find(HASH_OF(*val), 1, (void **)&zphrase) == FAILURE) {
02618                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
02619                      return NULL;
02620               }
02621               
02622               if (Z_TYPE_PP(zphrase) == IS_STRING) {
02623                      passphrase = Z_STRVAL_PP(zphrase);
02624               } else {
02625                      tmp = **zphrase;
02626                      zval_copy_ctor(&tmp);
02627                      convert_to_string(&tmp);
02628                      passphrase = Z_STRVAL(tmp);
02629               }
02630 
02631               /* now set val to be the key param and continue */
02632               if (zend_hash_index_find(HASH_OF(*val), 0, (void **)&val) == FAILURE) {
02633                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
02634                      TMP_CLEAN;
02635               }
02636        }
02637 
02638        if (Z_TYPE_PP(val) == IS_RESOURCE) {
02639               void * what;
02640               int type;
02641 
02642               what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key);
02643               if (!what) {
02644                      TMP_CLEAN;
02645               }
02646               if (resourceval) { 
02647                      *resourceval = Z_LVAL_PP(val);
02648               }
02649               if (type == le_x509) {
02650                      /* extract key from cert, depending on public_key param */
02651                      cert = (X509*)what;
02652                      free_cert = 0;
02653               } else if (type == le_key) {
02654                      int is_priv;
02655 
02656                      is_priv = php_openssl_is_private_key((EVP_PKEY*)what TSRMLS_CC);
02657 
02658                      /* check whether it is actually a private key if requested */
02659                      if (!public_key && !is_priv) {
02660                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param is a public key");
02661                             TMP_CLEAN;
02662                      }
02663 
02664                      if (public_key && is_priv) {
02665                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Don't know how to get public key from this private key");
02666                             TMP_CLEAN;
02667                      } else {
02668                             if (Z_TYPE(tmp) == IS_STRING) {
02669                                    zval_dtor(&tmp);
02670                             }
02671                             /* got the key - return it */
02672                             return (EVP_PKEY*)what;
02673                      }
02674               } else {
02675                      /* other types could be used here - eg: file pointers and read in the data from them */
02676                      TMP_CLEAN;
02677               }
02678        } else {
02679               /* force it to be a string and check if it refers to a file */
02680               /* passing non string values leaks, object uses toString, it returns NULL 
02681                * See bug38255.phpt 
02682                */
02683               if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) {
02684                      TMP_CLEAN;
02685               }
02686               convert_to_string_ex(val);
02687 
02688               if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
02689                      filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
02690               }
02691               /* it's an X509 file/cert of some kind, and we need to extract the data from that */
02692               if (public_key) {
02693                      cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC);
02694                      free_cert = (cert_res == -1);
02695                      /* actual extraction done later */
02696                      if (!cert) {
02697                             /* not a X509 certificate, try to retrieve public key */
02698                             BIO* in;
02699                             if (filename) {
02700                                    in = BIO_new_file(filename, "r");
02701                             } else {
02702                                    in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
02703                             }
02704                             if (in == NULL) {
02705                                    TMP_CLEAN;
02706                             }
02707                             key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
02708                             BIO_free(in);
02709                      }
02710               } else {
02711                      /* we want the private key */
02712                      BIO *in;
02713 
02714                      if (filename) {
02715                             if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
02716                                    TMP_CLEAN;
02717                             }
02718                             in = BIO_new_file(filename, "r");
02719                      } else {
02720                             in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
02721                      }
02722 
02723                      if (in == NULL) {
02724                             TMP_CLEAN;
02725                      }
02726                      key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);
02727                      BIO_free(in);
02728               }
02729        }
02730 
02731        if (public_key && cert && key == NULL) {
02732               /* extract public key from X509 cert */
02733               key = (EVP_PKEY *) X509_get_pubkey(cert);
02734        }
02735 
02736        if (free_cert && cert) {
02737               X509_free(cert);
02738        }
02739        if (key && makeresource && resourceval) {
02740               *resourceval = ZEND_REGISTER_RESOURCE(NULL, key, le_key);
02741        }
02742        if (Z_TYPE(tmp) == IS_STRING) {
02743               zval_dtor(&tmp);
02744        }
02745        return key;
02746 }
02747 /* }}} */
02748 
02749 /* {{{ php_openssl_generate_private_key */
02750 static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC)
02751 {
02752        char * randfile = NULL;
02753        int egdsocket, seeded;
02754        EVP_PKEY * return_val = NULL;
02755        
02756        if (req->priv_key_bits < MIN_KEY_LENGTH) {
02757               php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key length is too short; it needs to be at least %d bits, not %d",
02758                             MIN_KEY_LENGTH, req->priv_key_bits);
02759               return NULL;
02760        }
02761 
02762        randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE");
02763        php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
02764        
02765        if ((req->priv_key = EVP_PKEY_new()) != NULL) {
02766               switch(req->priv_key_type) {
02767                      case OPENSSL_KEYTYPE_RSA:
02768                             if (EVP_PKEY_assign_RSA(req->priv_key, RSA_generate_key(req->priv_key_bits, 0x10001, NULL, NULL))) {
02769                                    return_val = req->priv_key;
02770                             }
02771                             break;
02772 #if !defined(NO_DSA) && defined(HAVE_DSA_DEFAULT_METHOD)
02773                      case OPENSSL_KEYTYPE_DSA:
02774                             {
02775                                    DSA *dsapar = DSA_generate_parameters(req->priv_key_bits, NULL, 0, NULL, NULL, NULL, NULL);
02776                                    if (dsapar) {
02777                                           DSA_set_method(dsapar, DSA_get_default_method());
02778                                           if (DSA_generate_key(dsapar)) {
02779                                                  if (EVP_PKEY_assign_DSA(req->priv_key, dsapar)) {
02780                                                         return_val = req->priv_key;
02781                                                  }
02782                                           } else {
02783                                                  DSA_free(dsapar);
02784                                           }
02785                                    }
02786                             }
02787                             break;
02788 #endif
02789 #if !defined(NO_DH)
02790                      case OPENSSL_KEYTYPE_DH:
02791                             {
02792                                    DH *dhpar = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL);
02793                                    int codes = 0;
02794 
02795                                    if (dhpar) {
02796                                           DH_set_method(dhpar, DH_get_default_method());
02797                                           if (DH_check(dhpar, &codes) && codes == 0 && DH_generate_key(dhpar)) {
02798                                                  if (EVP_PKEY_assign_DH(req->priv_key, dhpar)) {
02799                                                         return_val = req->priv_key;
02800                                                  }
02801                                           } else {
02802                                                  DH_free(dhpar);
02803                                           }
02804                                    }
02805                             }
02806                             break;
02807 #endif
02808                      default:
02809                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported private key type");
02810               }
02811        }
02812 
02813        php_openssl_write_rand_file(randfile, egdsocket, seeded);
02814        
02815        if (return_val == NULL) {
02816               EVP_PKEY_free(req->priv_key);
02817               req->priv_key = NULL;
02818               return NULL;
02819        }
02820        
02821        return return_val;
02822 }
02823 /* }}} */
02824 
02825 /* {{{ php_openssl_is_private_key
02826        Check whether the supplied key is a private key by checking if the secret prime factors are set */
02827 static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC)
02828 {
02829        assert(pkey != NULL);
02830 
02831        switch (pkey->type) {
02832 #ifndef NO_RSA
02833               case EVP_PKEY_RSA:
02834               case EVP_PKEY_RSA2:
02835                      assert(pkey->pkey.rsa != NULL);
02836                      if (pkey->pkey.rsa != NULL && (NULL == pkey->pkey.rsa->p || NULL == pkey->pkey.rsa->q)) {
02837                             return 0;
02838                      }
02839                      break;
02840 #endif
02841 #ifndef NO_DSA
02842               case EVP_PKEY_DSA:
02843               case EVP_PKEY_DSA1:
02844               case EVP_PKEY_DSA2:
02845               case EVP_PKEY_DSA3:
02846               case EVP_PKEY_DSA4:
02847                      assert(pkey->pkey.dsa != NULL);
02848 
02849                      if (NULL == pkey->pkey.dsa->p || NULL == pkey->pkey.dsa->q || NULL == pkey->pkey.dsa->priv_key){ 
02850                             return 0;
02851                      }
02852                      break;
02853 #endif
02854 #ifndef NO_DH
02855               case EVP_PKEY_DH:
02856                      assert(pkey->pkey.dh != NULL);
02857 
02858                      if (NULL == pkey->pkey.dh->p || NULL == pkey->pkey.dh->priv_key) {
02859                             return 0;
02860                      }
02861                      break;
02862 #endif
02863               default:
02864                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
02865                      break;
02866        }
02867        return 1;
02868 }
02869 /* }}} */
02870 
02871 #define OPENSSL_PKEY_GET_BN(_type, _name) do {                                             \
02872               if (pkey->pkey._type->_name != NULL) {                                              \
02873                      int len = BN_num_bytes(pkey->pkey._type->_name);               \
02874                      char *str = emalloc(len + 1);                                                       \
02875                      BN_bn2bin(pkey->pkey._type->_name, (unsigned char*)str);       \
02876                      str[len] = 0;                                                  \
02877                      add_assoc_stringl(_type, #_name, str, len, 0);                        \
02878               }                                                                                                               \
02879        } while (0)
02880 
02881 #define OPENSSL_PKEY_SET_BN(_ht, _type, _name) do {                                        \
02882               zval **bn;                                                                                               \
02883               if (zend_hash_find(_ht, #_name, sizeof(#_name),  (void**)&bn) == SUCCESS && \
02884                             Z_TYPE_PP(bn) == IS_STRING) {                                                \
02885                      _type->_name = BN_bin2bn(                                                           \
02886                             (unsigned char*)Z_STRVAL_PP(bn),                                      \
02887                             Z_STRLEN_PP(bn), NULL);                                                             \
02888            }                                                               \
02889        } while (0);
02890 
02891 
02892 /* {{{ proto resource openssl_pkey_new([array configargs])
02893    Generates a new private key */
02894 PHP_FUNCTION(openssl_pkey_new)
02895 {
02896        struct php_x509_request req;
02897        zval * args = NULL;
02898        zval **data;
02899 
02900        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &args) == FAILURE) {
02901               return;
02902        }
02903        RETVAL_FALSE;
02904 
02905        if (args && Z_TYPE_P(args) == IS_ARRAY) {
02906               EVP_PKEY *pkey;
02907 
02908               if (zend_hash_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa"), (void**)&data) == SUCCESS &&
02909                   Z_TYPE_PP(data) == IS_ARRAY) {
02910                   pkey = EVP_PKEY_new();
02911                   if (pkey) {
02912                             RSA *rsa = RSA_new();
02913                             if (rsa) {
02914                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, n);
02915                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, e);
02916                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, d);
02917                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, p);
02918                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, q);
02919                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmp1);
02920                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmq1);
02921                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, iqmp);
02922                                    if (rsa->n && rsa->d) {
02923                                           if (EVP_PKEY_assign_RSA(pkey, rsa)) {
02924                                                  RETURN_RESOURCE(zend_list_insert(pkey, le_key));
02925                                           }
02926                                    }
02927                                    RSA_free(rsa);
02928                             }
02929                             EVP_PKEY_free(pkey);
02930                      }
02931                      RETURN_FALSE;
02932               } else if (zend_hash_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa"), (void**)&data) == SUCCESS &&
02933                          Z_TYPE_PP(data) == IS_ARRAY) {
02934                   pkey = EVP_PKEY_new();
02935                   if (pkey) {
02936                             DSA *dsa = DSA_new();
02937                             if (dsa) {
02938                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, p);
02939                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, q);
02940                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, g);
02941                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, priv_key);
02942                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, pub_key);
02943                                    if (dsa->p && dsa->q && dsa->g) {
02944                                           if (!dsa->priv_key && !dsa->pub_key) {
02945                                                  DSA_generate_key(dsa);
02946                                           }
02947                                           if (EVP_PKEY_assign_DSA(pkey, dsa)) {
02948                                                  RETURN_RESOURCE(zend_list_insert(pkey, le_key));
02949                                           }
02950                                    }
02951                                    DSA_free(dsa);
02952                             }
02953                             EVP_PKEY_free(pkey);
02954                      }
02955                      RETURN_FALSE;
02956               } else if (zend_hash_find(Z_ARRVAL_P(args), "dh", sizeof("dh"), (void**)&data) == SUCCESS &&
02957                          Z_TYPE_PP(data) == IS_ARRAY) {
02958                   pkey = EVP_PKEY_new();
02959                   if (pkey) {
02960                             DH *dh = DH_new();
02961                             if (dh) {
02962                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, p);
02963                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, g);
02964                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, priv_key);
02965                                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, pub_key);
02966                                    if (dh->p && dh->g) {
02967                                           if (!dh->pub_key) {
02968                                                  DH_generate_key(dh);
02969                                           }
02970                                           if (EVP_PKEY_assign_DH(pkey, dh)) {
02971                                                  RETURN_RESOURCE(zend_list_insert(pkey, le_key));
02972                                           }
02973                                    }
02974                                    DH_free(dh);
02975                             }
02976                             EVP_PKEY_free(pkey);
02977                      }
02978                      RETURN_FALSE;
02979               }
02980        } 
02981 
02982        PHP_SSL_REQ_INIT(&req);
02983 
02984        if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS)
02985        {
02986               if (php_openssl_generate_private_key(&req TSRMLS_CC)) {
02987                      /* pass back a key resource */
02988                      RETVAL_RESOURCE(zend_list_insert(req.priv_key, le_key));
02989                      /* make sure the cleanup code doesn't zap it! */
02990                      req.priv_key = NULL;
02991               }
02992        }
02993        PHP_SSL_REQ_DISPOSE(&req);
02994 }
02995 /* }}} */
02996 
02997 /* {{{ proto bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args)
02998    Gets an exportable representation of a key into a file */
02999 PHP_FUNCTION(openssl_pkey_export_to_file)
03000 {
03001        struct php_x509_request req;
03002        zval ** zpkey, * args = NULL;
03003        char * passphrase = NULL; int passphrase_len = 0;
03004        char * filename = NULL; int filename_len = 0;
03005        long key_resource = -1;
03006        EVP_PKEY * key;
03007        BIO * bio_out = NULL;
03008        const EVP_CIPHER * cipher;
03009        
03010        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) {
03011               return;
03012        }
03013        RETVAL_FALSE;
03014 
03015        if (strlen(filename) != filename_len) {
03016               return;
03017        }
03018 
03019        key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC);
03020 
03021        if (key == NULL) {
03022               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1");
03023               RETURN_FALSE;
03024        }
03025        
03026        if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
03027               RETURN_FALSE;
03028        }
03029        
03030        PHP_SSL_REQ_INIT(&req);
03031 
03032        if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
03033               bio_out = BIO_new_file(filename, "w");
03034 
03035               if (passphrase && req.priv_key_encrypt) {
03036                      cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
03037               } else {
03038                      cipher = NULL;
03039               }
03040               if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) {
03041                      /* Success!
03042                       * If returning the output as a string, do so now */
03043                      RETVAL_TRUE;
03044               }
03045        }
03046        PHP_SSL_REQ_DISPOSE(&req);
03047 
03048        if (key_resource == -1 && key) {
03049               EVP_PKEY_free(key);
03050        }
03051        if (bio_out) {
03052               BIO_free(bio_out);
03053        }
03054 }
03055 /* }}} */
03056 
03057 /* {{{ proto bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]])
03058    Gets an exportable representation of a key into a string or file */
03059 PHP_FUNCTION(openssl_pkey_export)
03060 {
03061        struct php_x509_request req;
03062        zval ** zpkey, * args = NULL, *out;
03063        char * passphrase = NULL; int passphrase_len = 0;
03064        long key_resource = -1;
03065        EVP_PKEY * key;
03066        BIO * bio_out = NULL;
03067        const EVP_CIPHER * cipher;
03068        
03069        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) {
03070               return;
03071        }
03072        RETVAL_FALSE;
03073 
03074        key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC);
03075 
03076        if (key == NULL) {
03077               php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1");
03078               RETURN_FALSE;
03079        }
03080        
03081        PHP_SSL_REQ_INIT(&req);
03082 
03083        if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
03084               bio_out = BIO_new(BIO_s_mem());
03085 
03086               if (passphrase && req.priv_key_encrypt) {
03087                      cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
03088               } else {
03089                      cipher = NULL;
03090               }
03091               if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) {
03092                      /* Success!
03093                       * If returning the output as a string, do so now */
03094 
03095                      char * bio_mem_ptr;
03096                      long bio_mem_len;
03097                      RETVAL_TRUE;
03098 
03099                      bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
03100                      zval_dtor(out);
03101                      ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len, 1);
03102               }
03103        }
03104        PHP_SSL_REQ_DISPOSE(&req);
03105 
03106        if (key_resource == -1 && key) {
03107               EVP_PKEY_free(key);
03108        }
03109        if (bio_out) {
03110               BIO_free(bio_out);
03111        }
03112 }
03113 /* }}} */
03114 
03115 /* {{{ proto int openssl_pkey_get_public(mixed cert)
03116    Gets public key from X.509 certificate */
03117 PHP_FUNCTION(openssl_pkey_get_public)
03118 {
03119        zval **cert;
03120        EVP_PKEY *pkey;
03121 
03122        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) {
03123               return;
03124        }
03125        Z_TYPE_P(return_value) = IS_RESOURCE;
03126        pkey = php_openssl_evp_from_zval(cert, 1, NULL, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
03127 
03128        if (pkey == NULL) {
03129               RETURN_FALSE;
03130        }
03131 }
03132 /* }}} */
03133 
03134 /* {{{ proto void openssl_pkey_free(int key)
03135    Frees a key */
03136 PHP_FUNCTION(openssl_pkey_free)
03137 {
03138        zval *key;
03139        EVP_PKEY *pkey;
03140 
03141        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) {
03142               return;
03143        }
03144        ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
03145        zend_list_delete(Z_LVAL_P(key));
03146 }
03147 /* }}} */
03148 
03149 /* {{{ proto int openssl_pkey_get_private(string key [, string passphrase])
03150    Gets private keys */
03151 PHP_FUNCTION(openssl_pkey_get_private)
03152 {
03153        zval **cert;
03154        EVP_PKEY *pkey;
03155        char * passphrase = "";
03156        int passphrase_len = sizeof("")-1;
03157 
03158        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|s", &cert, &passphrase, &passphrase_len) == FAILURE) {
03159               return;
03160        }
03161        Z_TYPE_P(return_value) = IS_RESOURCE;
03162        pkey = php_openssl_evp_from_zval(cert, 0, passphrase, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
03163 
03164        if (pkey == NULL) {
03165               RETURN_FALSE;
03166        }
03167 }
03168 
03169 /* }}} */
03170 
03171 /* {{{ proto resource openssl_pkey_get_details(resource key)
03172        returns an array with the key details (bits, pkey, type)*/
03173 PHP_FUNCTION(openssl_pkey_get_details)
03174 {
03175        zval *key;
03176        EVP_PKEY *pkey;
03177        BIO *out;
03178        unsigned int pbio_len;
03179        char *pbio;
03180        long ktype;
03181 
03182        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) {
03183               return;
03184        }
03185        ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
03186        if (!pkey) {
03187               RETURN_FALSE;
03188        }
03189        out = BIO_new(BIO_s_mem());
03190        PEM_write_bio_PUBKEY(out, pkey);
03191        pbio_len = BIO_get_mem_data(out, &pbio);
03192 
03193        array_init(return_value);
03194        add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey));
03195        add_assoc_stringl(return_value, "key", pbio, pbio_len, 1);
03196        /*TODO: Use the real values once the openssl constants are used 
03197         * See the enum at the top of this file
03198         */
03199        switch (EVP_PKEY_type(pkey->type)) {
03200               case EVP_PKEY_RSA:
03201               case EVP_PKEY_RSA2:
03202                      ktype = OPENSSL_KEYTYPE_RSA;
03203 
03204                      if (pkey->pkey.rsa != NULL) {
03205                             zval *rsa;
03206 
03207                             ALLOC_INIT_ZVAL(rsa);
03208                             array_init(rsa);
03209                             OPENSSL_PKEY_GET_BN(rsa, n);
03210                             OPENSSL_PKEY_GET_BN(rsa, e);
03211                             OPENSSL_PKEY_GET_BN(rsa, d);
03212                             OPENSSL_PKEY_GET_BN(rsa, p);
03213                             OPENSSL_PKEY_GET_BN(rsa, q);
03214                             OPENSSL_PKEY_GET_BN(rsa, dmp1);
03215                             OPENSSL_PKEY_GET_BN(rsa, dmq1);
03216                             OPENSSL_PKEY_GET_BN(rsa, iqmp);
03217                             add_assoc_zval(return_value, "rsa", rsa);
03218                      }
03219 
03220                      break; 
03221               case EVP_PKEY_DSA:
03222               case EVP_PKEY_DSA2:
03223               case EVP_PKEY_DSA3:
03224               case EVP_PKEY_DSA4:
03225                      ktype = OPENSSL_KEYTYPE_DSA;
03226 
03227                      if (pkey->pkey.dsa != NULL) {
03228                             zval *dsa;
03229 
03230                             ALLOC_INIT_ZVAL(dsa);
03231                             array_init(dsa);
03232                             OPENSSL_PKEY_GET_BN(dsa, p);
03233                             OPENSSL_PKEY_GET_BN(dsa, q);
03234                             OPENSSL_PKEY_GET_BN(dsa, g);
03235                             OPENSSL_PKEY_GET_BN(dsa, priv_key);
03236                             OPENSSL_PKEY_GET_BN(dsa, pub_key);
03237                             add_assoc_zval(return_value, "dsa", dsa);
03238                      }
03239                      break;
03240               case EVP_PKEY_DH:
03241                      
03242                      ktype = OPENSSL_KEYTYPE_DH;
03243 
03244                      if (pkey->pkey.dh != NULL) {
03245                             zval *dh;
03246 
03247                             ALLOC_INIT_ZVAL(dh);
03248                             array_init(dh);
03249                             OPENSSL_PKEY_GET_BN(dh, p);
03250                             OPENSSL_PKEY_GET_BN(dh, g);
03251                             OPENSSL_PKEY_GET_BN(dh, priv_key);
03252                             OPENSSL_PKEY_GET_BN(dh, pub_key);
03253                             add_assoc_zval(return_value, "dh", dh);
03254                      }
03255 
03256                      break;
03257 #ifdef EVP_PKEY_EC 
03258               case EVP_PKEY_EC:
03259                      ktype = OPENSSL_KEYTYPE_EC;
03260                      break;
03261 #endif
03262               default:
03263                      ktype = -1;
03264                      break;
03265        }
03266        add_assoc_long(return_value, "type", ktype);
03267 
03268        BIO_free(out);
03269 }
03270 /* }}} */
03271 
03272 /* }}} */
03273 
03274 /* {{{ PKCS7 S/MIME functions */
03275 
03276 /* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
03277    Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */
03278 PHP_FUNCTION(openssl_pkcs7_verify)
03279 {
03280        X509_STORE * store = NULL;
03281        zval * cainfo = NULL;
03282        STACK_OF(X509) *signers= NULL;
03283        STACK_OF(X509) *others = NULL;
03284        PKCS7 * p7 = NULL;
03285        BIO * in = NULL, * datain = NULL, * dataout = NULL;
03286        long flags = 0;
03287        char * filename; int filename_len;
03288        char * extracerts = NULL; int extracerts_len = 0;
03289        char * signersfilename = NULL; int signersfilename_len = 0;
03290        char * datafilename = NULL; int datafilename_len = 0;
03291        
03292        RETVAL_LONG(-1);
03293 
03294        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|sass", &filename, &filename_len,
03295                             &flags, &signersfilename, &signersfilename_len, &cainfo,
03296                             &extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) {
03297               return;
03298        }
03299        
03300        if (extracerts) {
03301               others = load_all_certs_from_file(extracerts);
03302               if (others == NULL) {
03303                      goto clean_exit;
03304               }
03305        }
03306 
03307        flags = flags & ~PKCS7_DETACHED;
03308 
03309        store = setup_verify(cainfo TSRMLS_CC);
03310 
03311        if (!store) {
03312               goto clean_exit;
03313        }
03314        if (php_openssl_safe_mode_chk(filename TSRMLS_CC)) {
03315               goto clean_exit;
03316        }
03317 
03318        in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r");
03319        if (in == NULL) {
03320               goto clean_exit;
03321        }
03322        p7 = SMIME_read_PKCS7(in, &datain);
03323        if (p7 == NULL) {
03324 #if DEBUG_SMIME
03325               zend_printf("SMIME_read_PKCS7 failed\n");
03326 #endif
03327               goto clean_exit;
03328        }
03329 
03330        if (datafilename) {
03331 
03332               if (php_openssl_safe_mode_chk(datafilename TSRMLS_CC)) {
03333                      goto clean_exit;
03334               }
03335 
03336               dataout = BIO_new_file(datafilename, "w");
03337               if (dataout == NULL) {
03338                      goto clean_exit;
03339               }
03340        }
03341 #if DEBUG_SMIME
03342        zend_printf("Calling PKCS7 verify\n");
03343 #endif
03344 
03345        if (PKCS7_verify(p7, others, store, datain, dataout, flags)) {
03346 
03347               RETVAL_TRUE;
03348 
03349               if (signersfilename) {
03350                      BIO *certout;
03351               
03352                      if (php_openssl_safe_mode_chk(signersfilename TSRMLS_CC)) {
03353                             goto clean_exit;
03354                      }
03355               
03356                      certout = BIO_new_file(signersfilename, "w");
03357                      if (certout) {
03358                             int i;
03359                             signers = PKCS7_get0_signers(p7, NULL, flags);
03360 
03361                             for(i = 0; i < sk_X509_num(signers); i++) {
03362                                    PEM_write_bio_X509(certout, sk_X509_value(signers, i));
03363                             }
03364                             BIO_free(certout);
03365                             sk_X509_free(signers);
03366                      } else {
03367                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
03368                             RETVAL_LONG(-1);
03369                      }
03370               }
03371               goto clean_exit;
03372        } else {
03373               RETVAL_FALSE;
03374        }
03375 clean_exit:
03376        X509_STORE_free(store);
03377        BIO_free(datain);
03378        BIO_free(in);
03379        BIO_free(dataout);
03380        PKCS7_free(p7);
03381        sk_X509_free(others);
03382 }
03383 /* }}} */
03384 
03385 /* {{{ proto bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]])
03386    Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */
03387 PHP_FUNCTION(openssl_pkcs7_encrypt)
03388 {
03389        zval ** zrecipcerts, * zheaders = NULL;
03390        STACK_OF(X509) * recipcerts = NULL;
03391        BIO * infile = NULL, * outfile = NULL;
03392        long flags = 0;
03393        PKCS7 * p7 = NULL;
03394        HashPosition hpos;
03395        zval ** zcertval;
03396        X509 * cert;
03397        const EVP_CIPHER *cipher = NULL;
03398        long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
03399        uint strindexlen;
03400        ulong intindex;
03401        char * strindex;
03402        char * infilename = NULL;   int infilename_len;
03403        char * outfilename = NULL;  int outfilename_len;
03404        
03405        RETVAL_FALSE;
03406 
03407        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZa!|ll", &infilename, &infilename_len,
03408                             &outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE)
03409               return;
03410 
03411        if (strlen(infilename) != infilename_len) {
03412               return;
03413        }
03414 
03415        if (strlen(outfilename) != outfilename_len) {
03416               return;
03417        }
03418 
03419        if (php_openssl_safe_mode_chk(infilename TSRMLS_CC) || php_openssl_safe_mode_chk(outfilename TSRMLS_CC)) {
03420               return;
03421        }
03422 
03423        infile = BIO_new_file(infilename, "r");
03424        if (infile == NULL) {
03425               goto clean_exit;
03426        }
03427 
03428        outfile = BIO_new_file(outfilename, "w");
03429        if (outfile == NULL) { 
03430               goto clean_exit;
03431        }
03432 
03433        recipcerts = sk_X509_new_null();
03434 
03435        /* get certs */
03436        if (Z_TYPE_PP(zrecipcerts) == IS_ARRAY) {
03437               zend_hash_internal_pointer_reset_ex(HASH_OF(*zrecipcerts), &hpos);
03438               while(zend_hash_get_current_data_ex(HASH_OF(*zrecipcerts), (void**)&zcertval, &hpos) == SUCCESS) {
03439                      long certresource;
03440 
03441                      cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC);
03442                      if (cert == NULL) {
03443                             goto clean_exit;
03444                      }
03445 
03446                      if (certresource != -1) {
03447                             /* we shouldn't free this particular cert, as it is a resource.
03448                                    make a copy and push that on the stack instead */
03449                             cert = X509_dup(cert);
03450                             if (cert == NULL) {
03451                                    goto clean_exit;
03452                             }
03453                      }
03454                      sk_X509_push(recipcerts, cert);
03455 
03456                      zend_hash_move_forward_ex(HASH_OF(*zrecipcerts), &hpos);
03457               }
03458        } else {
03459               /* a single certificate */
03460               long certresource;
03461 
03462               cert = php_openssl_x509_from_zval(zrecipcerts, 0, &certresource TSRMLS_CC);
03463               if (cert == NULL) {
03464                      goto clean_exit;
03465               }
03466 
03467               if (certresource != -1) {
03468                      /* we shouldn't free this particular cert, as it is a resource.
03469                             make a copy and push that on the stack instead */
03470                      cert = X509_dup(cert);
03471                      if (cert == NULL) {
03472                             goto clean_exit;
03473                      }
03474               }
03475               sk_X509_push(recipcerts, cert);
03476        }
03477 
03478        /* sanity check the cipher */
03479        cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
03480        if (cipher == NULL) {
03481               /* shouldn't happen */
03482               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get cipher");
03483               goto clean_exit;
03484        }
03485 
03486        p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, flags);
03487 
03488        if (p7 == NULL) {
03489               goto clean_exit;
03490        }
03491 
03492        /* tack on extra headers */
03493        if (zheaders) {
03494               zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos);
03495               while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&zcertval, &hpos) == SUCCESS) {
03496                      strindex = NULL;
03497                      zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);
03498 
03499                      convert_to_string_ex(zcertval);
03500 
03501                      if (strindex) {
03502                             BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(zcertval));
03503                      } else {
03504                             BIO_printf(outfile, "%s\n", Z_STRVAL_PP(zcertval));
03505                      }
03506 
03507                      zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
03508               }
03509        }
03510 
03511        (void)BIO_reset(infile);
03512 
03513        /* write the encrypted data */
03514        SMIME_write_PKCS7(outfile, p7, infile, flags);
03515 
03516        RETVAL_TRUE;
03517 
03518 clean_exit:
03519        PKCS7_free(p7);
03520        BIO_free(infile);
03521        BIO_free(outfile);
03522        if (recipcerts) {
03523               sk_X509_pop_free(recipcerts, X509_free);
03524        }
03525 }
03526 /* }}} */
03527 
03528 /* {{{ proto bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]])
03529    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 */
03530 
03531 PHP_FUNCTION(openssl_pkcs7_sign)
03532 {
03533        zval ** zcert, ** zprivkey, * zheaders;
03534        zval ** hval;
03535        X509 * cert = NULL;
03536        EVP_PKEY * privkey = NULL;
03537        long flags = PKCS7_DETACHED;
03538        PKCS7 * p7 = NULL;
03539        BIO * infile = NULL, * outfile = NULL;
03540        STACK_OF(X509) *others = NULL;
03541        long certresource = -1, keyresource = -1;
03542        ulong intindex;
03543        uint strindexlen;
03544        HashPosition hpos;
03545        char * strindex;
03546        char * infilename;   int infilename_len;
03547        char * outfilename;  int outfilename_len;
03548        char * extracertsfilename = NULL; int extracertsfilename_len;
03549 
03550        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZZa!|ls",
03551                             &infilename, &infilename_len, &outfilename, &outfilename_len,
03552                             &zcert, &zprivkey, &zheaders, &flags, &extracertsfilename,
03553                             &extracertsfilename_len) == FAILURE) {
03554               return;
03555        }
03556        RETVAL_FALSE;
03557 
03558        if (strlen(infilename) != infilename_len) {
03559               return;
03560        }
03561 
03562        if (strlen(outfilename) != outfilename_len) {
03563               return;
03564        }
03565 
03566        if (extracertsfilename) {
03567               others = load_all_certs_from_file(extracertsfilename);
03568               if (others == NULL) { 
03569                      goto clean_exit;
03570               }
03571        }
03572 
03573        privkey = php_openssl_evp_from_zval(zprivkey, 0, "", 0, &keyresource TSRMLS_CC);
03574        if (privkey == NULL) {
03575               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting private key");
03576               goto clean_exit;
03577        }
03578 
03579        cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
03580        if (cert == NULL) {
03581               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting cert");
03582               goto clean_exit;
03583        }
03584 
03585        if (php_openssl_safe_mode_chk(infilename TSRMLS_CC) || php_openssl_safe_mode_chk(outfilename TSRMLS_CC)) {
03586               goto clean_exit;
03587        }
03588 
03589        infile = BIO_new_file(infilename, "r");
03590        if (infile == NULL) {
03591               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening input file %s!", infilename);
03592               goto clean_exit;
03593        }
03594 
03595        outfile = BIO_new_file(outfilename, "w");
03596        if (outfile == NULL) {
03597               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening output file %s!", outfilename);
03598               goto clean_exit;
03599        }
03600 
03601        p7 = PKCS7_sign(cert, privkey, others, infile, flags);
03602        if (p7 == NULL) {
03603               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error creating PKCS7 structure!");
03604               goto clean_exit;
03605        }
03606 
03607        (void)BIO_reset(infile);
03608 
03609        /* tack on extra headers */
03610        if (zheaders) {
03611               zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos);
03612               while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&hval, &hpos) == SUCCESS) {
03613                      strindex = NULL;
03614                      zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);
03615 
03616                      convert_to_string_ex(hval);
03617 
03618                      if (strindex) {
03619                             BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(hval));
03620                      } else {
03621                             BIO_printf(outfile, "%s\n", Z_STRVAL_PP(hval));
03622                      }
03623                      zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
03624               }
03625        }
03626        /* write the signed data */
03627        SMIME_write_PKCS7(outfile, p7, infile, flags);
03628 
03629        RETVAL_TRUE;
03630 
03631 clean_exit:
03632        PKCS7_free(p7);
03633        BIO_free(infile);
03634        BIO_free(outfile);
03635        if (others) {
03636               sk_X509_pop_free(others, X509_free);
03637        }
03638        if (privkey && keyresource == -1) {
03639               EVP_PKEY_free(privkey);
03640        }
03641        if (cert && certresource == -1) {
03642               X509_free(cert);
03643        }
03644 }
03645 /* }}} */
03646 
03647 /* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey])
03648    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 */
03649 
03650 PHP_FUNCTION(openssl_pkcs7_decrypt)
03651 {
03652        zval ** recipcert, ** recipkey = NULL;
03653        X509 * cert = NULL;
03654        EVP_PKEY * key = NULL;
03655        long certresval, keyresval;
03656        BIO * in = NULL, * out = NULL, * datain = NULL;
03657        PKCS7 * p7 = NULL;
03658        char * infilename;   int infilename_len;
03659        char * outfilename;  int outfilename_len;
03660 
03661        RETVAL_FALSE;
03662 
03663        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|Z", &infilename, &infilename_len,
03664                             &outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE) {
03665               return;
03666        }
03667 
03668        if (strlen(infilename) != infilename_len) {
03669               return;
03670        }
03671 
03672        if (strlen(outfilename) != outfilename_len) {
03673               return;
03674        }
03675 
03676        cert = php_openssl_x509_from_zval(recipcert, 0, &certresval TSRMLS_CC);
03677        if (cert == NULL) {
03678               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 3 to x509 cert");
03679               goto clean_exit;
03680        }
03681 
03682        key = php_openssl_evp_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, &keyresval TSRMLS_CC);
03683        if (key == NULL) {
03684               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to get private key");
03685               goto clean_exit;
03686        }
03687        
03688        if (php_openssl_safe_mode_chk(infilename TSRMLS_CC) || php_openssl_safe_mode_chk(outfilename TSRMLS_CC)) {
03689               goto clean_exit;
03690        }
03691 
03692        in = BIO_new_file(infilename, "r");
03693        if (in == NULL) {
03694               goto clean_exit;
03695        }
03696        out = BIO_new_file(outfilename, "w");
03697        if (out == NULL) {
03698               goto clean_exit;
03699        }
03700 
03701        p7 = SMIME_read_PKCS7(in, &datain);
03702 
03703        if (p7 == NULL) {
03704               goto clean_exit;
03705        }
03706        if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) { 
03707               RETVAL_TRUE;
03708        }
03709 clean_exit:
03710        PKCS7_free(p7);
03711        BIO_free(datain);
03712        BIO_free(in);
03713        BIO_free(out);
03714        if (cert && certresval == -1) {
03715               X509_free(cert);
03716        }
03717        if (key && keyresval == -1) {
03718               EVP_PKEY_free(key);
03719        }
03720 }
03721 /* }}} */
03722 
03723 /* }}} */
03724 
03725 /* {{{ proto bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding])
03726    Encrypts data with private key */
03727 PHP_FUNCTION(openssl_private_encrypt)
03728 {
03729        zval **key, *crypted;
03730        EVP_PKEY *pkey;
03731        int cryptedlen;
03732        unsigned char *cryptedbuf = NULL;
03733        int successful = 0;
03734        long keyresource = -1;
03735        char * data;
03736        int data_len;
03737        long padding = RSA_PKCS1_PADDING;
03738 
03739        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) { 
03740               return;
03741        }
03742        RETVAL_FALSE;
03743 
03744        pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
03745 
03746        if (pkey == NULL) {
03747               php_error_docref(NULL TSRMLS_CC, E_WARNING, "key param is not a valid private key");
03748               RETURN_FALSE;
03749        }
03750 
03751        cryptedlen = EVP_PKEY_size(pkey);
03752        cryptedbuf = emalloc(cryptedlen + 1);
03753 
03754        switch (pkey->type) {
03755               case EVP_PKEY_RSA:
03756               case EVP_PKEY_RSA2:
03757                      successful =  (RSA_private_encrypt(data_len, 
03758                                           (unsigned char *)data, 
03759                                           cryptedbuf, 
03760                                           pkey->pkey.rsa, 
03761                                           padding) == cryptedlen);
03762                      break;
03763               default:
03764                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
03765        }
03766 
03767        if (successful) {
03768               zval_dtor(crypted);
03769               cryptedbuf[cryptedlen] = '\0';
03770               ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
03771               cryptedbuf = NULL;
03772               RETVAL_TRUE;
03773        }
03774        if (cryptedbuf) {
03775               efree(cryptedbuf);
03776        }
03777        if (keyresource == -1) { 
03778               EVP_PKEY_free(pkey);
03779        }
03780 }
03781 /* }}} */
03782 
03783 /* {{{ proto bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding])
03784    Decrypts data with private key */
03785 PHP_FUNCTION(openssl_private_decrypt)
03786 {
03787        zval **key, *crypted;
03788        EVP_PKEY *pkey;
03789        int cryptedlen;
03790        unsigned char *cryptedbuf = NULL;
03791        unsigned char *crypttemp;
03792        int successful = 0;
03793        long padding = RSA_PKCS1_PADDING;
03794        long keyresource = -1;
03795        char * data;
03796        int data_len;
03797 
03798        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
03799               return;
03800        }
03801        RETVAL_FALSE;
03802 
03803        pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
03804        if (pkey == NULL) {
03805               php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid private key");
03806               RETURN_FALSE;
03807        }
03808 
03809        cryptedlen = EVP_PKEY_size(pkey);
03810        crypttemp = emalloc(cryptedlen + 1);
03811 
03812        switch (pkey->type) {
03813               case EVP_PKEY_RSA:
03814               case EVP_PKEY_RSA2:
03815                      cryptedlen = RSA_private_decrypt(data_len, 
03816                                    (unsigned char *)data, 
03817                                    crypttemp, 
03818                                    pkey->pkey.rsa, 
03819                                    padding);
03820                      if (cryptedlen != -1) {
03821                             cryptedbuf = emalloc(cryptedlen + 1);
03822                             memcpy(cryptedbuf, crypttemp, cryptedlen);
03823                             successful = 1;
03824                      }
03825                      break;
03826               default:
03827                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
03828        }
03829 
03830        efree(crypttemp);
03831 
03832        if (successful) {
03833               zval_dtor(crypted);
03834               cryptedbuf[cryptedlen] = '\0';
03835               ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
03836               cryptedbuf = NULL;
03837               RETVAL_TRUE;
03838        }
03839 
03840        if (keyresource == -1) {
03841               EVP_PKEY_free(pkey);
03842        }
03843        if (cryptedbuf) { 
03844               efree(cryptedbuf);
03845        }
03846 }
03847 /* }}} */
03848 
03849 /* {{{ proto bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding])
03850    Encrypts data with public key */
03851 PHP_FUNCTION(openssl_public_encrypt)
03852 {
03853        zval **key, *crypted;
03854        EVP_PKEY *pkey;
03855        int cryptedlen;
03856        unsigned char *cryptedbuf;
03857        int successful = 0;
03858        long keyresource = -1;
03859        long padding = RSA_PKCS1_PADDING;
03860        char * data;
03861        int data_len;
03862 
03863        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
03864               return;
03865 
03866        RETVAL_FALSE;
03867        
03868        pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
03869        if (pkey == NULL) {
03870               php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key");
03871               RETURN_FALSE;
03872        }
03873 
03874        cryptedlen = EVP_PKEY_size(pkey);
03875        cryptedbuf = emalloc(cryptedlen + 1);
03876 
03877        switch (pkey->type) {
03878               case EVP_PKEY_RSA:
03879               case EVP_PKEY_RSA2:
03880                      successful = (RSA_public_encrypt(data_len, 
03881                                           (unsigned char *)data, 
03882                                           cryptedbuf, 
03883                                           pkey->pkey.rsa, 
03884                                           padding) == cryptedlen);
03885                      break;
03886               default:
03887                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
03888 
03889        }
03890 
03891        if (successful) {
03892               zval_dtor(crypted);
03893               cryptedbuf[cryptedlen] = '\0';
03894               ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
03895               cryptedbuf = NULL;
03896               RETVAL_TRUE;
03897        }
03898        if (keyresource == -1) {
03899               EVP_PKEY_free(pkey);
03900        }
03901        if (cryptedbuf) {
03902               efree(cryptedbuf);
03903        }
03904 }
03905 /* }}} */
03906 
03907 /* {{{ proto bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding])
03908    Decrypts data with public key */
03909 PHP_FUNCTION(openssl_public_decrypt)
03910 {
03911        zval **key, *crypted;
03912        EVP_PKEY *pkey;
03913        int cryptedlen;
03914        unsigned char *cryptedbuf = NULL;
03915        unsigned char *crypttemp;
03916        int successful = 0;
03917        long keyresource = -1;
03918        long padding = RSA_PKCS1_PADDING;
03919        char * data;
03920        int data_len;
03921 
03922        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
03923               return;
03924        }
03925        RETVAL_FALSE;
03926        
03927        pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
03928        if (pkey == NULL) {
03929               php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key");
03930               RETURN_FALSE;
03931        }
03932 
03933        cryptedlen = EVP_PKEY_size(pkey);
03934        crypttemp = emalloc(cryptedlen + 1);
03935 
03936        switch (pkey->type) {
03937               case EVP_PKEY_RSA:
03938               case EVP_PKEY_RSA2:
03939                      cryptedlen = RSA_public_decrypt(data_len, 
03940                                    (unsigned char *)data, 
03941                                    crypttemp, 
03942                                    pkey->pkey.rsa, 
03943                                    padding);
03944                      if (cryptedlen != -1) {
03945                             cryptedbuf = emalloc(cryptedlen + 1);
03946                             memcpy(cryptedbuf, crypttemp, cryptedlen);
03947                             successful = 1;
03948                      }
03949                      break;
03950                      
03951               default:
03952                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
03953                
03954        }
03955 
03956        efree(crypttemp);
03957 
03958        if (successful) {
03959               zval_dtor(crypted);
03960               cryptedbuf[cryptedlen] = '\0';
03961               ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
03962               cryptedbuf = NULL;
03963               RETVAL_TRUE;
03964        }
03965 
03966        if (cryptedbuf) {
03967               efree(cryptedbuf);
03968        }
03969        if (keyresource == -1) {
03970               EVP_PKEY_free(pkey);
03971        }
03972 }
03973 /* }}} */
03974 
03975 /* {{{ proto mixed openssl_error_string(void)
03976    Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */
03977 PHP_FUNCTION(openssl_error_string)
03978 {
03979        char buf[512];
03980        unsigned long val;
03981 
03982        if (zend_parse_parameters_none() == FAILURE) {
03983               return;
03984        }
03985 
03986        val = ERR_get_error();
03987        if (val) {
03988               RETURN_STRING(ERR_error_string(val, buf), 1);
03989        } else {
03990               RETURN_FALSE;
03991        }
03992 }
03993 /* }}} */
03994 
03995 /* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, mixed method])
03996    Signs data */
03997 PHP_FUNCTION(openssl_sign)
03998 {
03999        zval **key, *signature;
04000        EVP_PKEY *pkey;
04001        int siglen;
04002        unsigned char *sigbuf;
04003        long keyresource = -1;
04004        char * data;
04005        int data_len;
04006        EVP_MD_CTX md_ctx;
04007        zval *method = NULL;
04008        long signature_algo = OPENSSL_ALGO_SHA1;
04009        const EVP_MD *mdtype;
04010 
04011        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|z", &data, &data_len, &signature, &key, &method) == FAILURE) {
04012               return;
04013        }
04014        pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
04015        if (pkey == NULL) {
04016               php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a private key");
04017               RETURN_FALSE;
04018        }
04019 
04020        if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
04021               if (method != NULL) {
04022                      signature_algo = Z_LVAL_P(method);
04023               }
04024               mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
04025        } else if (Z_TYPE_P(method) == IS_STRING) {
04026               mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
04027        } else {
04028               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
04029               RETURN_FALSE;
04030        }
04031        if (!mdtype) {
04032               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
04033               RETURN_FALSE;
04034        }
04035 
04036        siglen = EVP_PKEY_size(pkey);
04037        sigbuf = emalloc(siglen + 1);
04038 
04039        EVP_SignInit(&md_ctx, mdtype);
04040        EVP_SignUpdate(&md_ctx, data, data_len);
04041        if (EVP_SignFinal (&md_ctx, sigbuf,(unsigned int *)&siglen, pkey)) {
04042               zval_dtor(signature);
04043               sigbuf[siglen] = '\0';
04044               ZVAL_STRINGL(signature, (char *)sigbuf, siglen, 0);
04045               RETVAL_TRUE;
04046        } else {
04047               efree(sigbuf);
04048               RETVAL_FALSE;
04049        }
04050        EVP_MD_CTX_cleanup(&md_ctx);
04051        if (keyresource == -1) {
04052               EVP_PKEY_free(pkey);
04053        }
04054 }
04055 /* }}} */
04056 
04057 /* {{{ proto int openssl_verify(string data, string signature, mixed key[, mixed method])
04058    Verifys data */
04059 PHP_FUNCTION(openssl_verify)
04060 {
04061        zval **key;
04062        EVP_PKEY *pkey;
04063        int err;
04064        EVP_MD_CTX     md_ctx;
04065        const EVP_MD *mdtype;
04066        long keyresource = -1;
04067        char * data;  int data_len;
04068        char * signature;    int signature_len;
04069        zval *method = NULL;
04070        long signature_algo = OPENSSL_ALGO_SHA1;
04071        
04072        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|z", &data, &data_len, &signature, &signature_len, &key, &method) == FAILURE) {
04073               return;
04074        }
04075 
04076        if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
04077               if (method != NULL) {
04078                      signature_algo = Z_LVAL_P(method);
04079               }
04080               mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
04081        } else if (Z_TYPE_P(method) == IS_STRING) {
04082               mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
04083        } else {
04084               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
04085               RETURN_FALSE;
04086        }
04087        if (!mdtype) {
04088               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
04089               RETURN_FALSE;
04090        }
04091 
04092        pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
04093        if (pkey == NULL) {
04094               php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a public key");
04095               RETURN_FALSE;
04096        }
04097 
04098        EVP_VerifyInit   (&md_ctx, mdtype);
04099        EVP_VerifyUpdate (&md_ctx, data, data_len);
04100        err = EVP_VerifyFinal (&md_ctx, (unsigned char *)signature, signature_len, pkey);
04101        EVP_MD_CTX_cleanup(&md_ctx);
04102 
04103        if (keyresource == -1) {
04104               EVP_PKEY_free(pkey);
04105        }
04106        RETURN_LONG(err);
04107 }
04108 /* }}} */
04109 
04110 /* {{{ proto int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys)
04111    Seals data */
04112 PHP_FUNCTION(openssl_seal)
04113 {
04114        zval *pubkeys, **pubkey, *sealdata, *ekeys;
04115        HashTable *pubkeysht;
04116        HashPosition pos;
04117        EVP_PKEY **pkeys;
04118        long * key_resources;       /* so we know what to cleanup */
04119        int i, len1, len2, *eksl, nkeys;
04120        unsigned char *buf = NULL, **eks;
04121        char * data; int data_len;
04122        char *method =NULL;
04123        int method_len = 0;
04124        const EVP_CIPHER *cipher;
04125        EVP_CIPHER_CTX ctx;
04126 
04127        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szza/|s", &data, &data_len, &sealdata, &ekeys, &pubkeys, &method, &method_len) == FAILURE) {
04128               return;
04129        }
04130        
04131        pubkeysht = HASH_OF(pubkeys);
04132        nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
04133        if (!nkeys) {
04134               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Fourth argument to openssl_seal() must be a non-empty array");
04135               RETURN_FALSE;
04136        }
04137 
04138        if (method) {
04139               cipher = EVP_get_cipherbyname(method);
04140               if (!cipher) {
04141                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
04142                      RETURN_FALSE;
04143               }
04144        } else {
04145               cipher = EVP_rc4();
04146        }
04147 
04148        pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
04149        eksl = safe_emalloc(nkeys, sizeof(*eksl), 0);
04150        eks = safe_emalloc(nkeys, sizeof(*eks), 0);
04151        memset(eks, 0, sizeof(*eks) * nkeys);
04152        key_resources = safe_emalloc(nkeys, sizeof(long), 0);
04153        memset(key_resources, 0, sizeof(*key_resources) * nkeys);
04154 
04155        /* get the public keys we are using to seal this data */
04156        zend_hash_internal_pointer_reset_ex(pubkeysht, &pos);
04157        i = 0;
04158        while (zend_hash_get_current_data_ex(pubkeysht, (void **) &pubkey,
04159                             &pos) == SUCCESS) {
04160               pkeys[i] = php_openssl_evp_from_zval(pubkey, 1, NULL, 0, &key_resources[i] TSRMLS_CC);
04161               if (pkeys[i] == NULL) {
04162                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "not a public key (%dth member of pubkeys)", i+1);
04163                      RETVAL_FALSE;
04164                      goto clean_exit;
04165               }
04166               eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
04167               zend_hash_move_forward_ex(pubkeysht, &pos);
04168               i++;
04169        }
04170 
04171        if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) {
04172               RETVAL_FALSE;
04173               goto clean_exit;
04174        }
04175 
04176 #if 0
04177        /* Need this if allow ciphers that require initialization vector */
04178        ivlen = EVP_CIPHER_CTX_iv_length(&ctx);
04179        iv = ivlen ? emalloc(ivlen + 1) : NULL;
04180 #endif
04181        /* allocate one byte extra to make room for \0 */
04182        buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx));
04183 
04184        if (!EVP_SealInit(&ctx, cipher, eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) {
04185               RETVAL_FALSE;
04186               efree(buf);
04187               goto clean_exit;
04188        }
04189 
04190        EVP_SealFinal(&ctx, buf + len1, &len2);
04191 
04192        if (len1 + len2 > 0) {
04193               zval_dtor(sealdata);
04194               buf[len1 + len2] = '\0';
04195               buf = erealloc(buf, len1 + len2 + 1);
04196               ZVAL_STRINGL(sealdata, (char *)buf, len1 + len2, 0);
04197 
04198               zval_dtor(ekeys);
04199               array_init(ekeys);
04200               for (i=0; i<nkeys; i++) {
04201                      eks[i][eksl[i]] = '\0';
04202                      add_next_index_stringl(ekeys, erealloc(eks[i], eksl[i] + 1), eksl[i], 0);
04203                      eks[i] = NULL;
04204               }
04205 #if 0
04206               /* If allow ciphers that need IV, we need this */
04207               zval_dtor(*ivec);
04208               if (ivlen) {
04209                      iv[ivlen] = '\0';
04210                      ZVAL_STRINGL(*ivec, erealloc(iv, ivlen + 1), ivlen, 0);
04211               } else {
04212                      ZVAL_EMPTY_STRING(*ivec);
04213               }
04214 #endif
04215        } else {
04216               efree(buf);
04217        }
04218        RETVAL_LONG(len1 + len2);
04219 
04220 clean_exit:
04221        for (i=0; i<nkeys; i++) {
04222               if (key_resources[i] == -1) {
04223                      EVP_PKEY_free(pkeys[i]);
04224               }
04225               if (eks[i]) { 
04226                      efree(eks[i]);
04227               }
04228        }
04229        efree(eks);
04230        efree(eksl);
04231        efree(pkeys);
04232        efree(key_resources);
04233 }
04234 /* }}} */
04235 
04236 /* {{{ proto bool openssl_open(string data, &string opendata, string ekey, mixed privkey)
04237    Opens data */
04238 PHP_FUNCTION(openssl_open)
04239 {
04240        zval **privkey, *opendata;
04241        EVP_PKEY *pkey;
04242        int len1, len2;
04243        unsigned char *buf;
04244        long keyresource = -1;
04245        EVP_CIPHER_CTX ctx;
04246        char * data;  int data_len;
04247        char * ekey;  int ekey_len;
04248        char *method =NULL;
04249        int method_len = 0;
04250        const EVP_CIPHER *cipher;
04251 
04252        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szsZ|s", &data, &data_len, &opendata, &ekey, &ekey_len, &privkey, &method, &method_len) == FAILURE) {
04253               return;
04254        }
04255 
04256        pkey = php_openssl_evp_from_zval(privkey, 0, "", 0, &keyresource TSRMLS_CC);
04257        if (pkey == NULL) {
04258               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 4 into a private key");
04259               RETURN_FALSE;
04260        }
04261 
04262        if (method) {
04263               cipher = EVP_get_cipherbyname(method);
04264               if (!cipher) {
04265                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
04266                      RETURN_FALSE;
04267               }
04268        } else {
04269               cipher = EVP_rc4();
04270        }
04271        
04272        buf = emalloc(data_len + 1);
04273 
04274        if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, ekey_len, NULL, pkey) && EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) {
04275               if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) {
04276                      efree(buf);
04277                      if (keyresource == -1) { 
04278                             EVP_PKEY_free(pkey);
04279                      }
04280                      RETURN_FALSE;
04281               }
04282        } else {
04283               efree(buf);
04284               if (keyresource == -1) {
04285                      EVP_PKEY_free(pkey);
04286               }
04287               RETURN_FALSE;
04288        }
04289        if (keyresource == -1) {
04290               EVP_PKEY_free(pkey);
04291        }
04292        zval_dtor(opendata);
04293        buf[len1 + len2] = '\0';
04294        ZVAL_STRINGL(opendata, erealloc(buf, len1 + len2 + 1), len1 + len2, 0);
04295        RETURN_TRUE;
04296 }
04297 /* }}} */
04298 
04299 /* SSL verification functions */
04300 
04301 #define GET_VER_OPT(name)               (stream->context && SUCCESS == php_stream_context_get_option(stream->context, "ssl", name, &val))
04302 #define GET_VER_OPT_STRING(name, str)   if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_PP(val); }
04303 
04304 static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */
04305 {
04306        php_stream *stream;
04307        SSL *ssl;
04308        X509 *err_cert;
04309        int err, depth, ret;
04310        zval **val;
04311 
04312        ret = preverify_ok;
04313 
04314        /* determine the status for the current cert */
04315        err_cert = X509_STORE_CTX_get_current_cert(ctx);
04316        err = X509_STORE_CTX_get_error(ctx);
04317        depth = X509_STORE_CTX_get_error_depth(ctx);
04318 
04319        /* conjure the stream & context to use */
04320        ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
04321        stream = (php_stream*)SSL_get_ex_data(ssl, ssl_stream_data_index);
04322 
04323        /* if allow_self_signed is set, make sure that verification succeeds */
04324        if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) {
04325               ret = 1;
04326        }
04327 
04328        /* check the depth */
04329        if (GET_VER_OPT("verify_depth")) {
04330               convert_to_long_ex(val);
04331 
04332               if (depth > Z_LVAL_PP(val)) {
04333                      ret = 0;
04334                      X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG);
04335               }
04336        }
04337 
04338        return ret;
04339 
04340 }
04341 /* }}} */
04342 
04343 int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC) /* {{{ */
04344 {
04345        zval **val = NULL;
04346        char *cnmatch = NULL;
04347        X509_NAME *name;
04348        char buf[1024];
04349        int err;
04350 
04351        /* verification is turned off */
04352        if (!(GET_VER_OPT("verify_peer") && zval_is_true(*val))) {
04353               return SUCCESS;
04354        }
04355 
04356        if (peer == NULL) {
04357               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not get peer certificate");
04358               return FAILURE;
04359        }
04360 
04361        err = SSL_get_verify_result(ssl);
04362        switch (err) {
04363               case X509_V_OK:
04364                      /* fine */
04365                      break;
04366               case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
04367                      if (GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) {
04368                             /* allowed */
04369                             break;
04370                      }
04371                      /* not allowed, so fall through */
04372               default:
04373                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not verify peer: code:%d %s", err, X509_verify_cert_error_string(err));
04374                      return FAILURE;
04375        }
04376 
04377        /* if the cert passed the usual checks, apply our own local policies now */
04378 
04379        name = X509_get_subject_name(peer);
04380 
04381        /* Does the common name match ? (used primarily for https://) */
04382        GET_VER_OPT_STRING("CN_match", cnmatch);
04383        if (cnmatch) {
04384               int match = 0;
04385               int name_len = X509_NAME_get_text_by_NID(name, NID_commonName, buf, sizeof(buf));
04386 
04387               if (name_len == -1) {
04388                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN");
04389                      return FAILURE;
04390               } else if (name_len != strlen(buf)) {
04391                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", name_len, buf);
04392                      return FAILURE;
04393               }
04394 
04395               match = strcmp(cnmatch, buf) == 0;
04396               if (!match && strlen(buf) > 3 && buf[0] == '*' && buf[1] == '.') {
04397                      /* Try wildcard */
04398 
04399                      if (strchr(buf+2, '.')) {
04400                             char *tmp = strstr(cnmatch, buf+1);
04401 
04402                             match = tmp && strcmp(tmp, buf+2) && tmp == strchr(cnmatch, '.');
04403                      }
04404               }
04405 
04406               if (!match) {
04407                      /* didn't match */
04408                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", name_len, buf, cnmatch);
04409                      return FAILURE;
04410               }
04411        }
04412 
04413        return SUCCESS;
04414 }
04415 /* }}} */
04416 
04417 static int passwd_callback(char *buf, int num, int verify, void *data) /* {{{ */
04418 {
04419     php_stream *stream = (php_stream *)data;
04420     zval **val = NULL;
04421     char *passphrase = NULL;
04422     /* TODO: could expand this to make a callback into PHP user-space */
04423 
04424     GET_VER_OPT_STRING("passphrase", passphrase);
04425 
04426     if (passphrase) {
04427         if (Z_STRLEN_PP(val) < num - 1) {
04428             memcpy(buf, Z_STRVAL_PP(val), Z_STRLEN_PP(val)+1);
04429             return Z_STRLEN_PP(val);
04430         }
04431     }
04432     return 0;
04433 }
04434 /* }}} */
04435 
04436 SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{ */
04437 {
04438        zval **val = NULL;
04439        char *cafile = NULL;
04440        char *capath = NULL;
04441        char *certfile = NULL;
04442        char *cipherlist = NULL;
04443        int ok = 1;
04444 
04445        ERR_clear_error();
04446 
04447        /* look at context options in the stream and set appropriate verification flags */
04448        if (GET_VER_OPT("verify_peer") && zval_is_true(*val)) {
04449 
04450               /* turn on verification callback */
04451               SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
04452 
04453               /* CA stuff */
04454               GET_VER_OPT_STRING("cafile", cafile);
04455               GET_VER_OPT_STRING("capath", capath);
04456 
04457               if (cafile || capath) {
04458                      if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
04459                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set verify locations `%s' `%s'", cafile, capath);
04460                             return NULL;
04461                      }
04462               }
04463 
04464               if (GET_VER_OPT("verify_depth")) {
04465                      convert_to_long_ex(val);
04466                      SSL_CTX_set_verify_depth(ctx, Z_LVAL_PP(val));
04467               }
04468        } else {
04469               SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
04470        }
04471 
04472        /* callback for the passphrase (for localcert) */
04473        if (GET_VER_OPT("passphrase")) {
04474               SSL_CTX_set_default_passwd_cb_userdata(ctx, stream);
04475               SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
04476        }
04477 
04478        GET_VER_OPT_STRING("ciphers", cipherlist);
04479        if (!cipherlist) {
04480               cipherlist = "DEFAULT";
04481        }
04482        if (SSL_CTX_set_cipher_list(ctx, cipherlist) != 1) {
04483               return NULL;
04484        }
04485 
04486        GET_VER_OPT_STRING("local_cert", certfile);
04487        if (certfile) {
04488               X509 *cert = NULL;
04489               EVP_PKEY *key = NULL;
04490               SSL *tmpssl;
04491               char resolved_path_buff[MAXPATHLEN];
04492               const char * private_key = NULL;
04493 
04494               if (VCWD_REALPATH(certfile, resolved_path_buff)) {
04495                      /* a certificate to use for authentication */
04496                      if (SSL_CTX_use_certificate_chain_file(ctx, resolved_path_buff) != 1) {
04497                             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);
04498                             return NULL;
04499                      }
04500                      GET_VER_OPT_STRING("local_pk", private_key);
04501 
04502                      if (private_key) {
04503                             char resolved_path_buff_pk[MAXPATHLEN];
04504                             if (VCWD_REALPATH(private_key, resolved_path_buff_pk)) {
04505                                    if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff_pk, SSL_FILETYPE_PEM) != 1) {
04506                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff_pk);
04507                                           return NULL;
04508                                    }
04509                             }
04510                      } else {
04511                             if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff, SSL_FILETYPE_PEM) != 1) {
04512                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff);
04513                                    return NULL;
04514                             }             
04515                      }
04516 
04517                      tmpssl = SSL_new(ctx);
04518                      cert = SSL_get_certificate(tmpssl);
04519 
04520                      if (cert) {
04521                             key = X509_get_pubkey(cert);
04522                             EVP_PKEY_copy_parameters(key, SSL_get_privatekey(tmpssl));
04523                             EVP_PKEY_free(key);
04524                      }
04525                      SSL_free(tmpssl);
04526 
04527                      if (!SSL_CTX_check_private_key(ctx)) {
04528                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Private key does not match certificate!");
04529                      }
04530               }
04531        }
04532        if (ok) {
04533               SSL *ssl = SSL_new(ctx);
04534 
04535               if (ssl) {
04536                      /* map SSL => stream */
04537                      SSL_set_ex_data(ssl, ssl_stream_data_index, stream);
04538               }
04539               return ssl;
04540        }
04541 
04542        return NULL;
04543 }
04544 /* }}} */
04545 
04546 static void openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */
04547 {
04548        add_next_index_string((zval*)arg, (char*)name->name, 1);
04549 }
04550 /* }}} */
04551 
04552 static void openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */
04553 {
04554        if (name->alias == 0) {
04555               add_next_index_string((zval*)arg, (char*)name->name, 1);
04556        }
04557 }
04558 /* }}} */
04559 
04560 /* {{{ proto array openssl_get_md_methods([bool aliases = false])
04561    Return array of available digest methods */
04562 PHP_FUNCTION(openssl_get_md_methods)
04563 {
04564        zend_bool aliases = 0;
04565 
04566        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) {
04567               return;
04568        }
04569        array_init(return_value);
04570        OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
04571               aliases ? openssl_add_method_or_alias: openssl_add_method, 
04572               return_value);
04573 }
04574 /* }}} */
04575 
04576 /* {{{ proto array openssl_get_cipher_methods([bool aliases = false])
04577    Return array of available cipher methods */
04578 PHP_FUNCTION(openssl_get_cipher_methods)
04579 {
04580        zend_bool aliases = 0;
04581 
04582        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) {
04583               return;
04584        }
04585        array_init(return_value);
04586        OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
04587               aliases ? openssl_add_method_or_alias: openssl_add_method, 
04588               return_value);
04589 }
04590 /* }}} */
04591 
04592 /* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false])
04593    Computes digest hash value for given data using given method, returns raw or binhex encoded string */
04594 PHP_FUNCTION(openssl_digest)
04595 {
04596        zend_bool raw_output = 0;
04597        char *data, *method;
04598        int data_len, method_len;
04599        const EVP_MD *mdtype;
04600        EVP_MD_CTX md_ctx;
04601        int siglen;
04602        unsigned char *sigbuf;
04603 
04604        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
04605               return;
04606        }
04607        mdtype = EVP_get_digestbyname(method);
04608        if (!mdtype) {
04609               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
04610               RETURN_FALSE;
04611        }
04612 
04613        siglen = EVP_MD_size(mdtype);
04614        sigbuf = emalloc(siglen + 1);
04615 
04616        EVP_DigestInit(&md_ctx, mdtype);
04617        EVP_DigestUpdate(&md_ctx, (unsigned char *)data, data_len);
04618        if (EVP_DigestFinal (&md_ctx, (unsigned char *)sigbuf, (unsigned int *)&siglen)) {
04619               if (raw_output) {
04620                      sigbuf[siglen] = '\0';
04621                      RETVAL_STRINGL((char *)sigbuf, siglen, 0);
04622               } else {
04623                      int digest_str_len = siglen * 2;
04624                      char *digest_str = emalloc(digest_str_len + 1);
04625 
04626                      make_digest_ex(digest_str, sigbuf, siglen);
04627                      efree(sigbuf);
04628                      RETVAL_STRINGL(digest_str, digest_str_len, 0);
04629               }
04630        } else {
04631               efree(sigbuf);
04632               RETVAL_FALSE;
04633        }
04634 }
04635 /* }}} */
04636 
04637 static zend_bool php_openssl_validate_iv(char **piv, int *piv_len, int iv_required_len TSRMLS_DC)
04638 {
04639        char *iv_new;
04640 
04641        /* Best case scenario, user behaved */
04642        if (*piv_len == iv_required_len) {
04643               return 0;
04644        }
04645 
04646        iv_new = ecalloc(1, iv_required_len + 1);
04647 
04648        if (*piv_len <= 0) {
04649               /* BC behavior */
04650               *piv_len = iv_required_len;
04651               *piv     = iv_new;
04652               return 1;
04653        }
04654 
04655        if (*piv_len < iv_required_len) {
04656               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);
04657               memcpy(iv_new, *piv, *piv_len);
04658               *piv_len = iv_required_len;
04659               *piv     = iv_new;
04660               return 1;
04661        }
04662 
04663        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);
04664        memcpy(iv_new, *piv, iv_required_len);
04665        *piv_len = iv_required_len;
04666        *piv     = iv_new;
04667        return 1;
04668 
04669 }
04670 
04671 /* {{{ proto string openssl_encrypt(string data, string method, string password [, bool raw_output=false [, string $iv='']])
04672    Encrypts given data with given method and key, returns raw or base64 encoded string */
04673 PHP_FUNCTION(openssl_encrypt)
04674 {
04675        zend_bool raw_output = 0;
04676        char *data, *method, *password, *iv = "";
04677        int data_len, method_len, password_len, iv_len = 0, max_iv_len;
04678        const EVP_CIPHER *cipher_type;
04679        EVP_CIPHER_CTX cipher_ctx;
04680        int i, outlen, keylen;
04681        unsigned char *outbuf, *key;
04682        zend_bool free_iv;
04683 
04684        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|bs", &data, &data_len, &method, &method_len, &password, &password_len, &raw_output, &iv, &iv_len) == FAILURE) {
04685               return;
04686        }
04687        cipher_type = EVP_get_cipherbyname(method);
04688        if (!cipher_type) {
04689               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
04690               RETURN_FALSE;
04691        }
04692 
04693        keylen = EVP_CIPHER_key_length(cipher_type);
04694        if (keylen > password_len) {
04695               key = emalloc(keylen);
04696               memset(key, 0, keylen);
04697               memcpy(key, password, password_len);
04698        } else {
04699               key = (unsigned char*)password;
04700        }
04701 
04702        max_iv_len = EVP_CIPHER_iv_length(cipher_type);
04703        if (iv_len <= 0 && max_iv_len > 0) {
04704               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
04705        }
04706        free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len TSRMLS_CC);
04707 
04708        outlen = data_len + EVP_CIPHER_block_size(cipher_type);
04709        outbuf = emalloc(outlen + 1);
04710 
04711        EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL);
04712        if (password_len > keylen) {
04713               EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len);
04714        }
04715        EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
04716        if (data_len > 0) {
04717               EVP_EncryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len);
04718        }
04719        outlen = i;
04720        if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) {
04721               outlen += i;
04722               if (raw_output) {
04723                      outbuf[outlen] = '\0';
04724                      RETVAL_STRINGL((char *)outbuf, outlen, 0);
04725               } else {
04726                      int base64_str_len;
04727                      char *base64_str;
04728 
04729                      base64_str = (char*)php_base64_encode(outbuf, outlen, &base64_str_len);
04730                      efree(outbuf);
04731                      RETVAL_STRINGL(base64_str, base64_str_len, 0);
04732               }
04733        } else {
04734               efree(outbuf);
04735               RETVAL_FALSE;
04736        }
04737        if (key != (unsigned char*)password) {
04738               efree(key);
04739        }
04740        if (free_iv) {
04741               efree(iv);
04742        }
04743        EVP_CIPHER_CTX_cleanup(&cipher_ctx);
04744 }
04745 /* }}} */
04746 
04747 /* {{{ proto string openssl_decrypt(string data, string method, string password [, bool raw_input=false [, string $iv = '']])
04748    Takes raw or base64 encoded string and dectupt it using given method and key */
04749 PHP_FUNCTION(openssl_decrypt)
04750 {
04751        zend_bool raw_input = 0;
04752        char *data, *method, *password, *iv = "";
04753        int data_len, method_len, password_len, iv_len = 0;
04754        const EVP_CIPHER *cipher_type;
04755        EVP_CIPHER_CTX cipher_ctx;
04756        int i, outlen, keylen;
04757        unsigned char *outbuf, *key;
04758        int base64_str_len;
04759        char *base64_str = NULL;
04760        zend_bool free_iv;
04761 
04762        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|bs", &data, &data_len, &method, &method_len, &password, &password_len, &raw_input, &iv, &iv_len) == FAILURE) {
04763               return;
04764        }
04765 
04766        if (!method_len) {
04767               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
04768               RETURN_FALSE;
04769        }
04770 
04771        cipher_type = EVP_get_cipherbyname(method);
04772        if (!cipher_type) {
04773               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
04774               RETURN_FALSE;
04775        }
04776 
04777        if (!raw_input) {
04778               base64_str = (char*)php_base64_decode((unsigned char*)data, data_len, &base64_str_len);
04779               data_len = base64_str_len;
04780               data = base64_str;
04781        }
04782 
04783        keylen = EVP_CIPHER_key_length(cipher_type);
04784        if (keylen > password_len) {
04785               key = emalloc(keylen);
04786               memset(key, 0, keylen);
04787               memcpy(key, password, password_len);
04788        } else {
04789               key = (unsigned char*)password;
04790        }
04791 
04792        free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type) TSRMLS_CC);
04793 
04794        outlen = data_len + EVP_CIPHER_block_size(cipher_type);
04795        outbuf = emalloc(outlen + 1);
04796 
04797        EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL);
04798        if (password_len > keylen) {
04799               EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len);
04800        }
04801        EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
04802        EVP_DecryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len);
04803        outlen = i;
04804        if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) {
04805               outlen += i;
04806               outbuf[outlen] = '\0';
04807               RETVAL_STRINGL((char *)outbuf, outlen, 0);
04808        } else {
04809               efree(outbuf);
04810               RETVAL_FALSE;
04811        }
04812        if (key != (unsigned char*)password) {
04813               efree(key);
04814        }
04815        if (free_iv) {
04816               efree(iv);
04817        }
04818        if (base64_str) {
04819               efree(base64_str);
04820        }
04821        EVP_CIPHER_CTX_cleanup(&cipher_ctx);
04822 }
04823 /* }}} */
04824 
04825 /* {{{ proto int openssl_cipher_iv_length(string $method) */
04826 PHP_FUNCTION(openssl_cipher_iv_length)
04827 {
04828        char *method;
04829        int method_len;
04830        const EVP_CIPHER *cipher_type;
04831 
04832        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len) == FAILURE) {
04833               return;
04834        }
04835 
04836        if (!method_len) {
04837               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
04838               RETURN_FALSE;
04839        }
04840 
04841        cipher_type = EVP_get_cipherbyname(method);
04842        if (!cipher_type) {
04843               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
04844               RETURN_FALSE;
04845        }
04846 
04847        RETURN_LONG(EVP_CIPHER_iv_length(cipher_type));
04848 }
04849 /* }}} */
04850 
04851 
04852 /* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
04853    Computes shared sicret for public value of remote DH key and local DH key */
04854 PHP_FUNCTION(openssl_dh_compute_key)
04855 {
04856        zval *key;
04857        char *pub_str;
04858        int pub_len;
04859        EVP_PKEY *pkey;
04860        BIGNUM *pub;
04861        char *data;
04862        int len;
04863 
04864        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr", &pub_str, &pub_len, &key) == FAILURE) {
04865               return;
04866        }
04867        ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
04868        if (!pkey || EVP_PKEY_type(pkey->type) != EVP_PKEY_DH || !pkey->pkey.dh) {
04869               RETURN_FALSE;
04870        }
04871 
04872        pub = BN_bin2bn((unsigned char*)pub_str, pub_len, NULL);
04873 
04874        data = emalloc(DH_size(pkey->pkey.dh) + 1);
04875        len = DH_compute_key((unsigned char*)data, pub, pkey->pkey.dh);
04876 
04877        if (len >= 0) {
04878               data[len] = 0;
04879               RETVAL_STRINGL(data, len, 0);
04880        } else {
04881               efree(data);
04882               RETVAL_FALSE;
04883        }
04884 
04885        BN_free(pub);
04886 }
04887 /* }}} */
04888 
04889 /* {{{ proto string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result])
04890    Returns a string of the length specified filled with random pseudo bytes */
04891 PHP_FUNCTION(openssl_random_pseudo_bytes)
04892 {
04893        long buffer_length;
04894        unsigned char *buffer = NULL;
04895        zval *zstrong_result_returned = NULL;
04896        int strong_result = 0;
04897 
04898        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) {
04899               return;
04900        }
04901 
04902        if (buffer_length <= 0) {
04903               RETURN_FALSE;
04904        }
04905 
04906        if (zstrong_result_returned) {
04907               zval_dtor(zstrong_result_returned);
04908               ZVAL_BOOL(zstrong_result_returned, 0);
04909        }
04910 
04911        buffer = emalloc(buffer_length + 1);
04912 
04913        if ((strong_result = RAND_pseudo_bytes(buffer, buffer_length)) < 0) {
04914               efree(buffer);
04915               RETURN_FALSE;
04916        }
04917 
04918        buffer[buffer_length] = 0;
04919        RETVAL_STRINGL((char *)buffer, buffer_length, 0);
04920 
04921        if (zstrong_result_returned) {
04922               ZVAL_BOOL(zstrong_result_returned, strong_result);
04923        }
04924 }
04925 /* }}} */
04926 
04927 /*
04928  * Local variables:
04929  * tab-width: 8
04930  * c-basic-offset: 8
04931  * End:
04932  * vim600: sw=4 ts=4 fdm=marker
04933  * vim<600: sw=4 ts=4
04934  */