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