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