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