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