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