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