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