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
1763int 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
1790PHP_FUNCTION(openssl_x509_fingerprint)
1791{
1792    X509 *cert;
1793    zval **zcert;
1794    long certresource;
1795    zend_bool raw_output = 0;
1796    char *method = "sha1";
1797    int method_len;
1798
1799    char *fingerprint;
1800    int fingerprint_len;
1801
1802    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|sb", &zcert, &method, &method_len, &raw_output) == FAILURE) {
1803        return;
1804    }
1805
1806    cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1807    if (cert == NULL) {
1808        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
1809        RETURN_FALSE;
1810    }
1811
1812    if (php_openssl_x509_fingerprint(cert, method, raw_output, &fingerprint, &fingerprint_len TSRMLS_CC) == SUCCESS) {
1813        RETVAL_STRINGL(fingerprint, fingerprint_len, 0);
1814    } else {
1815        RETVAL_FALSE;
1816    }
1817
1818    if (certresource == -1 && cert) {
1819        X509_free(cert);
1820    }
1821}
1822
1823/* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key)
1824   Checks if a private key corresponds to a CERT */
1825PHP_FUNCTION(openssl_x509_check_private_key)
1826{
1827    zval ** zcert, **zkey;
1828    X509 * cert = NULL;
1829    EVP_PKEY * key = NULL;
1830    long certresource = -1, keyresource = -1;
1831
1832    RETVAL_FALSE;
1833
1834    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ", &zcert, &zkey) == FAILURE) {
1835        return;
1836    }
1837    cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1838    if (cert == NULL) {
1839        RETURN_FALSE;
1840    }
1841    key = php_openssl_evp_from_zval(zkey, 0, "", 1, &keyresource TSRMLS_CC);
1842    if (key) {
1843        RETVAL_BOOL(X509_check_private_key(cert, key));
1844    }
1845
1846    if (keyresource == -1 && key) {
1847        EVP_PKEY_free(key);
1848    }
1849    if (certresource == -1 && cert) {
1850        X509_free(cert);
1851    }
1852}
1853/* }}} */
1854
1855/* Special handling of subjectAltName, see CVE-2013-4073
1856 * Christian Heimes
1857 */
1858
1859static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
1860{
1861    GENERAL_NAMES *names;
1862    const X509V3_EXT_METHOD *method = NULL;
1863    long i, length, num;
1864    const unsigned char *p;
1865
1866    method = X509V3_EXT_get(extension);
1867    if (method == NULL) {
1868        return -1;
1869    }
1870
1871    p = extension->value->data;
1872    length = extension->value->length;
1873    if (method->it) {
1874        names = (GENERAL_NAMES*)(ASN1_item_d2i(NULL, &p, length,
1875                               ASN1_ITEM_ptr(method->it)));
1876    } else {
1877        names = (GENERAL_NAMES*)(method->d2i(NULL, &p, length));
1878    }
1879    if (names == NULL) {
1880        return -1;
1881    }
1882
1883    num = sk_GENERAL_NAME_num(names);
1884    for (i = 0; i < num; i++) {
1885            GENERAL_NAME *name;
1886            ASN1_STRING *as;
1887            name = sk_GENERAL_NAME_value(names, i);
1888            switch (name->type) {
1889                case GEN_EMAIL:
1890                    BIO_puts(bio, "email:");
1891                    as = name->d.rfc822Name;
1892                    BIO_write(bio, ASN1_STRING_data(as),
1893                          ASN1_STRING_length(as));
1894                    break;
1895                case GEN_DNS:
1896                    BIO_puts(bio, "DNS:");
1897                    as = name->d.dNSName;
1898                    BIO_write(bio, ASN1_STRING_data(as),
1899                          ASN1_STRING_length(as));
1900                    break;
1901                case GEN_URI:
1902                    BIO_puts(bio, "URI:");
1903                    as = name->d.uniformResourceIdentifier;
1904                    BIO_write(bio, ASN1_STRING_data(as),
1905                          ASN1_STRING_length(as));
1906                    break;
1907                default:
1908                    /* use builtin print for GEN_OTHERNAME, GEN_X400,
1909                     * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID
1910                     */
1911                    GENERAL_NAME_print(bio, name);
1912            }
1913            /* trailing ', ' except for last element */
1914            if (i < (num - 1)) {
1915                BIO_puts(bio, ", ");
1916            }
1917    }
1918    sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1919
1920    return 0;
1921}
1922
1923/* {{{ proto array openssl_x509_parse(mixed x509 [, bool shortnames=true])
1924   Returns an array of the fields/values of the CERT */
1925PHP_FUNCTION(openssl_x509_parse)
1926{
1927    zval ** zcert;
1928    X509 * cert = NULL;
1929    long certresource = -1;
1930    int i;
1931    zend_bool useshortnames = 1;
1932    char * tmpstr;
1933    zval * subitem;
1934    X509_EXTENSION *extension;
1935    char *extname;
1936    BIO  *bio_out;
1937    BUF_MEM *bio_buf;
1938    char buf[256];
1939
1940    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcert, &useshortnames) == FAILURE) {
1941        return;
1942    }
1943    cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
1944    if (cert == NULL) {
1945        RETURN_FALSE;
1946    }
1947    array_init(return_value);
1948
1949    if (cert->name) {
1950        add_assoc_string(return_value, "name", cert->name, 1);
1951    }
1952/*  add_assoc_bool(return_value, "valid", cert->valid); */
1953
1954    add_assoc_name_entry(return_value, "subject",       X509_get_subject_name(cert), useshortnames TSRMLS_CC);
1955    /* hash as used in CA directories to lookup cert by subject name */
1956    {
1957        char buf[32];
1958        snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert));
1959        add_assoc_string(return_value, "hash", buf, 1);
1960    }
1961
1962    add_assoc_name_entry(return_value, "issuer",        X509_get_issuer_name(cert), useshortnames TSRMLS_CC);
1963    add_assoc_long(return_value, "version",             X509_get_version(cert));
1964
1965    add_assoc_string(return_value, "serialNumber", i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(cert)), 1);
1966
1967    add_assoc_asn1_string(return_value, "validFrom",    X509_get_notBefore(cert));
1968    add_assoc_asn1_string(return_value, "validTo",      X509_get_notAfter(cert));
1969
1970    add_assoc_long(return_value, "validFrom_time_t",    asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC));
1971    add_assoc_long(return_value, "validTo_time_t",      asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC));
1972
1973    tmpstr = (char *)X509_alias_get0(cert, NULL);
1974    if (tmpstr) {
1975        add_assoc_string(return_value, "alias", tmpstr, 1);
1976    }
1977/*
1978    add_assoc_long(return_value, "signaturetypeLONG", X509_get_signature_type(cert));
1979    add_assoc_string(return_value, "signaturetype", OBJ_nid2sn(X509_get_signature_type(cert)), 1);
1980    add_assoc_string(return_value, "signaturetypeLN", OBJ_nid2ln(X509_get_signature_type(cert)), 1);
1981*/
1982    MAKE_STD_ZVAL(subitem);
1983    array_init(subitem);
1984
1985    /* NOTE: the purposes are added as integer keys - the keys match up to the X509_PURPOSE_SSL_XXX defines
1986       in x509v3.h */
1987    for (i = 0; i < X509_PURPOSE_get_count(); i++) {
1988        int id, purpset;
1989        char * pname;
1990        X509_PURPOSE * purp;
1991        zval * subsub;
1992
1993        MAKE_STD_ZVAL(subsub);
1994        array_init(subsub);
1995
1996        purp = X509_PURPOSE_get0(i);
1997        id = X509_PURPOSE_get_id(purp);
1998
1999        purpset = X509_check_purpose(cert, id, 0);
2000        add_index_bool(subsub, 0, purpset);
2001
2002        purpset = X509_check_purpose(cert, id, 1);
2003        add_index_bool(subsub, 1, purpset);
2004
2005        pname = useshortnames ? X509_PURPOSE_get0_sname(purp) : X509_PURPOSE_get0_name(purp);
2006        add_index_string(subsub, 2, pname, 1);
2007
2008        /* NOTE: if purpset > 1 then it's a warning - we should mention it ? */
2009
2010        add_index_zval(subitem, id, subsub);
2011    }
2012    add_assoc_zval(return_value, "purposes", subitem);
2013
2014    MAKE_STD_ZVAL(subitem);
2015    array_init(subitem);
2016
2017
2018    for (i = 0; i < X509_get_ext_count(cert); i++) {
2019        int nid;
2020        extension = X509_get_ext(cert, i);
2021        nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
2022        if (nid != NID_undef) {
2023            extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension)));
2024        } else {
2025            OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1);
2026            extname = buf;
2027        }
2028        bio_out = BIO_new(BIO_s_mem());
2029        if (nid == NID_subject_alt_name) {
2030            if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
2031                BIO_get_mem_ptr(bio_out, &bio_buf);
2032                add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1);
2033            } else {
2034                zval_dtor(return_value);
2035                if (certresource == -1 && cert) {
2036                    X509_free(cert);
2037                }
2038                BIO_free(bio_out);
2039                RETURN_FALSE;
2040            }
2041        }
2042        else if (X509V3_EXT_print(bio_out, extension, 0, 0)) {
2043            BIO_get_mem_ptr(bio_out, &bio_buf);
2044            add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1);
2045        } else {
2046            add_assoc_asn1_string(subitem, extname, X509_EXTENSION_get_data(extension));
2047        }
2048        BIO_free(bio_out);
2049    }
2050    add_assoc_zval(return_value, "extensions", subitem);
2051
2052    if (certresource == -1 && cert) {
2053        X509_free(cert);
2054    }
2055}
2056/* }}} */
2057
2058/* {{{ load_all_certs_from_file */
2059static STACK_OF(X509) * load_all_certs_from_file(char *certfile)
2060{
2061    STACK_OF(X509_INFO) *sk=NULL;
2062    STACK_OF(X509) *stack=NULL, *ret=NULL;
2063    BIO *in=NULL;
2064    X509_INFO *xi;
2065    TSRMLS_FETCH();
2066
2067    if(!(stack = sk_X509_new_null())) {
2068        php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure");
2069        goto end;
2070    }
2071
2072    if (php_openssl_open_base_dir_chk(certfile TSRMLS_CC)) {
2073        sk_X509_free(stack);
2074        goto end;
2075    }
2076
2077    if(!(in=BIO_new_file(certfile, "r"))) {
2078        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening the file, %s", certfile);
2079        sk_X509_free(stack);
2080        goto end;
2081    }
2082
2083    /* This loads from a file, a stack of x509/crl/pkey sets */
2084    if(!(sk=PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) {
2085        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error reading the file, %s", certfile);
2086        sk_X509_free(stack);
2087        goto end;
2088    }
2089
2090    /* scan over it and pull out the certs */
2091    while (sk_X509_INFO_num(sk)) {
2092        xi=sk_X509_INFO_shift(sk);
2093        if (xi->x509 != NULL) {
2094            sk_X509_push(stack,xi->x509);
2095            xi->x509=NULL;
2096        }
2097        X509_INFO_free(xi);
2098    }
2099    if(!sk_X509_num(stack)) {
2100        php_error_docref(NULL TSRMLS_CC, E_WARNING, "no certificates in file, %s", certfile);
2101        sk_X509_free(stack);
2102        goto end;
2103    }
2104    ret=stack;
2105end:
2106    BIO_free(in);
2107    sk_X509_INFO_free(sk);
2108
2109    return ret;
2110}
2111/* }}} */
2112
2113/* {{{ check_cert */
2114static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose)
2115{
2116    int ret=0;
2117    X509_STORE_CTX *csc;
2118    TSRMLS_FETCH();
2119
2120    csc = X509_STORE_CTX_new();
2121    if (csc == NULL) {
2122        php_error_docref(NULL TSRMLS_CC, E_ERROR, "memory allocation failure");
2123        return 0;
2124    }
2125    X509_STORE_CTX_init(csc, ctx, x, untrustedchain);
2126    if(purpose >= 0) {
2127        X509_STORE_CTX_set_purpose(csc, purpose);
2128    }
2129    ret = X509_verify_cert(csc);
2130    X509_STORE_CTX_free(csc);
2131
2132    return ret;
2133}
2134/* }}} */
2135
2136/* {{{ proto int openssl_x509_checkpurpose(mixed x509cert, int purpose, array cainfo [, string untrustedfile])
2137   Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs */
2138PHP_FUNCTION(openssl_x509_checkpurpose)
2139{
2140    zval ** zcert, * zcainfo = NULL;
2141    X509_STORE * cainfo = NULL;
2142    X509 * cert = NULL;
2143    long certresource = -1;
2144    STACK_OF(X509) * untrustedchain = NULL;
2145    long purpose;
2146    char * untrusted = NULL;
2147    int untrusted_len = 0, ret;
2148
2149    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zl|a!s", &zcert, &purpose, &zcainfo, &untrusted, &untrusted_len) == FAILURE) {
2150        return;
2151    }
2152
2153    RETVAL_LONG(-1);
2154
2155    if (untrusted) {
2156        untrustedchain = load_all_certs_from_file(untrusted);
2157        if (untrustedchain == NULL) {
2158            goto clean_exit;
2159        }
2160    }
2161
2162    cainfo = setup_verify(zcainfo TSRMLS_CC);
2163    if (cainfo == NULL) {
2164        goto clean_exit;
2165    }
2166    cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
2167    if (cert == NULL) {
2168        goto clean_exit;
2169    }
2170
2171    ret = check_cert(cainfo, cert, untrustedchain, purpose);
2172    if (ret != 0 && ret != 1) {
2173        RETVAL_LONG(ret);
2174    } else {
2175        RETVAL_BOOL(ret);
2176    }
2177
2178clean_exit:
2179    if (certresource == 1 && cert) {
2180        X509_free(cert);
2181    }
2182    if (cainfo) {
2183        X509_STORE_free(cainfo);
2184    }
2185    if (untrustedchain) {
2186        sk_X509_pop_free(untrustedchain, X509_free);
2187    }
2188}
2189/* }}} */
2190
2191/* {{{ setup_verify
2192 * calist is an array containing file and directory names.  create a
2193 * certificate store and add those certs to it for use in verification.
2194*/
2195static X509_STORE * setup_verify(zval * calist TSRMLS_DC)
2196{
2197    X509_STORE *store;
2198    X509_LOOKUP * dir_lookup, * file_lookup;
2199    HashPosition pos;
2200    int ndirs = 0, nfiles = 0;
2201
2202    store = X509_STORE_new();
2203
2204    if (store == NULL) {
2205        return NULL;
2206    }
2207
2208    if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) {
2209        zend_hash_internal_pointer_reset_ex(HASH_OF(calist), &pos);
2210        for (;; zend_hash_move_forward_ex(HASH_OF(calist), &pos)) {
2211            zval ** item;
2212            struct stat sb;
2213
2214            if (zend_hash_get_current_data_ex(HASH_OF(calist), (void**)&item, &pos) == FAILURE) {
2215                break;
2216            }
2217            convert_to_string_ex(item);
2218
2219            if (VCWD_STAT(Z_STRVAL_PP(item), &sb) == -1) {
2220                php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to stat %s", Z_STRVAL_PP(item));
2221                continue;
2222            }
2223
2224            if ((sb.st_mode & S_IFREG) == S_IFREG) {
2225                file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
2226                if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) {
2227                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading file %s", Z_STRVAL_PP(item));
2228                } else {
2229                    nfiles++;
2230                }
2231                file_lookup = NULL;
2232            } else {
2233                dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
2234                if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_PP(item), X509_FILETYPE_PEM)) {
2235                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "error loading directory %s", Z_STRVAL_PP(item));
2236                } else {
2237                    ndirs++;
2238                }
2239                dir_lookup = NULL;
2240            }
2241        }
2242    }
2243    if (nfiles == 0) {
2244        file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
2245        if (file_lookup) {
2246            X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT);
2247        }
2248    }
2249    if (ndirs == 0) {
2250        dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
2251        if (dir_lookup) {
2252            X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT);
2253        }
2254    }
2255    return store;
2256}
2257/* }}} */
2258
2259/* {{{ proto resource openssl_x509_read(mixed cert)
2260   Reads X.509 certificates */
2261PHP_FUNCTION(openssl_x509_read)
2262{
2263    zval **cert;
2264    X509 *x509;
2265
2266    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) {
2267        return;
2268    }
2269    Z_TYPE_P(return_value) = IS_RESOURCE;
2270    x509 = php_openssl_x509_from_zval(cert, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
2271
2272    if (x509 == NULL) {
2273        php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied parameter cannot be coerced into an X509 certificate!");
2274        RETURN_FALSE;
2275    }
2276}
2277/* }}} */
2278
2279/* {{{ proto void openssl_x509_free(resource x509)
2280   Frees X.509 certificates */
2281PHP_FUNCTION(openssl_x509_free)
2282{
2283    zval *x509;
2284    X509 *cert;
2285
2286    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &x509) == FAILURE) {
2287        return;
2288    }
2289    ZEND_FETCH_RESOURCE(cert, X509 *, &x509, -1, "OpenSSL X.509", le_x509);
2290    zend_list_delete(Z_LVAL_P(x509));
2291}
2292/* }}} */
2293
2294/* }}} */
2295
2296/* Pop all X509 from Stack and free them, free the stack afterwards */
2297static void php_sk_X509_free(STACK_OF(X509) * sk) /* {{{ */
2298{
2299    for (;;) {
2300        X509* x = sk_X509_pop(sk);
2301        if (!x) break;
2302        X509_free(x);
2303    }
2304    sk_X509_free(sk);
2305}
2306/* }}} */
2307
2308static STACK_OF(X509) * php_array_to_X509_sk(zval ** zcerts TSRMLS_DC) /* {{{ */
2309{
2310    HashPosition hpos;
2311    zval ** zcertval;
2312    STACK_OF(X509) * sk = NULL;
2313    X509 * cert;
2314    long certresource;
2315
2316    sk = sk_X509_new_null();
2317
2318    /* get certs */
2319    if (Z_TYPE_PP(zcerts) == IS_ARRAY) {
2320        zend_hash_internal_pointer_reset_ex(HASH_OF(*zcerts), &hpos);
2321        while(zend_hash_get_current_data_ex(HASH_OF(*zcerts), (void**)&zcertval, &hpos) == SUCCESS) {
2322
2323            cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC);
2324            if (cert == NULL) {
2325                goto clean_exit;
2326            }
2327
2328            if (certresource != -1) {
2329                cert = X509_dup(cert);
2330
2331                if (cert == NULL) {
2332                    goto clean_exit;
2333                }
2334
2335            }
2336            sk_X509_push(sk, cert);
2337
2338            zend_hash_move_forward_ex(HASH_OF(*zcerts), &hpos);
2339        }
2340    } else {
2341        /* a single certificate */
2342        cert = php_openssl_x509_from_zval(zcerts, 0, &certresource TSRMLS_CC);
2343
2344        if (cert == NULL) {
2345            goto clean_exit;
2346        }
2347
2348        if (certresource != -1) {
2349            cert = X509_dup(cert);
2350            if (cert == NULL) {
2351                goto clean_exit;
2352            }
2353        }
2354        sk_X509_push(sk, cert);
2355    }
2356
2357  clean_exit:
2358    return sk;
2359}
2360/* }}} */
2361
2362/* {{{ proto bool openssl_pkcs12_export_to_file(mixed x509, string filename, mixed priv_key, string pass[, array args])
2363   Creates and exports a PKCS to file */
2364PHP_FUNCTION(openssl_pkcs12_export_to_file)
2365{
2366    X509 * cert = NULL;
2367    BIO * bio_out = NULL;
2368    PKCS12 * p12 = NULL;
2369    char * filename;
2370    char * friendly_name = NULL;
2371    int filename_len;
2372    char * pass;
2373    int pass_len;
2374    zval **zcert = NULL, *zpkey = NULL, *args = NULL;
2375    EVP_PKEY *priv_key = NULL;
2376    long certresource, keyresource;
2377    zval ** item;
2378    STACK_OF(X509) *ca = NULL;
2379
2380    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zpzs|a", &zcert, &filename, &filename_len, &zpkey, &pass, &pass_len, &args) == FAILURE)
2381        return;
2382
2383    RETVAL_FALSE;
2384
2385    cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
2386    if (cert == NULL) {
2387        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
2388        return;
2389    }
2390    priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC);
2391    if (priv_key == NULL) {
2392        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
2393        goto cleanup;
2394    }
2395    if (cert && !X509_check_private_key(cert, priv_key)) {
2396        php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert");
2397        goto cleanup;
2398    }
2399    if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) {
2400        goto cleanup;
2401    }
2402
2403    /* parse extra config from args array, promote this to an extra function */
2404    if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS && Z_TYPE_PP(item) == IS_STRING)
2405        friendly_name = Z_STRVAL_PP(item);
2406    /* certpbe (default RC2-40)
2407       keypbe (default 3DES)
2408       friendly_caname
2409    */
2410
2411    if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS)
2412        ca = php_array_to_X509_sk(item TSRMLS_CC);
2413    /* end parse extra config */
2414
2415    /*PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ca,
2416                                       int nid_key, int nid_cert, int iter, int mac_iter, int keytype);*/
2417
2418    p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
2419
2420    bio_out = BIO_new_file(filename, "w");
2421    if (bio_out) {
2422
2423        i2d_PKCS12_bio(bio_out, p12);
2424
2425        RETVAL_TRUE;
2426    } else {
2427        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename);
2428    }
2429
2430    BIO_free(bio_out);
2431    PKCS12_free(p12);
2432    php_sk_X509_free(ca);
2433
2434cleanup:
2435
2436    if (keyresource == -1 && priv_key) {
2437        EVP_PKEY_free(priv_key);
2438    }
2439    if (certresource == -1 && cert) {
2440        X509_free(cert);
2441    }
2442}
2443/* }}} */
2444
2445/* {{{ proto bool openssl_pkcs12_export(mixed x509, string &out, mixed priv_key, string pass[, array args])
2446   Creates and exports a PKCS12 to a var */
2447PHP_FUNCTION(openssl_pkcs12_export)
2448{
2449    X509 * cert = NULL;
2450    BIO * bio_out;
2451    PKCS12 * p12 = NULL;
2452    zval * zcert = NULL, *zout = NULL, *zpkey, *args = NULL;
2453    EVP_PKEY *priv_key = NULL;
2454    long certresource, keyresource;
2455    char * pass;
2456    int pass_len;
2457    char * friendly_name = NULL;
2458    zval ** item;
2459    STACK_OF(X509) *ca = NULL;
2460
2461    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzzs|a", &zcert, &zout, &zpkey, &pass, &pass_len, &args) == FAILURE)
2462        return;
2463
2464    RETVAL_FALSE;
2465
2466    cert = php_openssl_x509_from_zval(&zcert, 0, &certresource TSRMLS_CC);
2467    if (cert == NULL) {
2468        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 1");
2469        return;
2470    }
2471    priv_key = php_openssl_evp_from_zval(&zpkey, 0, "", 1, &keyresource TSRMLS_CC);
2472    if (priv_key == NULL) {
2473        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
2474        goto cleanup;
2475    }
2476    if (cert && !X509_check_private_key(cert, priv_key)) {
2477        php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to cert");
2478        goto cleanup;
2479    }
2480
2481    /* parse extra config from args array, promote this to an extra function */
2482    if (args && zend_hash_find(Z_ARRVAL_P(args), "friendly_name", sizeof("friendly_name"), (void**)&item) == SUCCESS && Z_TYPE_PP(item) == IS_STRING)
2483        friendly_name = Z_STRVAL_PP(item);
2484
2485    if (args && zend_hash_find(Z_ARRVAL_P(args), "extracerts", sizeof("extracerts"), (void**)&item) == SUCCESS)
2486        ca = php_array_to_X509_sk(item TSRMLS_CC);
2487    /* end parse extra config */
2488
2489    p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
2490
2491    bio_out = BIO_new(BIO_s_mem());
2492    if (i2d_PKCS12_bio(bio_out, p12))  {
2493        BUF_MEM *bio_buf;
2494
2495        zval_dtor(zout);
2496        BIO_get_mem_ptr(bio_out, &bio_buf);
2497        ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1);
2498
2499        RETVAL_TRUE;
2500    }
2501
2502    BIO_free(bio_out);
2503    PKCS12_free(p12);
2504    php_sk_X509_free(ca);
2505
2506cleanup:
2507
2508    if (keyresource == -1 && priv_key) {
2509        EVP_PKEY_free(priv_key);
2510    }
2511    if (certresource == -1 && cert) {
2512        X509_free(cert);
2513    }
2514}
2515/* }}} */
2516
2517/* {{{ proto bool openssl_pkcs12_read(string PKCS12, array &certs, string pass)
2518   Parses a PKCS12 to an array */
2519PHP_FUNCTION(openssl_pkcs12_read)
2520{
2521    zval *zout = NULL, *zextracerts, *zcert, *zpkey;
2522    char *pass, *zp12;
2523    int pass_len, zp12_len;
2524    PKCS12 * p12 = NULL;
2525    EVP_PKEY * pkey = NULL;
2526    X509 * cert = NULL;
2527    STACK_OF(X509) * ca = NULL;
2528    BIO * bio_in = NULL;
2529    int i;
2530
2531    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szs", &zp12, &zp12_len, &zout, &pass, &pass_len) == FAILURE)
2532        return;
2533
2534    RETVAL_FALSE;
2535
2536    bio_in = BIO_new(BIO_s_mem());
2537
2538    if(!BIO_write(bio_in, zp12, zp12_len))
2539        goto cleanup;
2540
2541    if(d2i_PKCS12_bio(bio_in, &p12)) {
2542        if(PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
2543            BIO * bio_out;
2544
2545            zval_dtor(zout);
2546            array_init(zout);
2547
2548            bio_out = BIO_new(BIO_s_mem());
2549            if (PEM_write_bio_X509(bio_out, cert)) {
2550                BUF_MEM *bio_buf;
2551                BIO_get_mem_ptr(bio_out, &bio_buf);
2552                MAKE_STD_ZVAL(zcert);
2553                ZVAL_STRINGL(zcert, bio_buf->data, bio_buf->length, 1);
2554                add_assoc_zval(zout, "cert", zcert);
2555            }
2556            BIO_free(bio_out);
2557
2558            bio_out = BIO_new(BIO_s_mem());
2559            if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
2560                BUF_MEM *bio_buf;
2561                BIO_get_mem_ptr(bio_out, &bio_buf);
2562                MAKE_STD_ZVAL(zpkey);
2563                ZVAL_STRINGL(zpkey, bio_buf->data, bio_buf->length, 1);
2564                add_assoc_zval(zout, "pkey", zpkey);
2565            }
2566            BIO_free(bio_out);
2567
2568            MAKE_STD_ZVAL(zextracerts);
2569            array_init(zextracerts);
2570
2571            for (i=0;;i++) {
2572                zval * zextracert;
2573                X509* aCA = sk_X509_pop(ca);
2574                if (!aCA) break;
2575
2576                bio_out = BIO_new(BIO_s_mem());
2577                if (PEM_write_bio_X509(bio_out, aCA)) {
2578                    BUF_MEM *bio_buf;
2579                    BIO_get_mem_ptr(bio_out, &bio_buf);
2580                    MAKE_STD_ZVAL(zextracert);
2581                    ZVAL_STRINGL(zextracert, bio_buf->data, bio_buf->length, 1);
2582                    add_index_zval(zextracerts, i, zextracert);
2583
2584                }
2585                BIO_free(bio_out);
2586
2587                X509_free(aCA);
2588            }
2589            if(ca) {
2590                sk_X509_free(ca);
2591                add_assoc_zval(zout, "extracerts", zextracerts);
2592            } else {
2593                zval_dtor(zextracerts);
2594            }
2595
2596            RETVAL_TRUE;
2597
2598            PKCS12_free(p12);
2599        }
2600    }
2601
2602  cleanup:
2603    if (bio_in) {
2604        BIO_free(bio_in);
2605    }
2606    if (pkey) {
2607        EVP_PKEY_free(pkey);
2608    }
2609    if (cert) {
2610        X509_free(cert);
2611    }
2612}
2613/* }}} */
2614
2615/* {{{ x509 CSR functions */
2616
2617/* {{{ php_openssl_make_REQ */
2618static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs TSRMLS_DC)
2619{
2620    STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
2621    char * str, *dn_sect, *attr_sect;
2622
2623    dn_sect = CONF_get_string(req->req_config, req->section_name, "distinguished_name");
2624    if (dn_sect == NULL) {
2625        return FAILURE;
2626    }
2627    dn_sk = CONF_get_section(req->req_config, dn_sect);
2628    if (dn_sk == NULL) {
2629        return FAILURE;
2630    }
2631    attr_sect = CONF_get_string(req->req_config, req->section_name, "attributes");
2632    if (attr_sect == NULL) {
2633        attr_sk = NULL;
2634    } else {
2635        attr_sk = CONF_get_section(req->req_config, attr_sect);
2636        if (attr_sk == NULL) {
2637            return FAILURE;
2638        }
2639    }
2640    /* setup the version number: version 1 */
2641    if (X509_REQ_set_version(csr, 0L)) {
2642        int i, nid;
2643        char * type;
2644        CONF_VALUE * v;
2645        X509_NAME * subj;
2646        HashPosition hpos;
2647        zval ** item;
2648
2649        subj = X509_REQ_get_subject_name(csr);
2650        /* apply values from the dn hash */
2651        zend_hash_internal_pointer_reset_ex(HASH_OF(dn), &hpos);
2652        while(zend_hash_get_current_data_ex(HASH_OF(dn), (void**)&item, &hpos) == SUCCESS) {
2653            char * strindex = NULL;
2654            uint strindexlen = 0;
2655            ulong intindex;
2656
2657            zend_hash_get_current_key_ex(HASH_OF(dn), &strindex, &strindexlen, &intindex, 0, &hpos);
2658
2659            convert_to_string_ex(item);
2660
2661            if (strindex) {
2662                int nid;
2663
2664                nid = OBJ_txt2nid(strindex);
2665                if (nid != NID_undef) {
2666                    if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8,
2667                                (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0))
2668                    {
2669                        php_error_docref(NULL TSRMLS_CC, E_WARNING,
2670                            "dn: add_entry_by_NID %d -> %s (failed; check error"
2671                            " queue and value of string_mask OpenSSL option "
2672                            "if illegal characters are reported)",
2673                            nid, Z_STRVAL_PP(item));
2674                        return FAILURE;
2675                    }
2676                } else {
2677                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex);
2678                }
2679            }
2680            zend_hash_move_forward_ex(HASH_OF(dn), &hpos);
2681        }
2682
2683        /* Finally apply defaults from config file */
2684        for(i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
2685            int len;
2686            char buffer[200 + 1]; /*200 + \0 !*/
2687
2688            v = sk_CONF_VALUE_value(dn_sk, i);
2689            type = v->name;
2690
2691            len = strlen(type);
2692            if (len < sizeof("_default")) {
2693                continue;
2694            }
2695            len -= sizeof("_default") - 1;
2696            if (strcmp("_default", type + len) != 0) {
2697                continue;
2698            }
2699            if (len > 200) {
2700                len = 200;
2701            }
2702            memcpy(buffer, type, len);
2703            buffer[len] = '\0';
2704            type = buffer;
2705
2706            /* Skip past any leading X. X: X, etc to allow for multiple
2707             * instances */
2708            for (str = type; *str; str++) {
2709                if (*str == ':' || *str == ',' || *str == '.') {
2710                    str++;
2711                    if (*str) {
2712                        type = str;
2713                    }
2714                    break;
2715                }
2716            }
2717            /* if it is already set, skip this */
2718            nid = OBJ_txt2nid(type);
2719            if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) {
2720                continue;
2721            }
2722            if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) {
2723                php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
2724                return FAILURE;
2725            }
2726            if (!X509_NAME_entry_count(subj)) {
2727                php_error_docref(NULL TSRMLS_CC, E_WARNING, "no objects specified in config file");
2728                return FAILURE;
2729            }
2730        }
2731        if (attribs) {
2732            zend_hash_internal_pointer_reset_ex(HASH_OF(attribs), &hpos);
2733            while(zend_hash_get_current_data_ex(HASH_OF(attribs), (void**)&item, &hpos) == SUCCESS) {
2734                char *strindex = NULL;
2735                uint strindexlen;
2736                ulong intindex;
2737
2738                zend_hash_get_current_key_ex(HASH_OF(attribs), &strindex, &strindexlen, &intindex, 0, &hpos);
2739                convert_to_string_ex(item);
2740
2741                if (strindex) {
2742                    int nid;
2743
2744                    nid = OBJ_txt2nid(strindex);
2745                    if (nid != NID_undef) {
2746                        if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)Z_STRVAL_PP(item), -1, -1, 0)) {
2747                            php_error_docref(NULL TSRMLS_CC, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_PP(item));
2748                            return FAILURE;
2749                        }
2750                    } else {
2751                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "dn: %s is not a recognized name", strindex);
2752                    }
2753                }
2754                zend_hash_move_forward_ex(HASH_OF(attribs), &hpos);
2755            }
2756            for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
2757                v = sk_CONF_VALUE_value(attr_sk, i);
2758                /* if it is already set, skip this */
2759                nid = OBJ_txt2nid(v->name);
2760                if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) {
2761                    continue;
2762                }
2763                if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) {
2764                    php_error_docref(NULL TSRMLS_CC, E_WARNING,
2765                        "add1_attr_by_txt %s -> %s (failed; check error queue "
2766                        "and value of string_mask OpenSSL option if illegal "
2767                        "characters are reported)",
2768                        v->name, v->value);
2769                    return FAILURE;
2770                }
2771            }
2772        }
2773    }
2774
2775    X509_REQ_set_pubkey(csr, req->priv_key);
2776    return SUCCESS;
2777}
2778/* }}} */
2779
2780/* {{{ php_openssl_csr_from_zval */
2781static X509_REQ * php_openssl_csr_from_zval(zval ** val, int makeresource, long * resourceval TSRMLS_DC)
2782{
2783    X509_REQ * csr = NULL;
2784    char * filename = NULL;
2785    BIO * in;
2786
2787    if (resourceval) {
2788        *resourceval = -1;
2789    }
2790    if (Z_TYPE_PP(val) == IS_RESOURCE) {
2791        void * what;
2792        int type;
2793
2794        what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509 CSR", &type, 1, le_csr);
2795        if (what) {
2796            if (resourceval) {
2797                *resourceval = Z_LVAL_PP(val);
2798            }
2799            return (X509_REQ*)what;
2800        }
2801        return NULL;
2802    } else if (Z_TYPE_PP(val) != IS_STRING) {
2803        return NULL;
2804    }
2805
2806    if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
2807        filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
2808    }
2809    if (filename) {
2810        if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) {
2811            return NULL;
2812        }
2813        in = BIO_new_file(filename, "r");
2814    } else {
2815        in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
2816    }
2817    csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
2818    BIO_free(in);
2819
2820    return csr;
2821}
2822/* }}} */
2823
2824/* {{{ proto bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true])
2825   Exports a CSR to file */
2826PHP_FUNCTION(openssl_csr_export_to_file)
2827{
2828    X509_REQ * csr;
2829    zval * zcsr = NULL;
2830    zend_bool notext = 1;
2831    char * filename = NULL; int filename_len;
2832    BIO * bio_out;
2833    long csr_resource;
2834
2835    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp|b", &zcsr, &filename, &filename_len, &notext) == FAILURE) {
2836        return;
2837    }
2838    RETVAL_FALSE;
2839
2840    csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC);
2841    if (csr == NULL) {
2842        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
2843        return;
2844    }
2845
2846    if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) {
2847        return;
2848    }
2849
2850    bio_out = BIO_new_file(filename, "w");
2851    if (bio_out) {
2852        if (!notext) {
2853            X509_REQ_print(bio_out, csr);
2854        }
2855        PEM_write_bio_X509_REQ(bio_out, csr);
2856        RETVAL_TRUE;
2857    } else {
2858        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening file %s", filename);
2859    }
2860
2861    if (csr_resource == -1 && csr) {
2862        X509_REQ_free(csr);
2863    }
2864    BIO_free(bio_out);
2865}
2866/* }}} */
2867
2868/* {{{ proto bool openssl_csr_export(resource csr, string &out [, bool notext=true])
2869   Exports a CSR to file or a var */
2870PHP_FUNCTION(openssl_csr_export)
2871{
2872    X509_REQ * csr;
2873    zval * zcsr = NULL, *zout=NULL;
2874    zend_bool notext = 1;
2875    BIO * bio_out;
2876
2877    long csr_resource;
2878
2879    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|b", &zcsr, &zout, &notext) == FAILURE) {
2880        return;
2881    }
2882    RETVAL_FALSE;
2883
2884    csr = php_openssl_csr_from_zval(&zcsr, 0, &csr_resource TSRMLS_CC);
2885    if (csr == NULL) {
2886        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
2887        return;
2888    }
2889
2890    /* export to a var */
2891
2892    bio_out = BIO_new(BIO_s_mem());
2893    if (!notext) {
2894        X509_REQ_print(bio_out, csr);
2895    }
2896
2897    if (PEM_write_bio_X509_REQ(bio_out, csr)) {
2898        BUF_MEM *bio_buf;
2899
2900        BIO_get_mem_ptr(bio_out, &bio_buf);
2901        zval_dtor(zout);
2902        ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length, 1);
2903
2904        RETVAL_TRUE;
2905    }
2906
2907    if (csr_resource == -1 && csr) {
2908        X509_REQ_free(csr);
2909    }
2910    BIO_free(bio_out);
2911}
2912/* }}} */
2913
2914/* {{{ proto resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]])
2915   Signs a cert with another CERT */
2916PHP_FUNCTION(openssl_csr_sign)
2917{
2918    zval ** zcert = NULL, **zcsr, **zpkey, *args = NULL;
2919    long num_days;
2920    long serial = 0L;
2921    X509 * cert = NULL, *new_cert = NULL;
2922    X509_REQ * csr;
2923    EVP_PKEY * key = NULL, *priv_key = NULL;
2924    long csr_resource, certresource = 0, keyresource = -1;
2925    int i;
2926    struct php_x509_request req;
2927
2928    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ!Zl|a!l", &zcsr, &zcert, &zpkey, &num_days, &args, &serial) == FAILURE)
2929        return;
2930
2931    RETVAL_FALSE;
2932    PHP_SSL_REQ_INIT(&req);
2933
2934    csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
2935    if (csr == NULL) {
2936        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get CSR from parameter 1");
2937        return;
2938    }
2939    if (zcert) {
2940        cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
2941        if (cert == NULL) {
2942            php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get cert from parameter 2");
2943            goto cleanup;
2944        }
2945    }
2946    priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 1, &keyresource TSRMLS_CC);
2947    if (priv_key == NULL) {
2948        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get private key from parameter 3");
2949        goto cleanup;
2950    }
2951    if (cert && !X509_check_private_key(cert, priv_key)) {
2952        php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key does not correspond to signing cert");
2953        goto cleanup;
2954    }
2955
2956    if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) {
2957        goto cleanup;
2958    }
2959    /* Check that the request matches the signature */
2960    key = X509_REQ_get_pubkey(csr);
2961    if (key == NULL) {
2962        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error unpacking public key");
2963        goto cleanup;
2964    }
2965    i = X509_REQ_verify(csr, key);
2966
2967    if (i < 0) {
2968        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature verification problems");
2969        goto cleanup;
2970    }
2971    else if (i == 0) {
2972        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Signature did not match the certificate request");
2973        goto cleanup;
2974    }
2975
2976    /* Now we can get on with it */
2977
2978    new_cert = X509_new();
2979    if (new_cert == NULL) {
2980        php_error_docref(NULL TSRMLS_CC, E_WARNING, "No memory");
2981        goto cleanup;
2982    }
2983    /* Version 3 cert */
2984    if (!X509_set_version(new_cert, 2))
2985        goto cleanup;
2986
2987    ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial);
2988
2989    X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr));
2990
2991    if (cert == NULL) {
2992        cert = new_cert;
2993    }
2994    if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
2995        goto cleanup;
2996    }
2997    X509_gmtime_adj(X509_get_notBefore(new_cert), 0);
2998    X509_gmtime_adj(X509_get_notAfter(new_cert), (long)60*60*24*num_days);
2999    i = X509_set_pubkey(new_cert, key);
3000    if (!i) {
3001        goto cleanup;
3002    }
3003    if (req.extensions_section) {
3004        X509V3_CTX ctx;
3005
3006        X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
3007        X509V3_set_conf_lhash(&ctx, req.req_config);
3008        if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) {
3009            goto cleanup;
3010        }
3011    }
3012
3013    /* Now sign it */
3014    if (!X509_sign(new_cert, priv_key, req.digest)) {
3015        php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to sign it");
3016        goto cleanup;
3017    }
3018
3019    /* Succeeded; lets return the cert */
3020    RETVAL_RESOURCE(zend_list_insert(new_cert, le_x509 TSRMLS_CC));
3021    new_cert = NULL;
3022
3023cleanup:
3024
3025    if (cert == new_cert) {
3026        cert = NULL;
3027    }
3028    PHP_SSL_REQ_DISPOSE(&req);
3029
3030    if (keyresource == -1 && priv_key) {
3031        EVP_PKEY_free(priv_key);
3032    }
3033    if (key) {
3034        EVP_PKEY_free(key);
3035    }
3036    if (csr_resource == -1 && csr) {
3037        X509_REQ_free(csr);
3038    }
3039    if (certresource == -1 && cert) {
3040        X509_free(cert);
3041    }
3042    if (new_cert) {
3043        X509_free(new_cert);
3044    }
3045}
3046/* }}} */
3047
3048/* {{{ proto bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]])
3049   Generates a privkey and CSR */
3050PHP_FUNCTION(openssl_csr_new)
3051{
3052    struct php_x509_request req;
3053    zval * args = NULL, * dn, *attribs = NULL;
3054    zval * out_pkey;
3055    X509_REQ * csr = NULL;
3056    int we_made_the_key = 1;
3057    long key_resource;
3058
3059    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) {
3060        return;
3061    }
3062    RETVAL_FALSE;
3063
3064    PHP_SSL_REQ_INIT(&req);
3065
3066    if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3067        /* Generate or use a private key */
3068        if (Z_TYPE_P(out_pkey) != IS_NULL) {
3069            req.priv_key = php_openssl_evp_from_zval(&out_pkey, 0, NULL, 0, &key_resource TSRMLS_CC);
3070            if (req.priv_key != NULL) {
3071                we_made_the_key = 0;
3072            }
3073        }
3074        if (req.priv_key == NULL) {
3075            php_openssl_generate_private_key(&req TSRMLS_CC);
3076        }
3077        if (req.priv_key == NULL) {
3078            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to generate a private key");
3079        } else {
3080            csr = X509_REQ_new();
3081            if (csr) {
3082                if (php_openssl_make_REQ(&req, csr, dn, attribs TSRMLS_CC) == SUCCESS) {
3083                    X509V3_CTX ext_ctx;
3084
3085                    X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0);
3086                    X509V3_set_conf_lhash(&ext_ctx, req.req_config);
3087
3088                    /* Add extensions */
3089                    if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
3090                                &ext_ctx, req.request_extensions_section, csr))
3091                    {
3092                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
3093                    } else {
3094                        RETVAL_TRUE;
3095
3096                        if (X509_REQ_sign(csr, req.priv_key, req.digest)) {
3097                            RETVAL_RESOURCE(zend_list_insert(csr, le_csr TSRMLS_CC));
3098                            csr = NULL;
3099                        } else {
3100                            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error signing request");
3101                        }
3102
3103                        if (we_made_the_key) {
3104                            /* and a resource for the private key */
3105                            zval_dtor(out_pkey);
3106                            ZVAL_RESOURCE(out_pkey, zend_list_insert(req.priv_key, le_key TSRMLS_CC));
3107                            req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
3108                        } else if (key_resource != -1) {
3109                            req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
3110                        }
3111                    }
3112                }
3113                else {
3114                    if (!we_made_the_key) {
3115                        /* if we have not made the key we are not supposed to zap it by calling dispose! */
3116                        req.priv_key = NULL;
3117                    }
3118                }
3119            }
3120        }
3121    }
3122    if (csr) {
3123        X509_REQ_free(csr);
3124    }
3125    PHP_SSL_REQ_DISPOSE(&req);
3126}
3127/* }}} */
3128
3129/* {{{ proto mixed openssl_csr_get_subject(mixed csr)
3130   Returns the subject of a CERT or FALSE on error */
3131PHP_FUNCTION(openssl_csr_get_subject)
3132{
3133    zval ** zcsr;
3134    zend_bool use_shortnames = 1;
3135    long csr_resource;
3136    X509_NAME * subject;
3137    X509_REQ * csr;
3138
3139    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) {
3140        return;
3141    }
3142
3143    csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
3144
3145    if (csr == NULL) {
3146        RETURN_FALSE;
3147    }
3148
3149    subject = X509_REQ_get_subject_name(csr);
3150
3151    array_init(return_value);
3152    add_assoc_name_entry(return_value, NULL, subject, use_shortnames TSRMLS_CC);
3153    return;
3154}
3155/* }}} */
3156
3157/* {{{ proto mixed openssl_csr_get_public_key(mixed csr)
3158    Returns the subject of a CERT or FALSE on error */
3159PHP_FUNCTION(openssl_csr_get_public_key)
3160{
3161    zval ** zcsr;
3162    zend_bool use_shortnames = 1;
3163    long csr_resource;
3164
3165    X509_REQ * csr;
3166    EVP_PKEY *tpubkey;
3167
3168    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &zcsr, &use_shortnames) == FAILURE) {
3169        return;
3170    }
3171
3172    csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource TSRMLS_CC);
3173
3174    if (csr == NULL) {
3175        RETURN_FALSE;
3176    }
3177
3178    tpubkey=X509_REQ_get_pubkey(csr);
3179    RETVAL_RESOURCE(zend_list_insert(tpubkey, le_key TSRMLS_CC));
3180    return;
3181}
3182/* }}} */
3183
3184/* }}} */
3185
3186/* {{{ EVP Public/Private key functions */
3187
3188/* {{{ php_openssl_evp_from_zval
3189   Given a zval, coerce it into a EVP_PKEY object.
3190    It can be:
3191        1. private key resource from openssl_get_privatekey()
3192        2. X509 resource -> public key will be extracted from it
3193        3. if it starts with file:// interpreted as path to key file
3194        4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey()
3195        5. an array(0 => [items 2..4], 1 => passphrase)
3196        6. if val is a string (possibly starting with file:///) and it is not an X509 certificate, then interpret as public key
3197    NOTE: If you are requesting a private key but have not specified a passphrase, you should use an
3198    empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in
3199    the Apache error log!
3200*/
3201static EVP_PKEY * php_openssl_evp_from_zval(zval ** val, int public_key, char * passphrase, int makeresource, long * resourceval TSRMLS_DC)
3202{
3203    EVP_PKEY * key = NULL;
3204    X509 * cert = NULL;
3205    int free_cert = 0;
3206    long cert_res = -1;
3207    char * filename = NULL;
3208    zval tmp;
3209
3210    Z_TYPE(tmp) = IS_NULL;
3211
3212#define TMP_CLEAN \
3213    if (Z_TYPE(tmp) == IS_STRING) {\
3214        zval_dtor(&tmp); \
3215    } \
3216    return NULL;
3217
3218    if (resourceval) {
3219        *resourceval = -1;
3220    }
3221    if (Z_TYPE_PP(val) == IS_ARRAY) {
3222        zval ** zphrase;
3223
3224        /* get passphrase */
3225
3226        if (zend_hash_index_find(HASH_OF(*val), 1, (void **)&zphrase) == FAILURE) {
3227            php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
3228            return NULL;
3229        }
3230
3231        if (Z_TYPE_PP(zphrase) == IS_STRING) {
3232            passphrase = Z_STRVAL_PP(zphrase);
3233        } else {
3234            tmp = **zphrase;
3235            zval_copy_ctor(&tmp);
3236            convert_to_string(&tmp);
3237            passphrase = Z_STRVAL(tmp);
3238        }
3239
3240        /* now set val to be the key param and continue */
3241        if (zend_hash_index_find(HASH_OF(*val), 0, (void **)&val) == FAILURE) {
3242            php_error_docref(NULL TSRMLS_CC, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
3243            TMP_CLEAN;
3244        }
3245    }
3246
3247    if (Z_TYPE_PP(val) == IS_RESOURCE) {
3248        void * what;
3249        int type;
3250
3251        what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL X.509/key", &type, 2, le_x509, le_key);
3252        if (!what) {
3253            TMP_CLEAN;
3254        }
3255        if (resourceval) {
3256            *resourceval = Z_LVAL_PP(val);
3257        }
3258        if (type == le_x509) {
3259            /* extract key from cert, depending on public_key param */
3260            cert = (X509*)what;
3261            free_cert = 0;
3262        } else if (type == le_key) {
3263            int is_priv;
3264
3265            is_priv = php_openssl_is_private_key((EVP_PKEY*)what TSRMLS_CC);
3266
3267            /* check whether it is actually a private key if requested */
3268            if (!public_key && !is_priv) {
3269                php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param is a public key");
3270                TMP_CLEAN;
3271            }
3272
3273            if (public_key && is_priv) {
3274                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Don't know how to get public key from this private key");
3275                TMP_CLEAN;
3276            } else {
3277                if (Z_TYPE(tmp) == IS_STRING) {
3278                    zval_dtor(&tmp);
3279                }
3280                /* got the key - return it */
3281                return (EVP_PKEY*)what;
3282            }
3283        } else {
3284            /* other types could be used here - eg: file pointers and read in the data from them */
3285            TMP_CLEAN;
3286        }
3287    } else {
3288        /* force it to be a string and check if it refers to a file */
3289        /* passing non string values leaks, object uses toString, it returns NULL
3290         * See bug38255.phpt
3291         */
3292        if (!(Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_OBJECT)) {
3293            TMP_CLEAN;
3294        }
3295        convert_to_string_ex(val);
3296
3297        if (Z_STRLEN_PP(val) > 7 && memcmp(Z_STRVAL_PP(val), "file://", sizeof("file://") - 1) == 0) {
3298            filename = Z_STRVAL_PP(val) + (sizeof("file://") - 1);
3299        }
3300        /* it's an X509 file/cert of some kind, and we need to extract the data from that */
3301        if (public_key) {
3302            cert = php_openssl_x509_from_zval(val, 0, &cert_res TSRMLS_CC);
3303            free_cert = (cert_res == -1);
3304            /* actual extraction done later */
3305            if (!cert) {
3306                /* not a X509 certificate, try to retrieve public key */
3307                BIO* in;
3308                if (filename) {
3309                    in = BIO_new_file(filename, "r");
3310                } else {
3311                    in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
3312                }
3313                if (in == NULL) {
3314                    TMP_CLEAN;
3315                }
3316                key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
3317                BIO_free(in);
3318            }
3319        } else {
3320            /* we want the private key */
3321            BIO *in;
3322
3323            if (filename) {
3324                if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) {
3325                    TMP_CLEAN;
3326                }
3327                in = BIO_new_file(filename, "r");
3328            } else {
3329                in = BIO_new_mem_buf(Z_STRVAL_PP(val), Z_STRLEN_PP(val));
3330            }
3331
3332            if (in == NULL) {
3333                TMP_CLEAN;
3334            }
3335            key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);
3336            BIO_free(in);
3337        }
3338    }
3339
3340    if (public_key && cert && key == NULL) {
3341        /* extract public key from X509 cert */
3342        key = (EVP_PKEY *) X509_get_pubkey(cert);
3343    }
3344
3345    if (free_cert && cert) {
3346        X509_free(cert);
3347    }
3348    if (key && makeresource && resourceval) {
3349        *resourceval = ZEND_REGISTER_RESOURCE(NULL, key, le_key);
3350    }
3351    if (Z_TYPE(tmp) == IS_STRING) {
3352        zval_dtor(&tmp);
3353    }
3354    return key;
3355}
3356/* }}} */
3357
3358/* {{{ php_openssl_generate_private_key */
3359static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req TSRMLS_DC)
3360{
3361    char * randfile = NULL;
3362    int egdsocket, seeded;
3363    EVP_PKEY * return_val = NULL;
3364
3365    if (req->priv_key_bits < MIN_KEY_LENGTH) {
3366        php_error_docref(NULL TSRMLS_CC, E_WARNING, "private key length is too short; it needs to be at least %d bits, not %d",
3367                MIN_KEY_LENGTH, req->priv_key_bits);
3368        return NULL;
3369    }
3370
3371    randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE");
3372    php_openssl_load_rand_file(randfile, &egdsocket, &seeded TSRMLS_CC);
3373
3374    if ((req->priv_key = EVP_PKEY_new()) != NULL) {
3375        switch(req->priv_key_type) {
3376            case OPENSSL_KEYTYPE_RSA:
3377                if (EVP_PKEY_assign_RSA(req->priv_key, RSA_generate_key(req->priv_key_bits, 0x10001, NULL, NULL))) {
3378                    return_val = req->priv_key;
3379                }
3380                break;
3381#if !defined(NO_DSA) && defined(HAVE_DSA_DEFAULT_METHOD)
3382            case OPENSSL_KEYTYPE_DSA:
3383                {
3384                    DSA *dsapar = DSA_generate_parameters(req->priv_key_bits, NULL, 0, NULL, NULL, NULL, NULL);
3385                    if (dsapar) {
3386                        DSA_set_method(dsapar, DSA_get_default_method());
3387                        if (DSA_generate_key(dsapar)) {
3388                            if (EVP_PKEY_assign_DSA(req->priv_key, dsapar)) {
3389                                return_val = req->priv_key;
3390                            }
3391                        } else {
3392                            DSA_free(dsapar);
3393                        }
3394                    }
3395                }
3396                break;
3397#endif
3398#if !defined(NO_DH)
3399            case OPENSSL_KEYTYPE_DH:
3400                {
3401                    DH *dhpar = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL);
3402                    int codes = 0;
3403
3404                    if (dhpar) {
3405                        DH_set_method(dhpar, DH_get_default_method());
3406                        if (DH_check(dhpar, &codes) && codes == 0 && DH_generate_key(dhpar)) {
3407                            if (EVP_PKEY_assign_DH(req->priv_key, dhpar)) {
3408                                return_val = req->priv_key;
3409                            }
3410                        } else {
3411                            DH_free(dhpar);
3412                        }
3413                    }
3414                }
3415                break;
3416#endif
3417            default:
3418                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported private key type");
3419        }
3420    }
3421
3422    php_openssl_write_rand_file(randfile, egdsocket, seeded);
3423
3424    if (return_val == NULL) {
3425        EVP_PKEY_free(req->priv_key);
3426        req->priv_key = NULL;
3427        return NULL;
3428    }
3429
3430    return return_val;
3431}
3432/* }}} */
3433
3434/* {{{ php_openssl_is_private_key
3435    Check whether the supplied key is a private key by checking if the secret prime factors are set */
3436static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC)
3437{
3438    assert(pkey != NULL);
3439
3440    switch (pkey->type) {
3441#ifndef NO_RSA
3442        case EVP_PKEY_RSA:
3443        case EVP_PKEY_RSA2:
3444            assert(pkey->pkey.rsa != NULL);
3445            if (pkey->pkey.rsa != NULL && (NULL == pkey->pkey.rsa->p || NULL == pkey->pkey.rsa->q)) {
3446                return 0;
3447            }
3448            break;
3449#endif
3450#ifndef NO_DSA
3451        case EVP_PKEY_DSA:
3452        case EVP_PKEY_DSA1:
3453        case EVP_PKEY_DSA2:
3454        case EVP_PKEY_DSA3:
3455        case EVP_PKEY_DSA4:
3456            assert(pkey->pkey.dsa != NULL);
3457
3458            if (NULL == pkey->pkey.dsa->p || NULL == pkey->pkey.dsa->q || NULL == pkey->pkey.dsa->priv_key){
3459                return 0;
3460            }
3461            break;
3462#endif
3463#ifndef NO_DH
3464        case EVP_PKEY_DH:
3465            assert(pkey->pkey.dh != NULL);
3466
3467            if (NULL == pkey->pkey.dh->p || NULL == pkey->pkey.dh->priv_key) {
3468                return 0;
3469            }
3470            break;
3471#endif
3472#ifdef HAVE_EVP_PKEY_EC
3473        case EVP_PKEY_EC:
3474            assert(pkey->pkey.ec != NULL);
3475
3476            if ( NULL == EC_KEY_get0_private_key(pkey->pkey.ec)) {
3477                return 0;
3478            }
3479            break;
3480#endif
3481        default:
3482            php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
3483            break;
3484    }
3485    return 1;
3486}
3487/* }}} */
3488
3489#define OPENSSL_PKEY_GET_BN(_type, _name) do {                          \
3490        if (pkey->pkey._type->_name != NULL) {                          \
3491            int len = BN_num_bytes(pkey->pkey._type->_name);            \
3492            char *str = emalloc(len + 1);                               \
3493            BN_bn2bin(pkey->pkey._type->_name, (unsigned char*)str);    \
3494            str[len] = 0;                                               \
3495            add_assoc_stringl(_type, #_name, str, len, 0);              \
3496        }                                                               \
3497    } while (0)
3498
3499#define OPENSSL_PKEY_SET_BN(_ht, _type, _name) do {                     \
3500        zval **bn;                                                      \
3501        if (zend_hash_find(_ht, #_name, sizeof(#_name), (void**)&bn) == SUCCESS && \
3502                Z_TYPE_PP(bn) == IS_STRING) {                           \
3503            _type->_name = BN_bin2bn(                                   \
3504                (unsigned char*)Z_STRVAL_PP(bn),                        \
3505                Z_STRLEN_PP(bn), NULL);                                 \
3506        }                                                               \
3507    } while (0);
3508
3509
3510/* {{{ proto resource openssl_pkey_new([array configargs])
3511   Generates a new private key */
3512PHP_FUNCTION(openssl_pkey_new)
3513{
3514    struct php_x509_request req;
3515    zval * args = NULL;
3516    zval **data;
3517
3518    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &args) == FAILURE) {
3519        return;
3520    }
3521    RETVAL_FALSE;
3522
3523    if (args && Z_TYPE_P(args) == IS_ARRAY) {
3524        EVP_PKEY *pkey;
3525
3526        if (zend_hash_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa"), (void**)&data) == SUCCESS &&
3527            Z_TYPE_PP(data) == IS_ARRAY) {
3528            pkey = EVP_PKEY_new();
3529            if (pkey) {
3530                RSA *rsa = RSA_new();
3531                if (rsa) {
3532                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, n);
3533                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, e);
3534                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, d);
3535                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, p);
3536                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, q);
3537                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmp1);
3538                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmq1);
3539                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, iqmp);
3540                    if (rsa->n && rsa->d) {
3541                        if (EVP_PKEY_assign_RSA(pkey, rsa)) {
3542                            RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC));
3543                        }
3544                    }
3545                    RSA_free(rsa);
3546                }
3547                EVP_PKEY_free(pkey);
3548            }
3549            RETURN_FALSE;
3550        } else if (zend_hash_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa"), (void**)&data) == SUCCESS &&
3551                   Z_TYPE_PP(data) == IS_ARRAY) {
3552            pkey = EVP_PKEY_new();
3553            if (pkey) {
3554                DSA *dsa = DSA_new();
3555                if (dsa) {
3556                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, p);
3557                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, q);
3558                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, g);
3559                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, priv_key);
3560                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, pub_key);
3561                    if (dsa->p && dsa->q && dsa->g) {
3562                        if (!dsa->priv_key && !dsa->pub_key) {
3563                            DSA_generate_key(dsa);
3564                        }
3565                        if (EVP_PKEY_assign_DSA(pkey, dsa)) {
3566                            RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC));
3567                        }
3568                    }
3569                    DSA_free(dsa);
3570                }
3571                EVP_PKEY_free(pkey);
3572            }
3573            RETURN_FALSE;
3574        } else if (zend_hash_find(Z_ARRVAL_P(args), "dh", sizeof("dh"), (void**)&data) == SUCCESS &&
3575                   Z_TYPE_PP(data) == IS_ARRAY) {
3576            pkey = EVP_PKEY_new();
3577            if (pkey) {
3578                DH *dh = DH_new();
3579                if (dh) {
3580                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, p);
3581                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, g);
3582                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, priv_key);
3583                    OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, pub_key);
3584                    if (dh->p && dh->g) {
3585                        if (!dh->pub_key) {
3586                            DH_generate_key(dh);
3587                        }
3588                        if (EVP_PKEY_assign_DH(pkey, dh)) {
3589                            RETURN_RESOURCE(zend_list_insert(pkey, le_key TSRMLS_CC));
3590                        }
3591                    }
3592                    DH_free(dh);
3593                }
3594                EVP_PKEY_free(pkey);
3595            }
3596            RETURN_FALSE;
3597        }
3598    }
3599
3600    PHP_SSL_REQ_INIT(&req);
3601
3602    if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS)
3603    {
3604        if (php_openssl_generate_private_key(&req TSRMLS_CC)) {
3605            /* pass back a key resource */
3606            RETVAL_RESOURCE(zend_list_insert(req.priv_key, le_key TSRMLS_CC));
3607            /* make sure the cleanup code doesn't zap it! */
3608            req.priv_key = NULL;
3609        }
3610    }
3611    PHP_SSL_REQ_DISPOSE(&req);
3612}
3613/* }}} */
3614
3615/* {{{ proto bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args)
3616   Gets an exportable representation of a key into a file */
3617PHP_FUNCTION(openssl_pkey_export_to_file)
3618{
3619    struct php_x509_request req;
3620    zval ** zpkey, * args = NULL;
3621    char * passphrase = NULL; int passphrase_len = 0;
3622    char * filename = NULL; int filename_len = 0;
3623    long key_resource = -1;
3624    EVP_PKEY * key;
3625    BIO * bio_out = NULL;
3626    const EVP_CIPHER * cipher;
3627
3628    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zp|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) {
3629        return;
3630    }
3631    RETVAL_FALSE;
3632
3633    key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC);
3634
3635    if (key == NULL) {
3636        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1");
3637        RETURN_FALSE;
3638    }
3639
3640    if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) {
3641        RETURN_FALSE;
3642    }
3643
3644    PHP_SSL_REQ_INIT(&req);
3645
3646    if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3647        bio_out = BIO_new_file(filename, "w");
3648
3649        if (passphrase && req.priv_key_encrypt) {
3650            if (req.priv_key_encrypt_cipher) {
3651                cipher = req.priv_key_encrypt_cipher;
3652            } else {
3653                cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
3654            }
3655        } else {
3656            cipher = NULL;
3657        }
3658        if (PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, passphrase_len, NULL, NULL)) {
3659            /* Success!
3660             * If returning the output as a string, do so now */
3661            RETVAL_TRUE;
3662        }
3663    }
3664    PHP_SSL_REQ_DISPOSE(&req);
3665
3666    if (key_resource == -1 && key) {
3667        EVP_PKEY_free(key);
3668    }
3669    if (bio_out) {
3670        BIO_free(bio_out);
3671    }
3672}
3673/* }}} */
3674
3675/* {{{ proto bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]])
3676   Gets an exportable representation of a key into a string or file */
3677PHP_FUNCTION(openssl_pkey_export)
3678{
3679    struct php_x509_request req;
3680    zval ** zpkey, * args = NULL, *out;
3681    char * passphrase = NULL; int passphrase_len = 0;
3682    long key_resource = -1;
3683    EVP_PKEY * key;
3684    BIO * bio_out = NULL;
3685    const EVP_CIPHER * cipher;
3686
3687    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zz|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) {
3688        return;
3689    }
3690    RETVAL_FALSE;
3691
3692    key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource TSRMLS_CC);
3693
3694    if (key == NULL) {
3695        php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot get key from parameter 1");
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(BIO_s_mem());
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
3717            char * bio_mem_ptr;
3718            long bio_mem_len;
3719            RETVAL_TRUE;
3720
3721            bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
3722            zval_dtor(out);
3723            ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len, 1);
3724        }
3725    }
3726    PHP_SSL_REQ_DISPOSE(&req);
3727
3728    if (key_resource == -1 && key) {
3729        EVP_PKEY_free(key);
3730    }
3731    if (bio_out) {
3732        BIO_free(bio_out);
3733    }
3734}
3735/* }}} */
3736
3737/* {{{ proto int openssl_pkey_get_public(mixed cert)
3738   Gets public key from X.509 certificate */
3739PHP_FUNCTION(openssl_pkey_get_public)
3740{
3741    zval **cert;
3742    EVP_PKEY *pkey;
3743
3744    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &cert) == FAILURE) {
3745        return;
3746    }
3747    Z_TYPE_P(return_value) = IS_RESOURCE;
3748    pkey = php_openssl_evp_from_zval(cert, 1, NULL, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
3749
3750    if (pkey == NULL) {
3751        RETURN_FALSE;
3752    }
3753    zend_list_addref(Z_LVAL_P(return_value));
3754}
3755/* }}} */
3756
3757/* {{{ proto void openssl_pkey_free(int key)
3758   Frees a key */
3759PHP_FUNCTION(openssl_pkey_free)
3760{
3761    zval *key;
3762    EVP_PKEY *pkey;
3763
3764    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) {
3765        return;
3766    }
3767    ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
3768    zend_list_delete(Z_LVAL_P(key));
3769}
3770/* }}} */
3771
3772/* {{{ proto int openssl_pkey_get_private(string key [, string passphrase])
3773   Gets private keys */
3774PHP_FUNCTION(openssl_pkey_get_private)
3775{
3776    zval **cert;
3777    EVP_PKEY *pkey;
3778    char * passphrase = "";
3779    int passphrase_len = sizeof("")-1;
3780
3781    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|s", &cert, &passphrase, &passphrase_len) == FAILURE) {
3782        return;
3783    }
3784    Z_TYPE_P(return_value) = IS_RESOURCE;
3785    pkey = php_openssl_evp_from_zval(cert, 0, passphrase, 1, &Z_LVAL_P(return_value) TSRMLS_CC);
3786
3787    if (pkey == NULL) {
3788        RETURN_FALSE;
3789    }
3790    zend_list_addref(Z_LVAL_P(return_value));
3791}
3792
3793/* }}} */
3794
3795/* {{{ proto resource openssl_pkey_get_details(resource key)
3796    returns an array with the key details (bits, pkey, type)*/
3797PHP_FUNCTION(openssl_pkey_get_details)
3798{
3799    zval *key;
3800    EVP_PKEY *pkey;
3801    BIO *out;
3802    unsigned int pbio_len;
3803    char *pbio;
3804    long ktype;
3805
3806    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &key) == FAILURE) {
3807        return;
3808    }
3809    ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
3810    if (!pkey) {
3811        RETURN_FALSE;
3812    }
3813    out = BIO_new(BIO_s_mem());
3814    PEM_write_bio_PUBKEY(out, pkey);
3815    pbio_len = BIO_get_mem_data(out, &pbio);
3816
3817    array_init(return_value);
3818    add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey));
3819    add_assoc_stringl(return_value, "key", pbio, pbio_len, 1);
3820    /*TODO: Use the real values once the openssl constants are used
3821     * See the enum at the top of this file
3822     */
3823    switch (EVP_PKEY_type(pkey->type)) {
3824        case EVP_PKEY_RSA:
3825        case EVP_PKEY_RSA2:
3826            ktype = OPENSSL_KEYTYPE_RSA;
3827
3828            if (pkey->pkey.rsa != NULL) {
3829                zval *rsa;
3830
3831                ALLOC_INIT_ZVAL(rsa);
3832                array_init(rsa);
3833                OPENSSL_PKEY_GET_BN(rsa, n);
3834                OPENSSL_PKEY_GET_BN(rsa, e);
3835                OPENSSL_PKEY_GET_BN(rsa, d);
3836                OPENSSL_PKEY_GET_BN(rsa, p);
3837                OPENSSL_PKEY_GET_BN(rsa, q);
3838                OPENSSL_PKEY_GET_BN(rsa, dmp1);
3839                OPENSSL_PKEY_GET_BN(rsa, dmq1);
3840                OPENSSL_PKEY_GET_BN(rsa, iqmp);
3841                add_assoc_zval(return_value, "rsa", rsa);
3842            }
3843
3844            break;
3845        case EVP_PKEY_DSA:
3846        case EVP_PKEY_DSA2:
3847        case EVP_PKEY_DSA3:
3848        case EVP_PKEY_DSA4:
3849            ktype = OPENSSL_KEYTYPE_DSA;
3850
3851            if (pkey->pkey.dsa != NULL) {
3852                zval *dsa;
3853
3854                ALLOC_INIT_ZVAL(dsa);
3855                array_init(dsa);
3856                OPENSSL_PKEY_GET_BN(dsa, p);
3857                OPENSSL_PKEY_GET_BN(dsa, q);
3858                OPENSSL_PKEY_GET_BN(dsa, g);
3859                OPENSSL_PKEY_GET_BN(dsa, priv_key);
3860                OPENSSL_PKEY_GET_BN(dsa, pub_key);
3861                add_assoc_zval(return_value, "dsa", dsa);
3862            }
3863            break;
3864        case EVP_PKEY_DH:
3865
3866            ktype = OPENSSL_KEYTYPE_DH;
3867
3868            if (pkey->pkey.dh != NULL) {
3869                zval *dh;
3870
3871                ALLOC_INIT_ZVAL(dh);
3872                array_init(dh);
3873                OPENSSL_PKEY_GET_BN(dh, p);
3874                OPENSSL_PKEY_GET_BN(dh, g);
3875                OPENSSL_PKEY_GET_BN(dh, priv_key);
3876                OPENSSL_PKEY_GET_BN(dh, pub_key);
3877                add_assoc_zval(return_value, "dh", dh);
3878            }
3879
3880            break;
3881#ifdef HAVE_EVP_PKEY_EC
3882        case EVP_PKEY_EC:
3883            ktype = OPENSSL_KEYTYPE_EC;
3884            break;
3885#endif
3886        default:
3887            ktype = -1;
3888            break;
3889    }
3890    add_assoc_long(return_value, "type", ktype);
3891
3892    BIO_free(out);
3893}
3894/* }}} */
3895
3896/* }}} */
3897
3898#if OPENSSL_VERSION_NUMBER >= 0x10000000L
3899
3900/* {{{ proto string openssl_pbkdf2(string password, string salt, long key_length, long iterations [, string digest_method = "sha1"])
3901   Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
3902PHP_FUNCTION(openssl_pbkdf2)
3903{
3904    long key_length = 0, iterations = 0;
3905    char *password; int password_len;
3906    char *salt; int salt_len;
3907    char *method; int method_len = 0;
3908    unsigned char *out_buffer;
3909
3910    const EVP_MD *digest;
3911
3912    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssll|s",
3913                &password, &password_len,
3914                &salt, &salt_len,
3915                &key_length, &iterations,
3916                &method, &method_len) == FAILURE) {
3917        return;
3918    }
3919
3920    if (key_length <= 0) {
3921        RETURN_FALSE;
3922    }
3923
3924    if (method_len) {
3925        digest = EVP_get_digestbyname(method);
3926    } else {
3927        digest = EVP_sha1();
3928    }
3929
3930    if (!digest) {
3931        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
3932        RETURN_FALSE;
3933    }
3934
3935    out_buffer = emalloc(key_length + 1);
3936    out_buffer[key_length] = '\0';
3937
3938    if (PKCS5_PBKDF2_HMAC(password, password_len, (unsigned char *)salt, salt_len, iterations, digest, key_length, out_buffer) == 1) {
3939        RETVAL_STRINGL((char *)out_buffer, key_length, 0);
3940    } else {
3941        efree(out_buffer);
3942        RETURN_FALSE;
3943    }
3944}
3945/* }}} */
3946
3947#endif
3948
3949/* {{{ PKCS7 S/MIME functions */
3950
3951/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
3952   Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */
3953PHP_FUNCTION(openssl_pkcs7_verify)
3954{
3955    X509_STORE * store = NULL;
3956    zval * cainfo = NULL;
3957    STACK_OF(X509) *signers= NULL;
3958    STACK_OF(X509) *others = NULL;
3959    PKCS7 * p7 = NULL;
3960    BIO * in = NULL, * datain = NULL, * dataout = NULL;
3961    long flags = 0;
3962    char * filename; int filename_len;
3963    char * extracerts = NULL; int extracerts_len = 0;
3964    char * signersfilename = NULL; int signersfilename_len = 0;
3965    char * datafilename = NULL; int datafilename_len = 0;
3966
3967    RETVAL_LONG(-1);
3968
3969    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "pl|papp", &filename, &filename_len,
3970                &flags, &signersfilename, &signersfilename_len, &cainfo,
3971                &extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) {
3972        return;
3973    }
3974
3975    if (extracerts) {
3976        others = load_all_certs_from_file(extracerts);
3977        if (others == NULL) {
3978            goto clean_exit;
3979        }
3980    }
3981
3982    flags = flags & ~PKCS7_DETACHED;
3983
3984    store = setup_verify(cainfo TSRMLS_CC);
3985
3986    if (!store) {
3987        goto clean_exit;
3988    }
3989    if (php_openssl_open_base_dir_chk(filename TSRMLS_CC)) {
3990        goto clean_exit;
3991    }
3992
3993    in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r");
3994    if (in == NULL) {
3995        goto clean_exit;
3996    }
3997    p7 = SMIME_read_PKCS7(in, &datain);
3998    if (p7 == NULL) {
3999#if DEBUG_SMIME
4000        zend_printf("SMIME_read_PKCS7 failed\n");
4001#endif
4002        goto clean_exit;
4003    }
4004
4005    if (datafilename) {
4006
4007        if (php_openssl_open_base_dir_chk(datafilename TSRMLS_CC)) {
4008            goto clean_exit;
4009        }
4010
4011        dataout = BIO_new_file(datafilename, "w");
4012        if (dataout == NULL) {
4013            goto clean_exit;
4014        }
4015    }
4016#if DEBUG_SMIME
4017    zend_printf("Calling PKCS7 verify\n");
4018#endif
4019
4020    if (PKCS7_verify(p7, others, store, datain, dataout, flags)) {
4021
4022        RETVAL_TRUE;
4023
4024        if (signersfilename) {
4025            BIO *certout;
4026
4027            if (php_openssl_open_base_dir_chk(signersfilename TSRMLS_CC)) {
4028                goto clean_exit;
4029            }
4030
4031            certout = BIO_new_file(signersfilename, "w");
4032            if (certout) {
4033                int i;
4034                signers = PKCS7_get0_signers(p7, NULL, flags);
4035
4036                for(i = 0; i < sk_X509_num(signers); i++) {
4037                    PEM_write_bio_X509(certout, sk_X509_value(signers, i));
4038                }
4039                BIO_free(certout);
4040                sk_X509_free(signers);
4041            } else {
4042                php_error_docref(NULL TSRMLS_CC, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
4043                RETVAL_LONG(-1);
4044            }
4045        }
4046        goto clean_exit;
4047    } else {
4048        RETVAL_FALSE;
4049    }
4050clean_exit:
4051    X509_STORE_free(store);
4052    BIO_free(datain);
4053    BIO_free(in);
4054    BIO_free(dataout);
4055    PKCS7_free(p7);
4056    sk_X509_free(others);
4057}
4058/* }}} */
4059
4060/* {{{ proto bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]])
4061   Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */
4062PHP_FUNCTION(openssl_pkcs7_encrypt)
4063{
4064    zval ** zrecipcerts, * zheaders = NULL;
4065    STACK_OF(X509) * recipcerts = NULL;
4066    BIO * infile = NULL, * outfile = NULL;
4067    long flags = 0;
4068    PKCS7 * p7 = NULL;
4069    HashPosition hpos;
4070    zval ** zcertval;
4071    X509 * cert;
4072    const EVP_CIPHER *cipher = NULL;
4073    long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
4074    uint strindexlen;
4075    ulong intindex;
4076    char * strindex;
4077    char * infilename = NULL;   int infilename_len;
4078    char * outfilename = NULL;  int outfilename_len;
4079
4080    RETVAL_FALSE;
4081
4082    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZa!|ll", &infilename, &infilename_len,
4083                &outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE)
4084        return;
4085
4086
4087    if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) {
4088        return;
4089    }
4090
4091    infile = BIO_new_file(infilename, "r");
4092    if (infile == NULL) {
4093        goto clean_exit;
4094    }
4095
4096    outfile = BIO_new_file(outfilename, "w");
4097    if (outfile == NULL) {
4098        goto clean_exit;
4099    }
4100
4101    recipcerts = sk_X509_new_null();
4102
4103    /* get certs */
4104    if (Z_TYPE_PP(zrecipcerts) == IS_ARRAY) {
4105        zend_hash_internal_pointer_reset_ex(HASH_OF(*zrecipcerts), &hpos);
4106        while(zend_hash_get_current_data_ex(HASH_OF(*zrecipcerts), (void**)&zcertval, &hpos) == SUCCESS) {
4107            long certresource;
4108
4109            cert = php_openssl_x509_from_zval(zcertval, 0, &certresource TSRMLS_CC);
4110            if (cert == NULL) {
4111                goto clean_exit;
4112            }
4113
4114            if (certresource != -1) {
4115                /* we shouldn't free this particular cert, as it is a resource.
4116                    make a copy and push that on the stack instead */
4117                cert = X509_dup(cert);
4118                if (cert == NULL) {
4119                    goto clean_exit;
4120                }
4121            }
4122            sk_X509_push(recipcerts, cert);
4123
4124            zend_hash_move_forward_ex(HASH_OF(*zrecipcerts), &hpos);
4125        }
4126    } else {
4127        /* a single certificate */
4128        long certresource;
4129
4130        cert = php_openssl_x509_from_zval(zrecipcerts, 0, &certresource TSRMLS_CC);
4131        if (cert == NULL) {
4132            goto clean_exit;
4133        }
4134
4135        if (certresource != -1) {
4136            /* we shouldn't free this particular cert, as it is a resource.
4137                make a copy and push that on the stack instead */
4138            cert = X509_dup(cert);
4139            if (cert == NULL) {
4140                goto clean_exit;
4141            }
4142        }
4143        sk_X509_push(recipcerts, cert);
4144    }
4145
4146    /* sanity check the cipher */
4147    cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
4148    if (cipher == NULL) {
4149        /* shouldn't happen */
4150        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get cipher");
4151        goto clean_exit;
4152    }
4153
4154    p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, flags);
4155
4156    if (p7 == NULL) {
4157        goto clean_exit;
4158    }
4159
4160    /* tack on extra headers */
4161    if (zheaders) {
4162        zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos);
4163        while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&zcertval, &hpos) == SUCCESS) {
4164            strindex = NULL;
4165            zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);
4166
4167            convert_to_string_ex(zcertval);
4168
4169            if (strindex) {
4170                BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(zcertval));
4171            } else {
4172                BIO_printf(outfile, "%s\n", Z_STRVAL_PP(zcertval));
4173            }
4174
4175            zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
4176        }
4177    }
4178
4179    (void)BIO_reset(infile);
4180
4181    /* write the encrypted data */
4182    SMIME_write_PKCS7(outfile, p7, infile, flags);
4183
4184    RETVAL_TRUE;
4185
4186clean_exit:
4187    PKCS7_free(p7);
4188    BIO_free(infile);
4189    BIO_free(outfile);
4190    if (recipcerts) {
4191        sk_X509_pop_free(recipcerts, X509_free);
4192    }
4193}
4194/* }}} */
4195
4196/* {{{ proto bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]])
4197   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 */
4198
4199PHP_FUNCTION(openssl_pkcs7_sign)
4200{
4201    zval ** zcert, ** zprivkey, * zheaders;
4202    zval ** hval;
4203    X509 * cert = NULL;
4204    EVP_PKEY * privkey = NULL;
4205    long flags = PKCS7_DETACHED;
4206    PKCS7 * p7 = NULL;
4207    BIO * infile = NULL, * outfile = NULL;
4208    STACK_OF(X509) *others = NULL;
4209    long certresource = -1, keyresource = -1;
4210    ulong intindex;
4211    uint strindexlen;
4212    HashPosition hpos;
4213    char * strindex;
4214    char * infilename;  int infilename_len;
4215    char * outfilename; int outfilename_len;
4216    char * extracertsfilename = NULL; int extracertsfilename_len;
4217
4218    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZZa!|lp",
4219                &infilename, &infilename_len, &outfilename, &outfilename_len,
4220                &zcert, &zprivkey, &zheaders, &flags, &extracertsfilename,
4221                &extracertsfilename_len) == FAILURE) {
4222        return;
4223    }
4224
4225    RETVAL_FALSE;
4226
4227    if (extracertsfilename) {
4228        others = load_all_certs_from_file(extracertsfilename);
4229        if (others == NULL) {
4230            goto clean_exit;
4231        }
4232    }
4233
4234    privkey = php_openssl_evp_from_zval(zprivkey, 0, "", 0, &keyresource TSRMLS_CC);
4235    if (privkey == NULL) {
4236        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting private key");
4237        goto clean_exit;
4238    }
4239
4240    cert = php_openssl_x509_from_zval(zcert, 0, &certresource TSRMLS_CC);
4241    if (cert == NULL) {
4242        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error getting cert");
4243        goto clean_exit;
4244    }
4245
4246    if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) {
4247        goto clean_exit;
4248    }
4249
4250    infile = BIO_new_file(infilename, "r");
4251    if (infile == NULL) {
4252        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening input file %s!", infilename);
4253        goto clean_exit;
4254    }
4255
4256    outfile = BIO_new_file(outfilename, "w");
4257    if (outfile == NULL) {
4258        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error opening output file %s!", outfilename);
4259        goto clean_exit;
4260    }
4261
4262    p7 = PKCS7_sign(cert, privkey, others, infile, flags);
4263    if (p7 == NULL) {
4264        php_error_docref(NULL TSRMLS_CC, E_WARNING, "error creating PKCS7 structure!");
4265        goto clean_exit;
4266    }
4267
4268    (void)BIO_reset(infile);
4269
4270    /* tack on extra headers */
4271    if (zheaders) {
4272        zend_hash_internal_pointer_reset_ex(HASH_OF(zheaders), &hpos);
4273        while(zend_hash_get_current_data_ex(HASH_OF(zheaders), (void**)&hval, &hpos) == SUCCESS) {
4274            strindex = NULL;
4275            zend_hash_get_current_key_ex(HASH_OF(zheaders), &strindex, &strindexlen, &intindex, 0, &hpos);
4276
4277            convert_to_string_ex(hval);
4278
4279            if (strindex) {
4280                BIO_printf(outfile, "%s: %s\n", strindex, Z_STRVAL_PP(hval));
4281            } else {
4282                BIO_printf(outfile, "%s\n", Z_STRVAL_PP(hval));
4283            }
4284            zend_hash_move_forward_ex(HASH_OF(zheaders), &hpos);
4285        }
4286    }
4287    /* write the signed data */
4288    SMIME_write_PKCS7(outfile, p7, infile, flags);
4289
4290    RETVAL_TRUE;
4291
4292clean_exit:
4293    PKCS7_free(p7);
4294    BIO_free(infile);
4295    BIO_free(outfile);
4296    if (others) {
4297        sk_X509_pop_free(others, X509_free);
4298    }
4299    if (privkey && keyresource == -1) {
4300        EVP_PKEY_free(privkey);
4301    }
4302    if (cert && certresource == -1) {
4303        X509_free(cert);
4304    }
4305}
4306/* }}} */
4307
4308/* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey])
4309   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 */
4310
4311PHP_FUNCTION(openssl_pkcs7_decrypt)
4312{
4313    zval ** recipcert, ** recipkey = NULL;
4314    X509 * cert = NULL;
4315    EVP_PKEY * key = NULL;
4316    long certresval, keyresval;
4317    BIO * in = NULL, * out = NULL, * datain = NULL;
4318    PKCS7 * p7 = NULL;
4319    char * infilename;  int infilename_len;
4320    char * outfilename; int outfilename_len;
4321
4322    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ppZ|Z", &infilename, &infilename_len,
4323                &outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE) {
4324        return;
4325    }
4326
4327    RETVAL_FALSE;
4328
4329    cert = php_openssl_x509_from_zval(recipcert, 0, &certresval TSRMLS_CC);
4330    if (cert == NULL) {
4331        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 3 to x509 cert");
4332        goto clean_exit;
4333    }
4334
4335    key = php_openssl_evp_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, &keyresval TSRMLS_CC);
4336    if (key == NULL) {
4337        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to get private key");
4338        goto clean_exit;
4339    }
4340
4341    if (php_openssl_open_base_dir_chk(infilename TSRMLS_CC) || php_openssl_open_base_dir_chk(outfilename TSRMLS_CC)) {
4342        goto clean_exit;
4343    }
4344
4345    in = BIO_new_file(infilename, "r");
4346    if (in == NULL) {
4347        goto clean_exit;
4348    }
4349    out = BIO_new_file(outfilename, "w");
4350    if (out == NULL) {
4351        goto clean_exit;
4352    }
4353
4354    p7 = SMIME_read_PKCS7(in, &datain);
4355
4356    if (p7 == NULL) {
4357        goto clean_exit;
4358    }
4359    if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
4360        RETVAL_TRUE;
4361    }
4362clean_exit:
4363    PKCS7_free(p7);
4364    BIO_free(datain);
4365    BIO_free(in);
4366    BIO_free(out);
4367    if (cert && certresval == -1) {
4368        X509_free(cert);
4369    }
4370    if (key && keyresval == -1) {
4371        EVP_PKEY_free(key);
4372    }
4373}
4374/* }}} */
4375
4376/* }}} */
4377
4378/* {{{ proto bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding])
4379   Encrypts data with private key */
4380PHP_FUNCTION(openssl_private_encrypt)
4381{
4382    zval **key, *crypted;
4383    EVP_PKEY *pkey;
4384    int cryptedlen;
4385    unsigned char *cryptedbuf = NULL;
4386    int successful = 0;
4387    long keyresource = -1;
4388    char * data;
4389    int data_len;
4390    long padding = RSA_PKCS1_PADDING;
4391
4392    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
4393        return;
4394    }
4395    RETVAL_FALSE;
4396
4397    pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
4398
4399    if (pkey == NULL) {
4400        php_error_docref(NULL TSRMLS_CC, E_WARNING, "key param is not a valid private key");
4401        RETURN_FALSE;
4402    }
4403
4404    cryptedlen = EVP_PKEY_size(pkey);
4405    cryptedbuf = emalloc(cryptedlen + 1);
4406
4407    switch (pkey->type) {
4408        case EVP_PKEY_RSA:
4409        case EVP_PKEY_RSA2:
4410            successful =  (RSA_private_encrypt(data_len,
4411                        (unsigned char *)data,
4412                        cryptedbuf,
4413                        pkey->pkey.rsa,
4414                        padding) == cryptedlen);
4415            break;
4416        default:
4417            php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
4418    }
4419
4420    if (successful) {
4421        zval_dtor(crypted);
4422        cryptedbuf[cryptedlen] = '\0';
4423        ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
4424        cryptedbuf = NULL;
4425        RETVAL_TRUE;
4426    }
4427    if (cryptedbuf) {
4428        efree(cryptedbuf);
4429    }
4430    if (keyresource == -1) {
4431        EVP_PKEY_free(pkey);
4432    }
4433}
4434/* }}} */
4435
4436/* {{{ proto bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding])
4437   Decrypts data with private key */
4438PHP_FUNCTION(openssl_private_decrypt)
4439{
4440    zval **key, *crypted;
4441    EVP_PKEY *pkey;
4442    int cryptedlen;
4443    unsigned char *cryptedbuf = NULL;
4444    unsigned char *crypttemp;
4445    int successful = 0;
4446    long padding = RSA_PKCS1_PADDING;
4447    long keyresource = -1;
4448    char * data;
4449    int data_len;
4450
4451    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
4452        return;
4453    }
4454    RETVAL_FALSE;
4455
4456    pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
4457    if (pkey == NULL) {
4458        php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid private key");
4459        RETURN_FALSE;
4460    }
4461
4462    cryptedlen = EVP_PKEY_size(pkey);
4463    crypttemp = emalloc(cryptedlen + 1);
4464
4465    switch (pkey->type) {
4466        case EVP_PKEY_RSA:
4467        case EVP_PKEY_RSA2:
4468            cryptedlen = RSA_private_decrypt(data_len,
4469                    (unsigned char *)data,
4470                    crypttemp,
4471                    pkey->pkey.rsa,
4472                    padding);
4473            if (cryptedlen != -1) {
4474                cryptedbuf = emalloc(cryptedlen + 1);
4475                memcpy(cryptedbuf, crypttemp, cryptedlen);
4476                successful = 1;
4477            }
4478            break;
4479        default:
4480            php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
4481    }
4482
4483    efree(crypttemp);
4484
4485    if (successful) {
4486        zval_dtor(crypted);
4487        cryptedbuf[cryptedlen] = '\0';
4488        ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
4489        cryptedbuf = NULL;
4490        RETVAL_TRUE;
4491    }
4492
4493    if (keyresource == -1) {
4494        EVP_PKEY_free(pkey);
4495    }
4496    if (cryptedbuf) {
4497        efree(cryptedbuf);
4498    }
4499}
4500/* }}} */
4501
4502/* {{{ proto bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding])
4503   Encrypts data with public key */
4504PHP_FUNCTION(openssl_public_encrypt)
4505{
4506    zval **key, *crypted;
4507    EVP_PKEY *pkey;
4508    int cryptedlen;
4509    unsigned char *cryptedbuf;
4510    int successful = 0;
4511    long keyresource = -1;
4512    long padding = RSA_PKCS1_PADDING;
4513    char * data;
4514    int data_len;
4515
4516    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
4517        return;
4518
4519    RETVAL_FALSE;
4520
4521    pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
4522    if (pkey == NULL) {
4523        php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key");
4524        RETURN_FALSE;
4525    }
4526
4527    cryptedlen = EVP_PKEY_size(pkey);
4528    cryptedbuf = emalloc(cryptedlen + 1);
4529
4530    switch (pkey->type) {
4531        case EVP_PKEY_RSA:
4532        case EVP_PKEY_RSA2:
4533            successful = (RSA_public_encrypt(data_len,
4534                        (unsigned char *)data,
4535                        cryptedbuf,
4536                        pkey->pkey.rsa,
4537                        padding) == cryptedlen);
4538            break;
4539        default:
4540            php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
4541
4542    }
4543
4544    if (successful) {
4545        zval_dtor(crypted);
4546        cryptedbuf[cryptedlen] = '\0';
4547        ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
4548        cryptedbuf = NULL;
4549        RETVAL_TRUE;
4550    }
4551    if (keyresource == -1) {
4552        EVP_PKEY_free(pkey);
4553    }
4554    if (cryptedbuf) {
4555        efree(cryptedbuf);
4556    }
4557}
4558/* }}} */
4559
4560/* {{{ proto bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding])
4561   Decrypts data with public key */
4562PHP_FUNCTION(openssl_public_decrypt)
4563{
4564    zval **key, *crypted;
4565    EVP_PKEY *pkey;
4566    int cryptedlen;
4567    unsigned char *cryptedbuf = NULL;
4568    unsigned char *crypttemp;
4569    int successful = 0;
4570    long keyresource = -1;
4571    long padding = RSA_PKCS1_PADDING;
4572    char * data;
4573    int data_len;
4574
4575    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
4576        return;
4577    }
4578    RETVAL_FALSE;
4579
4580    pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
4581    if (pkey == NULL) {
4582        php_error_docref(NULL TSRMLS_CC, E_WARNING, "key parameter is not a valid public key");
4583        RETURN_FALSE;
4584    }
4585
4586    cryptedlen = EVP_PKEY_size(pkey);
4587    crypttemp = emalloc(cryptedlen + 1);
4588
4589    switch (pkey->type) {
4590        case EVP_PKEY_RSA:
4591        case EVP_PKEY_RSA2:
4592            cryptedlen = RSA_public_decrypt(data_len,
4593                    (unsigned char *)data,
4594                    crypttemp,
4595                    pkey->pkey.rsa,
4596                    padding);
4597            if (cryptedlen != -1) {
4598                cryptedbuf = emalloc(cryptedlen + 1);
4599                memcpy(cryptedbuf, crypttemp, cryptedlen);
4600                successful = 1;
4601            }
4602            break;
4603
4604        default:
4605            php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!");
4606
4607    }
4608
4609    efree(crypttemp);
4610
4611    if (successful) {
4612        zval_dtor(crypted);
4613        cryptedbuf[cryptedlen] = '\0';
4614        ZVAL_STRINGL(crypted, (char *)cryptedbuf, cryptedlen, 0);
4615        cryptedbuf = NULL;
4616        RETVAL_TRUE;
4617    }
4618
4619    if (cryptedbuf) {
4620        efree(cryptedbuf);
4621    }
4622    if (keyresource == -1) {
4623        EVP_PKEY_free(pkey);
4624    }
4625}
4626/* }}} */
4627
4628/* {{{ proto mixed openssl_error_string(void)
4629   Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */
4630PHP_FUNCTION(openssl_error_string)
4631{
4632    char buf[512];
4633    unsigned long val;
4634
4635    if (zend_parse_parameters_none() == FAILURE) {
4636        return;
4637    }
4638
4639    val = ERR_get_error();
4640    if (val) {
4641        RETURN_STRING(ERR_error_string(val, buf), 1);
4642    } else {
4643        RETURN_FALSE;
4644    }
4645}
4646/* }}} */
4647
4648/* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, mixed method])
4649   Signs data */
4650PHP_FUNCTION(openssl_sign)
4651{
4652    zval **key, *signature;
4653    EVP_PKEY *pkey;
4654    int siglen;
4655    unsigned char *sigbuf;
4656    long keyresource = -1;
4657    char * data;
4658    int data_len;
4659    EVP_MD_CTX md_ctx;
4660    zval *method = NULL;
4661    long signature_algo = OPENSSL_ALGO_SHA1;
4662    const EVP_MD *mdtype;
4663
4664    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|z", &data, &data_len, &signature, &key, &method) == FAILURE) {
4665        return;
4666    }
4667    pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC);
4668    if (pkey == NULL) {
4669        php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a private key");
4670        RETURN_FALSE;
4671    }
4672
4673    if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
4674        if (method != NULL) {
4675            signature_algo = Z_LVAL_P(method);
4676        }
4677        mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
4678    } else if (Z_TYPE_P(method) == IS_STRING) {
4679        mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
4680    } else {
4681        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4682        RETURN_FALSE;
4683    }
4684    if (!mdtype) {
4685        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4686        RETURN_FALSE;
4687    }
4688
4689    siglen = EVP_PKEY_size(pkey);
4690    sigbuf = emalloc(siglen + 1);
4691
4692    EVP_SignInit(&md_ctx, mdtype);
4693    EVP_SignUpdate(&md_ctx, data, data_len);
4694    if (EVP_SignFinal (&md_ctx, sigbuf,(unsigned int *)&siglen, pkey)) {
4695        zval_dtor(signature);
4696        sigbuf[siglen] = '\0';
4697        ZVAL_STRINGL(signature, (char *)sigbuf, siglen, 0);
4698        RETVAL_TRUE;
4699    } else {
4700        efree(sigbuf);
4701        RETVAL_FALSE;
4702    }
4703    EVP_MD_CTX_cleanup(&md_ctx);
4704    if (keyresource == -1) {
4705        EVP_PKEY_free(pkey);
4706    }
4707}
4708/* }}} */
4709
4710/* {{{ proto int openssl_verify(string data, string signature, mixed key[, mixed method])
4711   Verifys data */
4712PHP_FUNCTION(openssl_verify)
4713{
4714    zval **key;
4715    EVP_PKEY *pkey;
4716    int err;
4717    EVP_MD_CTX     md_ctx;
4718    const EVP_MD *mdtype;
4719    long keyresource = -1;
4720    char * data;    int data_len;
4721    char * signature;   int signature_len;
4722    zval *method = NULL;
4723    long signature_algo = OPENSSL_ALGO_SHA1;
4724
4725    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|z", &data, &data_len, &signature, &signature_len, &key, &method) == FAILURE) {
4726        return;
4727    }
4728
4729    if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
4730        if (method != NULL) {
4731            signature_algo = Z_LVAL_P(method);
4732        }
4733        mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
4734    } else if (Z_TYPE_P(method) == IS_STRING) {
4735        mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
4736    } else {
4737        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4738        RETURN_FALSE;
4739    }
4740    if (!mdtype) {
4741        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4742        RETURN_FALSE;
4743    }
4744
4745    pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource TSRMLS_CC);
4746    if (pkey == NULL) {
4747        php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied key param cannot be coerced into a public key");
4748        RETURN_FALSE;
4749    }
4750
4751    EVP_VerifyInit   (&md_ctx, mdtype);
4752    EVP_VerifyUpdate (&md_ctx, data, data_len);
4753    err = EVP_VerifyFinal (&md_ctx, (unsigned char *)signature, signature_len, pkey);
4754    EVP_MD_CTX_cleanup(&md_ctx);
4755
4756    if (keyresource == -1) {
4757        EVP_PKEY_free(pkey);
4758    }
4759    RETURN_LONG(err);
4760}
4761/* }}} */
4762
4763/* {{{ proto int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys)
4764   Seals data */
4765PHP_FUNCTION(openssl_seal)
4766{
4767    zval *pubkeys, **pubkey, *sealdata, *ekeys;
4768    HashTable *pubkeysht;
4769    HashPosition pos;
4770    EVP_PKEY **pkeys;
4771    long * key_resources;   /* so we know what to cleanup */
4772    int i, len1, len2, *eksl, nkeys;
4773    unsigned char *buf = NULL, **eks;
4774    char * data; int data_len;
4775    char *method =NULL;
4776    int method_len = 0;
4777    const EVP_CIPHER *cipher;
4778    EVP_CIPHER_CTX ctx;
4779
4780    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szza/|s", &data, &data_len, &sealdata, &ekeys, &pubkeys, &method, &method_len) == FAILURE) {
4781        return;
4782    }
4783
4784    pubkeysht = HASH_OF(pubkeys);
4785    nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
4786    if (!nkeys) {
4787        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Fourth argument to openssl_seal() must be a non-empty array");
4788        RETURN_FALSE;
4789    }
4790
4791    if (method) {
4792        cipher = EVP_get_cipherbyname(method);
4793        if (!cipher) {
4794            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4795            RETURN_FALSE;
4796        }
4797    } else {
4798        cipher = EVP_rc4();
4799    }
4800
4801    pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
4802    eksl = safe_emalloc(nkeys, sizeof(*eksl), 0);
4803    eks = safe_emalloc(nkeys, sizeof(*eks), 0);
4804    memset(eks, 0, sizeof(*eks) * nkeys);
4805    key_resources = safe_emalloc(nkeys, sizeof(long), 0);
4806    memset(key_resources, 0, sizeof(*key_resources) * nkeys);
4807
4808    /* get the public keys we are using to seal this data */
4809    zend_hash_internal_pointer_reset_ex(pubkeysht, &pos);
4810    i = 0;
4811    while (zend_hash_get_current_data_ex(pubkeysht, (void **) &pubkey,
4812                &pos) == SUCCESS) {
4813        pkeys[i] = php_openssl_evp_from_zval(pubkey, 1, NULL, 0, &key_resources[i] TSRMLS_CC);
4814        if (pkeys[i] == NULL) {
4815            php_error_docref(NULL TSRMLS_CC, E_WARNING, "not a public key (%dth member of pubkeys)", i+1);
4816            RETVAL_FALSE;
4817            goto clean_exit;
4818        }
4819        eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
4820        zend_hash_move_forward_ex(pubkeysht, &pos);
4821        i++;
4822    }
4823
4824    if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) {
4825        RETVAL_FALSE;
4826        EVP_CIPHER_CTX_cleanup(&ctx);
4827        goto clean_exit;
4828    }
4829
4830#if 0
4831    /* Need this if allow ciphers that require initialization vector */
4832    ivlen = EVP_CIPHER_CTX_iv_length(&ctx);
4833    iv = ivlen ? emalloc(ivlen + 1) : NULL;
4834#endif
4835    /* allocate one byte extra to make room for \0 */
4836    buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx));
4837    EVP_CIPHER_CTX_cleanup(&ctx);
4838
4839    if (!EVP_SealInit(&ctx, cipher, eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) {
4840        RETVAL_FALSE;
4841        efree(buf);
4842        EVP_CIPHER_CTX_cleanup(&ctx);
4843        goto clean_exit;
4844    }
4845
4846    EVP_SealFinal(&ctx, buf + len1, &len2);
4847
4848    if (len1 + len2 > 0) {
4849        zval_dtor(sealdata);
4850        buf[len1 + len2] = '\0';
4851        buf = erealloc(buf, len1 + len2 + 1);
4852        ZVAL_STRINGL(sealdata, (char *)buf, len1 + len2, 0);
4853
4854        zval_dtor(ekeys);
4855        array_init(ekeys);
4856        for (i=0; i<nkeys; i++) {
4857            eks[i][eksl[i]] = '\0';
4858            add_next_index_stringl(ekeys, erealloc(eks[i], eksl[i] + 1), eksl[i], 0);
4859            eks[i] = NULL;
4860        }
4861#if 0
4862        /* If allow ciphers that need IV, we need this */
4863        zval_dtor(*ivec);
4864        if (ivlen) {
4865            iv[ivlen] = '\0';
4866            ZVAL_STRINGL(*ivec, erealloc(iv, ivlen + 1), ivlen, 0);
4867        } else {
4868            ZVAL_EMPTY_STRING(*ivec);
4869        }
4870#endif
4871    } else {
4872        efree(buf);
4873    }
4874    RETVAL_LONG(len1 + len2);
4875    EVP_CIPHER_CTX_cleanup(&ctx);
4876
4877clean_exit:
4878    for (i=0; i<nkeys; i++) {
4879        if (key_resources[i] == -1) {
4880            EVP_PKEY_free(pkeys[i]);
4881        }
4882        if (eks[i]) {
4883            efree(eks[i]);
4884        }
4885    }
4886    efree(eks);
4887    efree(eksl);
4888    efree(pkeys);
4889    efree(key_resources);
4890}
4891/* }}} */
4892
4893/* {{{ proto bool openssl_open(string data, &string opendata, string ekey, mixed privkey)
4894   Opens data */
4895PHP_FUNCTION(openssl_open)
4896{
4897    zval **privkey, *opendata;
4898    EVP_PKEY *pkey;
4899    int len1, len2;
4900    unsigned char *buf;
4901    long keyresource = -1;
4902    EVP_CIPHER_CTX ctx;
4903    char * data;    int data_len;
4904    char * ekey;    int ekey_len;
4905    char *method =NULL;
4906    int method_len = 0;
4907    const EVP_CIPHER *cipher;
4908
4909    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szsZ|s", &data, &data_len, &opendata, &ekey, &ekey_len, &privkey, &method, &method_len) == FAILURE) {
4910        return;
4911    }
4912
4913    pkey = php_openssl_evp_from_zval(privkey, 0, "", 0, &keyresource TSRMLS_CC);
4914    if (pkey == NULL) {
4915        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 4 into a private key");
4916        RETURN_FALSE;
4917    }
4918
4919    if (method) {
4920        cipher = EVP_get_cipherbyname(method);
4921        if (!cipher) {
4922            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm.");
4923            RETURN_FALSE;
4924        }
4925    } else {
4926        cipher = EVP_rc4();
4927    }
4928
4929    buf = emalloc(data_len + 1);
4930
4931    if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, ekey_len, NULL, pkey) && EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) {
4932        if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) {
4933            efree(buf);
4934            RETVAL_FALSE;
4935        } else {
4936            zval_dtor(opendata);
4937            buf[len1 + len2] = '\0';
4938            ZVAL_STRINGL(opendata, erealloc(buf, len1 + len2 + 1), len1 + len2, 0);
4939            RETVAL_TRUE;
4940        }
4941    } else {
4942        efree(buf);
4943        RETVAL_FALSE;
4944    }
4945    if (keyresource == -1) {
4946        EVP_PKEY_free(pkey);
4947    }
4948    EVP_CIPHER_CTX_cleanup(&ctx);
4949}
4950/* }}} */
4951
4952
4953
4954static void openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */
4955{
4956    add_next_index_string((zval*)arg, (char*)name->name, 1);
4957}
4958/* }}} */
4959
4960static void openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */
4961{
4962    if (name->alias == 0) {
4963        add_next_index_string((zval*)arg, (char*)name->name, 1);
4964    }
4965}
4966/* }}} */
4967
4968/* {{{ proto array openssl_get_md_methods([bool aliases = false])
4969   Return array of available digest methods */
4970PHP_FUNCTION(openssl_get_md_methods)
4971{
4972    zend_bool aliases = 0;
4973
4974    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) {
4975        return;
4976    }
4977    array_init(return_value);
4978    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
4979        aliases ? openssl_add_method_or_alias: openssl_add_method,
4980        return_value);
4981}
4982/* }}} */
4983
4984/* {{{ proto array openssl_get_cipher_methods([bool aliases = false])
4985   Return array of available cipher methods */
4986PHP_FUNCTION(openssl_get_cipher_methods)
4987{
4988    zend_bool aliases = 0;
4989
4990    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) {
4991        return;
4992    }
4993    array_init(return_value);
4994    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
4995        aliases ? openssl_add_method_or_alias: openssl_add_method,
4996        return_value);
4997}
4998/* }}} */
4999
5000/* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false])
5001   Computes digest hash value for given data using given method, returns raw or binhex encoded string */
5002PHP_FUNCTION(openssl_digest)
5003{
5004    zend_bool raw_output = 0;
5005    char *data, *method;
5006    int data_len, method_len;
5007    const EVP_MD *mdtype;
5008    EVP_MD_CTX md_ctx;
5009    int siglen;
5010    unsigned char *sigbuf;
5011
5012    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
5013        return;
5014    }
5015    mdtype = EVP_get_digestbyname(method);
5016    if (!mdtype) {
5017        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
5018        RETURN_FALSE;
5019    }
5020
5021    siglen = EVP_MD_size(mdtype);
5022    sigbuf = emalloc(siglen + 1);
5023
5024    EVP_DigestInit(&md_ctx, mdtype);
5025    EVP_DigestUpdate(&md_ctx, (unsigned char *)data, data_len);
5026    if (EVP_DigestFinal (&md_ctx, (unsigned char *)sigbuf, (unsigned int *)&siglen)) {
5027        if (raw_output) {
5028            sigbuf[siglen] = '\0';
5029            RETVAL_STRINGL((char *)sigbuf, siglen, 0);
5030        } else {
5031            int digest_str_len = siglen * 2;
5032            char *digest_str = emalloc(digest_str_len + 1);
5033
5034            make_digest_ex(digest_str, sigbuf, siglen);
5035            efree(sigbuf);
5036            RETVAL_STRINGL(digest_str, digest_str_len, 0);
5037        }
5038    } else {
5039        efree(sigbuf);
5040        RETVAL_FALSE;
5041    }
5042}
5043/* }}} */
5044
5045static zend_bool php_openssl_validate_iv(char **piv, int *piv_len, int iv_required_len TSRMLS_DC)
5046{
5047    char *iv_new;
5048
5049    /* Best case scenario, user behaved */
5050    if (*piv_len == iv_required_len) {
5051        return 0;
5052    }
5053
5054    iv_new = ecalloc(1, iv_required_len + 1);
5055
5056    if (*piv_len <= 0) {
5057        /* BC behavior */
5058        *piv_len = iv_required_len;
5059        *piv     = iv_new;
5060        return 1;
5061    }
5062
5063    if (*piv_len < iv_required_len) {
5064        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);
5065        memcpy(iv_new, *piv, *piv_len);
5066        *piv_len = iv_required_len;
5067        *piv     = iv_new;
5068        return 1;
5069    }
5070
5071    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);
5072    memcpy(iv_new, *piv, iv_required_len);
5073    *piv_len = iv_required_len;
5074    *piv     = iv_new;
5075    return 1;
5076
5077}
5078
5079/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv='']])
5080   Encrypts given data with given method and key, returns raw or base64 encoded string */
5081PHP_FUNCTION(openssl_encrypt)
5082{
5083    long options = 0;
5084    char *data, *method, *password, *iv = "";
5085    int data_len, method_len, password_len, iv_len = 0, max_iv_len;
5086    const EVP_CIPHER *cipher_type;
5087    EVP_CIPHER_CTX cipher_ctx;
5088    int i=0, outlen, keylen;
5089    unsigned char *outbuf, *key;
5090    zend_bool free_iv;
5091
5092    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) {
5093        return;
5094    }
5095    cipher_type = EVP_get_cipherbyname(method);
5096    if (!cipher_type) {
5097        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
5098        RETURN_FALSE;
5099    }
5100
5101    keylen = EVP_CIPHER_key_length(cipher_type);
5102    if (keylen > password_len) {
5103        key = emalloc(keylen);
5104        memset(key, 0, keylen);
5105        memcpy(key, password, password_len);
5106    } else {
5107        key = (unsigned char*)password;
5108    }
5109
5110    max_iv_len = EVP_CIPHER_iv_length(cipher_type);
5111    if (iv_len <= 0 && max_iv_len > 0) {
5112        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
5113    }
5114    free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len TSRMLS_CC);
5115
5116    outlen = data_len + EVP_CIPHER_block_size(cipher_type);
5117    outbuf = emalloc(outlen + 1);
5118
5119    EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL);
5120    if (password_len > keylen) {
5121        EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len);
5122    }
5123    EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
5124    if (options & OPENSSL_ZERO_PADDING) {
5125        EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0);
5126    }
5127    if (data_len > 0) {
5128        EVP_EncryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len);
5129    }
5130    outlen = i;
5131    if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) {
5132        outlen += i;
5133        if (options & OPENSSL_RAW_DATA) {
5134            outbuf[outlen] = '\0';
5135            RETVAL_STRINGL((char *)outbuf, outlen, 0);
5136        } else {
5137            int base64_str_len;
5138            char *base64_str;
5139
5140            base64_str = (char*)php_base64_encode(outbuf, outlen, &base64_str_len);
5141            efree(outbuf);
5142            RETVAL_STRINGL(base64_str, base64_str_len, 0);
5143        }
5144    } else {
5145        efree(outbuf);
5146        RETVAL_FALSE;
5147    }
5148    if (key != (unsigned char*)password) {
5149        efree(key);
5150    }
5151    if (free_iv) {
5152        efree(iv);
5153    }
5154    EVP_CIPHER_CTX_cleanup(&cipher_ctx);
5155}
5156/* }}} */
5157
5158/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = '']])
5159   Takes raw or base64 encoded string and dectupt it using given method and key */
5160PHP_FUNCTION(openssl_decrypt)
5161{
5162    long options = 0;
5163    char *data, *method, *password, *iv = "";
5164    int data_len, method_len, password_len, iv_len = 0;
5165    const EVP_CIPHER *cipher_type;
5166    EVP_CIPHER_CTX cipher_ctx;
5167    int i, outlen, keylen;
5168    unsigned char *outbuf, *key;
5169    int base64_str_len;
5170    char *base64_str = NULL;
5171    zend_bool free_iv;
5172
5173    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) {
5174        return;
5175    }
5176
5177    if (!method_len) {
5178        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
5179        RETURN_FALSE;
5180    }
5181
5182    cipher_type = EVP_get_cipherbyname(method);
5183    if (!cipher_type) {
5184        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
5185        RETURN_FALSE;
5186    }
5187
5188    if (!(options & OPENSSL_RAW_DATA)) {
5189        base64_str = (char*)php_base64_decode((unsigned char*)data, data_len, &base64_str_len);
5190        if (!base64_str) {
5191            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to base64 decode the input");
5192            RETURN_FALSE;
5193        }
5194        data_len = base64_str_len;
5195        data = base64_str;
5196    }
5197
5198    keylen = EVP_CIPHER_key_length(cipher_type);
5199    if (keylen > password_len) {
5200        key = emalloc(keylen);
5201        memset(key, 0, keylen);
5202        memcpy(key, password, password_len);
5203    } else {
5204        key = (unsigned char*)password;
5205    }
5206
5207    free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type) TSRMLS_CC);
5208
5209    outlen = data_len + EVP_CIPHER_block_size(cipher_type);
5210    outbuf = emalloc(outlen + 1);
5211
5212    EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL);
5213    if (password_len > keylen) {
5214        EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len);
5215    }
5216    EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
5217    if (options & OPENSSL_ZERO_PADDING) {
5218        EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0);
5219    }
5220    EVP_DecryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len);
5221    outlen = i;
5222    if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) {
5223        outlen += i;
5224        outbuf[outlen] = '\0';
5225        RETVAL_STRINGL((char *)outbuf, outlen, 0);
5226    } else {
5227        efree(outbuf);
5228        RETVAL_FALSE;
5229    }
5230    if (key != (unsigned char*)password) {
5231        efree(key);
5232    }
5233    if (free_iv) {
5234        efree(iv);
5235    }
5236    if (base64_str) {
5237        efree(base64_str);
5238    }
5239    EVP_CIPHER_CTX_cleanup(&cipher_ctx);
5240}
5241/* }}} */
5242
5243/* {{{ proto int openssl_cipher_iv_length(string $method) */
5244PHP_FUNCTION(openssl_cipher_iv_length)
5245{
5246    char *method;
5247    int method_len;
5248    const EVP_CIPHER *cipher_type;
5249
5250    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len) == FAILURE) {
5251        return;
5252    }
5253
5254    if (!method_len) {
5255        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
5256        RETURN_FALSE;
5257    }
5258
5259    cipher_type = EVP_get_cipherbyname(method);
5260    if (!cipher_type) {
5261        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
5262        RETURN_FALSE;
5263    }
5264
5265    RETURN_LONG(EVP_CIPHER_iv_length(cipher_type));
5266}
5267/* }}} */
5268
5269
5270/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
5271   Computes shared secret for public value of remote DH key and local DH key */
5272PHP_FUNCTION(openssl_dh_compute_key)
5273{
5274    zval *key;
5275    char *pub_str;
5276    int pub_len;
5277    EVP_PKEY *pkey;
5278    BIGNUM *pub;
5279    char *data;
5280    int len;
5281
5282    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr", &pub_str, &pub_len, &key) == FAILURE) {
5283        return;
5284    }
5285    ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key);
5286    if (!pkey || EVP_PKEY_type(pkey->type) != EVP_PKEY_DH || !pkey->pkey.dh) {
5287        RETURN_FALSE;
5288    }
5289
5290    pub = BN_bin2bn((unsigned char*)pub_str, pub_len, NULL);
5291
5292    data = emalloc(DH_size(pkey->pkey.dh) + 1);
5293    len = DH_compute_key((unsigned char*)data, pub, pkey->pkey.dh);
5294
5295    if (len >= 0) {
5296        data[len] = 0;
5297        RETVAL_STRINGL(data, len, 0);
5298    } else {
5299        efree(data);
5300        RETVAL_FALSE;
5301    }
5302
5303    BN_free(pub);
5304}
5305/* }}} */
5306
5307/* {{{ proto string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result])
5308   Returns a string of the length specified filled with random pseudo bytes */
5309PHP_FUNCTION(openssl_random_pseudo_bytes)
5310{
5311    long buffer_length;
5312    unsigned char *buffer = NULL;
5313    zval *zstrong_result_returned = NULL;
5314    int strong_result = 0;
5315
5316    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) {
5317        return;
5318    }
5319
5320    if (buffer_length <= 0) {
5321        RETURN_FALSE;
5322    }
5323
5324    if (zstrong_result_returned) {
5325        zval_dtor(zstrong_result_returned);
5326        ZVAL_BOOL(zstrong_result_returned, 0);
5327    }
5328
5329    buffer = emalloc(buffer_length + 1);
5330
5331#ifdef PHP_WIN32
5332    strong_result = 1;
5333    /* random/urandom equivalent on Windows */
5334    if (php_win32_get_random_bytes(buffer, (size_t) buffer_length) == FAILURE){
5335        efree(buffer);
5336        if (zstrong_result_returned) {
5337            ZVAL_BOOL(zstrong_result_returned, 0);
5338        }
5339        RETURN_FALSE;
5340    }
5341#else
5342    if ((strong_result = RAND_pseudo_bytes(buffer, buffer_length)) < 0) {
5343        efree(buffer);
5344        if (zstrong_result_returned) {
5345            ZVAL_BOOL(zstrong_result_returned, 0);
5346        }
5347        RETURN_FALSE;
5348    }
5349#endif
5350
5351    buffer[buffer_length] = 0;
5352    RETVAL_STRINGL((char *)buffer, buffer_length, 0);
5353
5354    if (zstrong_result_returned) {
5355        ZVAL_BOOL(zstrong_result_returned, strong_result);
5356    }
5357}
5358/* }}} */
5359
5360/*
5361 * Local variables:
5362 * tab-width: 8
5363 * c-basic-offset: 8
5364 * End:
5365 * vim600: sw=4 ts=4 fdm=marker
5366 * vim<600: sw=4 ts=4
5367 */
5368
5369