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