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