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