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