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