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				if (NULL == strindex) {
2771					php_error_docref(NULL, E_WARNING, "dn: numeric fild names are not supported");
2772					continue;
2773				}
2774
2775				convert_to_string_ex(item);
2776
2777				nid = OBJ_txt2nid(ZSTR_VAL(strindex));
2778				if (nid != NID_undef) {
2779					if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)Z_STRVAL_P(item), -1, -1, 0)) {
2780						php_error_docref(NULL, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_P(item));
2781						return FAILURE;
2782					}
2783				} else {
2784					php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex));
2785				}
2786			} ZEND_HASH_FOREACH_END();
2787			for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
2788				v = sk_CONF_VALUE_value(attr_sk, i);
2789				/* if it is already set, skip this */
2790				nid = OBJ_txt2nid(v->name);
2791				if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) {
2792					continue;
2793				}
2794				if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_UTF8, (unsigned char*)v->value, -1)) {
2795					php_error_docref(NULL, E_WARNING,
2796						"add1_attr_by_txt %s -> %s (failed; check error queue "
2797						"and value of string_mask OpenSSL option if illegal "
2798						"characters are reported)",
2799						v->name, v->value);
2800					return FAILURE;
2801				}
2802			}
2803		}
2804	}
2805
2806	X509_REQ_set_pubkey(csr, req->priv_key);
2807	return SUCCESS;
2808}
2809/* }}} */
2810
2811/* {{{ php_openssl_csr_from_zval */
2812static X509_REQ * php_openssl_csr_from_zval(zval * val, int makeresource, zend_resource **resourceval)
2813{
2814	X509_REQ * csr = NULL;
2815	char * filename = NULL;
2816	BIO * in;
2817
2818	if (resourceval) {
2819		*resourceval = NULL;
2820	}
2821	if (Z_TYPE_P(val) == IS_RESOURCE) {
2822		void * what;
2823		zend_resource *res = Z_RES_P(val);
2824
2825		what = zend_fetch_resource(res, "OpenSSL X.509 CSR", le_csr);
2826		if (what) {
2827			if (resourceval) {
2828				*resourceval = res;
2829				Z_ADDREF_P(val);
2830			}
2831			return (X509_REQ*)what;
2832		}
2833		return NULL;
2834	} else if (Z_TYPE_P(val) != IS_STRING) {
2835		return NULL;
2836	}
2837
2838	if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {
2839		filename = Z_STRVAL_P(val) + (sizeof("file://") - 1);
2840	}
2841	if (filename) {
2842		if (php_openssl_open_base_dir_chk(filename)) {
2843			return NULL;
2844		}
2845		in = BIO_new_file(filename, "r");
2846	} else {
2847		in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
2848	}
2849	csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
2850	BIO_free(in);
2851
2852	return csr;
2853}
2854/* }}} */
2855
2856/* {{{ proto bool openssl_csr_export_to_file(resource csr, string outfilename [, bool notext=true])
2857   Exports a CSR to file */
2858PHP_FUNCTION(openssl_csr_export_to_file)
2859{
2860	X509_REQ * csr;
2861	zval * zcsr = NULL;
2862	zend_bool notext = 1;
2863	char * filename = NULL;
2864	size_t filename_len;
2865	BIO * bio_out;
2866	zend_resource *csr_resource;
2867
2868	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rp|b", &zcsr, &filename, &filename_len, &notext) == FAILURE) {
2869		return;
2870	}
2871	RETVAL_FALSE;
2872
2873	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
2874	if (csr == NULL) {
2875		php_error_docref(NULL, E_WARNING, "cannot get CSR from parameter 1");
2876		return;
2877	}
2878
2879	if (php_openssl_open_base_dir_chk(filename)) {
2880		return;
2881	}
2882
2883	bio_out = BIO_new_file(filename, "w");
2884	if (bio_out) {
2885		if (!notext) {
2886			X509_REQ_print(bio_out, csr);
2887		}
2888		PEM_write_bio_X509_REQ(bio_out, csr);
2889		RETVAL_TRUE;
2890	} else {
2891		php_error_docref(NULL, E_WARNING, "error opening file %s", filename);
2892	}
2893
2894	if (csr_resource == NULL && csr) {
2895		X509_REQ_free(csr);
2896	}
2897	BIO_free(bio_out);
2898}
2899/* }}} */
2900
2901/* {{{ proto bool openssl_csr_export(resource csr, string &out [, bool notext=true])
2902   Exports a CSR to file or a var */
2903PHP_FUNCTION(openssl_csr_export)
2904{
2905	X509_REQ * csr;
2906	zval * zcsr = NULL, *zout=NULL;
2907	zend_bool notext = 1;
2908	BIO * bio_out;
2909	zend_resource *csr_resource;
2910
2911	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz/|b", &zcsr, &zout, &notext) == FAILURE) {
2912		return;
2913	}
2914
2915	RETVAL_FALSE;
2916
2917	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
2918	if (csr == NULL) {
2919		php_error_docref(NULL, E_WARNING, "cannot get CSR from parameter 1");
2920		return;
2921	}
2922
2923	/* export to a var */
2924
2925	bio_out = BIO_new(BIO_s_mem());
2926	if (!notext) {
2927		X509_REQ_print(bio_out, csr);
2928	}
2929
2930	if (PEM_write_bio_X509_REQ(bio_out, csr)) {
2931		BUF_MEM *bio_buf;
2932
2933		BIO_get_mem_ptr(bio_out, &bio_buf);
2934		zval_dtor(zout);
2935		ZVAL_STRINGL(zout, bio_buf->data, bio_buf->length);
2936
2937		RETVAL_TRUE;
2938	}
2939
2940	if (csr_resource == NULL && csr) {
2941		X509_REQ_free(csr);
2942	}
2943	BIO_free(bio_out);
2944}
2945/* }}} */
2946
2947/* {{{ proto resource openssl_csr_sign(mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]])
2948   Signs a cert with another CERT */
2949PHP_FUNCTION(openssl_csr_sign)
2950{
2951	zval * zcert = NULL, *zcsr, *zpkey, *args = NULL;
2952	zend_long num_days;
2953	zend_long serial = Z_L(0);
2954	X509 * cert = NULL, *new_cert = NULL;
2955	X509_REQ * csr;
2956	EVP_PKEY * key = NULL, *priv_key = NULL;
2957	zend_resource *csr_resource, *certresource = NULL, *keyresource = NULL;
2958	int i;
2959	struct php_x509_request req;
2960
2961	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz!zl|a!l", &zcsr, &zcert, &zpkey, &num_days, &args, &serial) == FAILURE)
2962		return;
2963
2964	RETVAL_FALSE;
2965	PHP_SSL_REQ_INIT(&req);
2966
2967	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
2968	if (csr == NULL) {
2969		php_error_docref(NULL, E_WARNING, "cannot get CSR from parameter 1");
2970		return;
2971	}
2972	if (zcert) {
2973		cert = php_openssl_x509_from_zval(zcert, 0, &certresource);
2974		if (cert == NULL) {
2975			php_error_docref(NULL, E_WARNING, "cannot get cert from parameter 2");
2976			goto cleanup;
2977		}
2978	}
2979	priv_key = php_openssl_evp_from_zval(zpkey, 0, "", 1, &keyresource);
2980	if (priv_key == NULL) {
2981		php_error_docref(NULL, E_WARNING, "cannot get private key from parameter 3");
2982		goto cleanup;
2983	}
2984	if (cert && !X509_check_private_key(cert, priv_key)) {
2985		php_error_docref(NULL, E_WARNING, "private key does not correspond to signing cert");
2986		goto cleanup;
2987	}
2988
2989	if (PHP_SSL_REQ_PARSE(&req, args) == FAILURE) {
2990		goto cleanup;
2991	}
2992	/* Check that the request matches the signature */
2993	key = X509_REQ_get_pubkey(csr);
2994	if (key == NULL) {
2995		php_error_docref(NULL, E_WARNING, "error unpacking public key");
2996		goto cleanup;
2997	}
2998	i = X509_REQ_verify(csr, key);
2999
3000	if (i < 0) {
3001		php_error_docref(NULL, E_WARNING, "Signature verification problems");
3002		goto cleanup;
3003	}
3004	else if (i == 0) {
3005		php_error_docref(NULL, E_WARNING, "Signature did not match the certificate request");
3006		goto cleanup;
3007	}
3008
3009	/* Now we can get on with it */
3010
3011	new_cert = X509_new();
3012	if (new_cert == NULL) {
3013		php_error_docref(NULL, E_WARNING, "No memory");
3014		goto cleanup;
3015	}
3016	/* Version 3 cert */
3017	if (!X509_set_version(new_cert, 2))
3018		goto cleanup;
3019
3020
3021	ASN1_INTEGER_set(X509_get_serialNumber(new_cert), (long)serial);
3022
3023	X509_set_subject_name(new_cert, X509_REQ_get_subject_name(csr));
3024
3025	if (cert == NULL) {
3026		cert = new_cert;
3027	}
3028	if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
3029		goto cleanup;
3030	}
3031	X509_gmtime_adj(X509_get_notBefore(new_cert), 0);
3032	X509_gmtime_adj(X509_get_notAfter(new_cert), 60*60*24*(long)num_days);
3033	i = X509_set_pubkey(new_cert, key);
3034	if (!i) {
3035		goto cleanup;
3036	}
3037	if (req.extensions_section) {
3038		X509V3_CTX ctx;
3039
3040		X509V3_set_ctx(&ctx, cert, new_cert, csr, NULL, 0);
3041		X509V3_set_conf_lhash(&ctx, req.req_config);
3042		if (!X509V3_EXT_add_conf(req.req_config, &ctx, req.extensions_section, new_cert)) {
3043			goto cleanup;
3044		}
3045	}
3046
3047	/* Now sign it */
3048	if (!X509_sign(new_cert, priv_key, req.digest)) {
3049		php_error_docref(NULL, E_WARNING, "failed to sign it");
3050		goto cleanup;
3051	}
3052
3053	/* Succeeded; lets return the cert */
3054	ZVAL_RES(return_value, zend_register_resource(new_cert, le_x509));
3055	new_cert = NULL;
3056
3057cleanup:
3058
3059	if (cert == new_cert) {
3060		cert = NULL;
3061	}
3062	PHP_SSL_REQ_DISPOSE(&req);
3063
3064	if (keyresource == NULL && priv_key) {
3065		EVP_PKEY_free(priv_key);
3066	}
3067	if (key) {
3068		EVP_PKEY_free(key);
3069	}
3070	if (csr_resource == NULL && csr) {
3071		X509_REQ_free(csr);
3072	}
3073	if (zcert && certresource == NULL && cert) {
3074		X509_free(cert);
3075	}
3076	if (new_cert) {
3077		X509_free(new_cert);
3078	}
3079}
3080/* }}} */
3081
3082/* {{{ proto bool openssl_csr_new(array dn, resource &privkey [, array configargs [, array extraattribs]])
3083   Generates a privkey and CSR */
3084PHP_FUNCTION(openssl_csr_new)
3085{
3086	struct php_x509_request req;
3087	zval * args = NULL, * dn, *attribs = NULL;
3088	zval * out_pkey;
3089	X509_REQ * csr = NULL;
3090	int we_made_the_key = 1;
3091	zend_resource *key_resource;
3092
3093	if (zend_parse_parameters(ZEND_NUM_ARGS(), "az/|a!a!", &dn, &out_pkey, &args, &attribs) == FAILURE) {
3094		return;
3095	}
3096	RETVAL_FALSE;
3097
3098	PHP_SSL_REQ_INIT(&req);
3099
3100	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3101		/* Generate or use a private key */
3102		if (Z_TYPE_P(out_pkey) != IS_NULL) {
3103			req.priv_key = php_openssl_evp_from_zval(out_pkey, 0, NULL, 0, &key_resource);
3104			if (req.priv_key != NULL) {
3105				we_made_the_key = 0;
3106			}
3107		}
3108		if (req.priv_key == NULL) {
3109			php_openssl_generate_private_key(&req);
3110		}
3111		if (req.priv_key == NULL) {
3112			php_error_docref(NULL, E_WARNING, "Unable to generate a private key");
3113		} else {
3114			csr = X509_REQ_new();
3115			if (csr) {
3116				if (php_openssl_make_REQ(&req, csr, dn, attribs) == SUCCESS) {
3117					X509V3_CTX ext_ctx;
3118
3119					X509V3_set_ctx(&ext_ctx, NULL, NULL, csr, NULL, 0);
3120					X509V3_set_conf_lhash(&ext_ctx, req.req_config);
3121
3122					/* Add extensions */
3123					if (req.request_extensions_section && !X509V3_EXT_REQ_add_conf(req.req_config,
3124								&ext_ctx, req.request_extensions_section, csr))
3125					{
3126						php_error_docref(NULL, E_WARNING, "Error loading extension section %s", req.request_extensions_section);
3127					} else {
3128						RETVAL_TRUE;
3129
3130						if (X509_REQ_sign(csr, req.priv_key, req.digest)) {
3131							ZVAL_RES(return_value, zend_register_resource(csr, le_csr));
3132							csr = NULL;
3133						} else {
3134							php_error_docref(NULL, E_WARNING, "Error signing request");
3135						}
3136
3137						if (we_made_the_key) {
3138							/* and a resource for the private key */
3139							zval_dtor(out_pkey);
3140							ZVAL_RES(out_pkey, zend_register_resource(req.priv_key, le_key));
3141							req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
3142						} else if (key_resource != NULL) {
3143							req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
3144						}
3145					}
3146				}
3147				else {
3148					if (!we_made_the_key) {
3149						/* if we have not made the key we are not supposed to zap it by calling dispose! */
3150						req.priv_key = NULL;
3151					}
3152				}
3153			}
3154		}
3155	}
3156	if (csr) {
3157		X509_REQ_free(csr);
3158	}
3159	PHP_SSL_REQ_DISPOSE(&req);
3160}
3161/* }}} */
3162
3163/* {{{ proto mixed openssl_csr_get_subject(mixed csr)
3164   Returns the subject of a CERT or FALSE on error */
3165PHP_FUNCTION(openssl_csr_get_subject)
3166{
3167	zval * zcsr;
3168	zend_bool use_shortnames = 1;
3169	zend_resource *csr_resource;
3170	X509_NAME * subject;
3171	X509_REQ * csr;
3172
3173	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &zcsr, &use_shortnames) == FAILURE) {
3174		return;
3175	}
3176
3177	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
3178
3179	if (csr == NULL) {
3180		RETURN_FALSE;
3181	}
3182
3183	subject = X509_REQ_get_subject_name(csr);
3184
3185	array_init(return_value);
3186	add_assoc_name_entry(return_value, NULL, subject, use_shortnames);
3187	return;
3188}
3189/* }}} */
3190
3191/* {{{ proto mixed openssl_csr_get_public_key(mixed csr)
3192	Returns the subject of a CERT or FALSE on error */
3193PHP_FUNCTION(openssl_csr_get_public_key)
3194{
3195	zval * zcsr;
3196	zend_bool use_shortnames = 1;
3197	zend_resource *csr_resource;
3198
3199	X509_REQ * csr;
3200	EVP_PKEY *tpubkey;
3201
3202	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &zcsr, &use_shortnames) == FAILURE) {
3203		return;
3204	}
3205
3206	csr = php_openssl_csr_from_zval(zcsr, 0, &csr_resource);
3207
3208	if (csr == NULL) {
3209		RETURN_FALSE;
3210	}
3211
3212	tpubkey=X509_REQ_get_pubkey(csr);
3213	RETURN_RES(zend_register_resource(tpubkey, le_key));
3214}
3215/* }}} */
3216
3217/* }}} */
3218
3219/* {{{ EVP Public/Private key functions */
3220
3221/* {{{ php_openssl_evp_from_zval
3222   Given a zval, coerce it into a EVP_PKEY object.
3223	It can be:
3224		1. private key resource from openssl_get_privatekey()
3225		2. X509 resource -> public key will be extracted from it
3226		3. if it starts with file:// interpreted as path to key file
3227		4. interpreted as the data from the cert/key file and interpreted in same way as openssl_get_privatekey()
3228		5. an array(0 => [items 2..4], 1 => passphrase)
3229		6. if val is a string (possibly starting with file:///) and it is not an X509 certificate, then interpret as public key
3230	NOTE: If you are requesting a private key but have not specified a passphrase, you should use an
3231	empty string rather than NULL for the passphrase - NULL causes a passphrase prompt to be emitted in
3232	the Apache error log!
3233*/
3234static EVP_PKEY * php_openssl_evp_from_zval(zval * val, int public_key, char * passphrase, int makeresource, zend_resource **resourceval)
3235{
3236	EVP_PKEY * key = NULL;
3237	X509 * cert = NULL;
3238	int free_cert = 0;
3239	zend_resource *cert_res = NULL;
3240	char * filename = NULL;
3241	zval tmp;
3242
3243	ZVAL_NULL(&tmp);
3244
3245#define TMP_CLEAN \
3246	if (Z_TYPE(tmp) == IS_STRING) {\
3247		zval_dtor(&tmp); \
3248	} \
3249	return NULL;
3250
3251	if (resourceval) {
3252		*resourceval = NULL;
3253	}
3254	if (Z_TYPE_P(val) == IS_ARRAY) {
3255		zval * zphrase;
3256
3257		/* get passphrase */
3258
3259		if ((zphrase = zend_hash_index_find(Z_ARRVAL_P(val), 1)) == NULL) {
3260			php_error_docref(NULL, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
3261			return NULL;
3262		}
3263
3264		if (Z_TYPE_P(zphrase) == IS_STRING) {
3265			passphrase = Z_STRVAL_P(zphrase);
3266		} else {
3267			ZVAL_COPY(&tmp, zphrase);
3268			convert_to_string(&tmp);
3269			passphrase = Z_STRVAL(tmp);
3270		}
3271
3272		/* now set val to be the key param and continue */
3273		if ((val = zend_hash_index_find(Z_ARRVAL_P(val), 0)) == NULL) {
3274			php_error_docref(NULL, E_WARNING, "key array must be of the form array(0 => key, 1 => phrase)");
3275			TMP_CLEAN;
3276		}
3277	}
3278
3279	if (Z_TYPE_P(val) == IS_RESOURCE) {
3280		void * what;
3281		zend_resource * res = Z_RES_P(val);
3282
3283		what = zend_fetch_resource2(res, "OpenSSL X.509/key", le_x509, le_key);
3284		if (!what) {
3285			TMP_CLEAN;
3286		}
3287		if (resourceval) {
3288			*resourceval = res;
3289			Z_ADDREF_P(val);
3290		}
3291		if (res->type == le_x509) {
3292			/* extract key from cert, depending on public_key param */
3293			cert = (X509*)what;
3294			free_cert = 0;
3295		} else if (res->type == le_key) {
3296			int is_priv;
3297
3298			is_priv = php_openssl_is_private_key((EVP_PKEY*)what);
3299
3300			/* check whether it is actually a private key if requested */
3301			if (!public_key && !is_priv) {
3302				php_error_docref(NULL, E_WARNING, "supplied key param is a public key");
3303				TMP_CLEAN;
3304			}
3305
3306			if (public_key && is_priv) {
3307				php_error_docref(NULL, E_WARNING, "Don't know how to get public key from this private key");
3308				TMP_CLEAN;
3309			} else {
3310				if (Z_TYPE(tmp) == IS_STRING) {
3311					zval_dtor(&tmp);
3312				}
3313				/* got the key - return it */
3314				return (EVP_PKEY*)what;
3315			}
3316		} else {
3317			/* other types could be used here - eg: file pointers and read in the data from them */
3318			TMP_CLEAN;
3319		}
3320	} else {
3321		/* force it to be a string and check if it refers to a file */
3322		/* passing non string values leaks, object uses toString, it returns NULL
3323		 * See bug38255.phpt
3324		 */
3325		if (!(Z_TYPE_P(val) == IS_STRING || Z_TYPE_P(val) == IS_OBJECT)) {
3326			TMP_CLEAN;
3327		}
3328		convert_to_string_ex(val);
3329
3330		if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {
3331			filename = Z_STRVAL_P(val) + (sizeof("file://") - 1);
3332		}
3333		/* it's an X509 file/cert of some kind, and we need to extract the data from that */
3334		if (public_key) {
3335			cert = php_openssl_x509_from_zval(val, 0, &cert_res);
3336			free_cert = (cert_res == NULL);
3337			/* actual extraction done later */
3338			if (!cert) {
3339				/* not a X509 certificate, try to retrieve public key */
3340				BIO* in;
3341				if (filename) {
3342					in = BIO_new_file(filename, "r");
3343				} else {
3344					in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
3345				}
3346				if (in == NULL) {
3347					TMP_CLEAN;
3348				}
3349				key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
3350				BIO_free(in);
3351			}
3352		} else {
3353			/* we want the private key */
3354			BIO *in;
3355
3356			if (filename) {
3357				if (php_openssl_open_base_dir_chk(filename)) {
3358					TMP_CLEAN;
3359				}
3360				in = BIO_new_file(filename, "r");
3361			} else {
3362				in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));
3363			}
3364
3365			if (in == NULL) {
3366				TMP_CLEAN;
3367			}
3368			key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);
3369			BIO_free(in);
3370		}
3371	}
3372
3373	if (public_key && cert && key == NULL) {
3374		/* extract public key from X509 cert */
3375		key = (EVP_PKEY *) X509_get_pubkey(cert);
3376	}
3377
3378	if (free_cert && cert) {
3379		X509_free(cert);
3380	}
3381	if (key && makeresource && resourceval) {
3382		*resourceval = zend_register_resource(key, le_key);
3383	}
3384	if (Z_TYPE(tmp) == IS_STRING) {
3385		zval_dtor(&tmp);
3386	}
3387	return key;
3388}
3389/* }}} */
3390
3391/* {{{ php_openssl_generate_private_key */
3392static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req)
3393{
3394	char * randfile = NULL;
3395	int egdsocket, seeded;
3396	EVP_PKEY * return_val = NULL;
3397
3398	if (req->priv_key_bits < MIN_KEY_LENGTH) {
3399		php_error_docref(NULL, E_WARNING, "private key length is too short; it needs to be at least %d bits, not %d",
3400				MIN_KEY_LENGTH, req->priv_key_bits);
3401		return NULL;
3402	}
3403
3404	randfile = CONF_get_string(req->req_config, req->section_name, "RANDFILE");
3405	php_openssl_load_rand_file(randfile, &egdsocket, &seeded);
3406
3407	if ((req->priv_key = EVP_PKEY_new()) != NULL) {
3408		switch(req->priv_key_type) {
3409			case OPENSSL_KEYTYPE_RSA:
3410				{
3411					RSA* rsaparam;
3412#if OPENSSL_VERSION_NUMBER < 0x10002000L
3413					/* OpenSSL 1.0.2 deprecates RSA_generate_key */
3414					rsaparam = (RSA*)RSA_generate_key(req->priv_key_bits, RSA_F4, NULL, NULL);
3415#else
3416					{
3417						BIGNUM *bne = (BIGNUM *)BN_new();
3418						if (BN_set_word(bne, RSA_F4) != 1) {
3419							BN_free(bne);
3420							php_error_docref(NULL, E_WARNING, "failed setting exponent");
3421							return NULL;
3422						}
3423						rsaparam = RSA_new();
3424						RSA_generate_key_ex(rsaparam, req->priv_key_bits, bne, NULL);
3425						BN_free(bne);
3426					}
3427#endif
3428					if (rsaparam && EVP_PKEY_assign_RSA(req->priv_key, rsaparam)) {
3429						return_val = req->priv_key;
3430					}
3431				}
3432				break;
3433#if !defined(NO_DSA)
3434			case OPENSSL_KEYTYPE_DSA:
3435				{
3436					DSA *dsaparam = NULL;
3437#if OPENSSL_VERSION_NUMBER < 0x10002000L
3438					dsaparam = DSA_generate_parameters(req->priv_key_bits, NULL, 0, NULL, NULL, NULL, NULL);
3439#else
3440					DSA_generate_parameters_ex(dsaparam, req->priv_key_bits, NULL, 0, NULL, NULL, NULL);
3441#endif
3442					if (dsaparam) {
3443						DSA_set_method(dsaparam, DSA_get_default_method());
3444						if (DSA_generate_key(dsaparam)) {
3445							if (EVP_PKEY_assign_DSA(req->priv_key, dsaparam)) {
3446								return_val = req->priv_key;
3447							}
3448						} else {
3449							DSA_free(dsaparam);
3450						}
3451					}
3452				}
3453				break;
3454#endif
3455#if !defined(NO_DH)
3456			case OPENSSL_KEYTYPE_DH:
3457				{
3458					int codes = 0;
3459					DH *dhparam = NULL;
3460#if OPENSSL_VERSION_NUMBER < 0x10002000L
3461					dhparam = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL);
3462#else
3463					DH_generate_parameters_ex(dhparam, req->priv_key_bits, 2, NULL);
3464#endif
3465					if (dhparam) {
3466						DH_set_method(dhparam, DH_get_default_method());
3467						if (DH_check(dhparam, &codes) && codes == 0 && DH_generate_key(dhparam)) {
3468							if (EVP_PKEY_assign_DH(req->priv_key, dhparam)) {
3469								return_val = req->priv_key;
3470							}
3471						} else {
3472							DH_free(dhparam);
3473						}
3474					}
3475				}
3476				break;
3477#endif
3478			default:
3479				php_error_docref(NULL, E_WARNING, "Unsupported private key type");
3480		}
3481	}
3482
3483	php_openssl_write_rand_file(randfile, egdsocket, seeded);
3484
3485	if (return_val == NULL) {
3486		EVP_PKEY_free(req->priv_key);
3487		req->priv_key = NULL;
3488		return NULL;
3489	}
3490
3491	return return_val;
3492}
3493/* }}} */
3494
3495/* {{{ php_openssl_is_private_key
3496	Check whether the supplied key is a private key by checking if the secret prime factors are set */
3497static int php_openssl_is_private_key(EVP_PKEY* pkey)
3498{
3499	assert(pkey != NULL);
3500
3501	switch (pkey->type) {
3502#ifndef NO_RSA
3503		case EVP_PKEY_RSA:
3504		case EVP_PKEY_RSA2:
3505			assert(pkey->pkey.rsa != NULL);
3506			if (pkey->pkey.rsa != NULL && (NULL == pkey->pkey.rsa->p || NULL == pkey->pkey.rsa->q)) {
3507				return 0;
3508			}
3509			break;
3510#endif
3511#ifndef NO_DSA
3512		case EVP_PKEY_DSA:
3513		case EVP_PKEY_DSA1:
3514		case EVP_PKEY_DSA2:
3515		case EVP_PKEY_DSA3:
3516		case EVP_PKEY_DSA4:
3517			assert(pkey->pkey.dsa != NULL);
3518
3519			if (NULL == pkey->pkey.dsa->p || NULL == pkey->pkey.dsa->q || NULL == pkey->pkey.dsa->priv_key){
3520				return 0;
3521			}
3522			break;
3523#endif
3524#ifndef NO_DH
3525		case EVP_PKEY_DH:
3526			assert(pkey->pkey.dh != NULL);
3527
3528			if (NULL == pkey->pkey.dh->p || NULL == pkey->pkey.dh->priv_key) {
3529				return 0;
3530			}
3531			break;
3532#endif
3533#ifdef HAVE_EVP_PKEY_EC
3534		case EVP_PKEY_EC:
3535			assert(pkey->pkey.ec != NULL);
3536
3537			if ( NULL == EC_KEY_get0_private_key(pkey->pkey.ec)) {
3538				return 0;
3539			}
3540			break;
3541#endif
3542		default:
3543			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
3544			break;
3545	}
3546	return 1;
3547}
3548/* }}} */
3549
3550#define OPENSSL_PKEY_GET_BN(_type, _name) do {							\
3551		if (pkey->pkey._type->_name != NULL) {							\
3552			int len = BN_num_bytes(pkey->pkey._type->_name);			\
3553			zend_string *str = zend_string_alloc(len, 0);				\
3554			BN_bn2bin(pkey->pkey._type->_name, (unsigned char*)ZSTR_VAL(str));	\
3555			ZSTR_VAL(str)[len] = 0;										\
3556			add_assoc_str(&_type, #_name, str);							\
3557		}																\
3558	} while (0)
3559
3560#define OPENSSL_PKEY_SET_BN(_ht, _type, _name) do {						\
3561		zval *bn;														\
3562		if ((bn = zend_hash_str_find(_ht, #_name, sizeof(#_name)-1)) != NULL && \
3563				Z_TYPE_P(bn) == IS_STRING) {							\
3564			_type->_name = BN_bin2bn(									\
3565				(unsigned char*)Z_STRVAL_P(bn),							\
3566	 			(int)Z_STRLEN_P(bn), NULL);									\
3567	    }                                                               \
3568	} while (0);
3569
3570
3571/* {{{ proto resource openssl_pkey_new([array configargs])
3572   Generates a new private key */
3573PHP_FUNCTION(openssl_pkey_new)
3574{
3575	struct php_x509_request req;
3576	zval * args = NULL;
3577	zval *data;
3578
3579	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &args) == FAILURE) {
3580		return;
3581	}
3582	RETVAL_FALSE;
3583
3584	if (args && Z_TYPE_P(args) == IS_ARRAY) {
3585		EVP_PKEY *pkey;
3586
3587		if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa")-1)) != NULL &&
3588			Z_TYPE_P(data) == IS_ARRAY) {
3589			pkey = EVP_PKEY_new();
3590			if (pkey) {
3591				RSA *rsa = RSA_new();
3592				if (rsa) {
3593					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, n);
3594					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, e);
3595					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, d);
3596					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, p);
3597					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, q);
3598					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, dmp1);
3599					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, dmq1);
3600					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), rsa, iqmp);
3601					if (rsa->n && rsa->d) {
3602						if (EVP_PKEY_assign_RSA(pkey, rsa)) {
3603							RETURN_RES(zend_register_resource(pkey, le_key));
3604						}
3605					}
3606					RSA_free(rsa);
3607				}
3608				EVP_PKEY_free(pkey);
3609			}
3610			RETURN_FALSE;
3611		} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa") - 1)) != NULL &&
3612			Z_TYPE_P(data) == IS_ARRAY) {
3613			pkey = EVP_PKEY_new();
3614			if (pkey) {
3615				DSA *dsa = DSA_new();
3616				if (dsa) {
3617					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, p);
3618					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, q);
3619					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, g);
3620					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, priv_key);
3621					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dsa, pub_key);
3622					if (dsa->p && dsa->q && dsa->g) {
3623						if (!dsa->priv_key && !dsa->pub_key) {
3624							DSA_generate_key(dsa);
3625						}
3626						if (EVP_PKEY_assign_DSA(pkey, dsa)) {
3627							RETURN_RES(zend_register_resource(pkey, le_key));
3628						}
3629					}
3630					DSA_free(dsa);
3631				}
3632				EVP_PKEY_free(pkey);
3633			}
3634			RETURN_FALSE;
3635		} else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "dh", sizeof("dh") - 1)) != NULL &&
3636			Z_TYPE_P(data) == IS_ARRAY) {
3637			pkey = EVP_PKEY_new();
3638			if (pkey) {
3639				DH *dh = DH_new();
3640				if (dh) {
3641					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dh, p);
3642					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dh, g);
3643					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dh, priv_key);
3644					OPENSSL_PKEY_SET_BN(Z_ARRVAL_P(data), dh, pub_key);
3645					if (dh->p && dh->g &&
3646							(dh->pub_key || DH_generate_key(dh)) &&
3647							EVP_PKEY_assign_DH(pkey, dh)) {
3648						ZVAL_COPY_VALUE(return_value, zend_list_insert(pkey, le_key));
3649						return;
3650					}
3651					DH_free(dh);
3652				}
3653				EVP_PKEY_free(pkey);
3654			}
3655			RETURN_FALSE;
3656		}
3657	}
3658
3659	PHP_SSL_REQ_INIT(&req);
3660
3661	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS)
3662	{
3663		if (php_openssl_generate_private_key(&req)) {
3664			/* pass back a key resource */
3665			RETVAL_RES(zend_register_resource(req.priv_key, le_key));
3666			/* make sure the cleanup code doesn't zap it! */
3667			req.priv_key = NULL;
3668		}
3669	}
3670	PHP_SSL_REQ_DISPOSE(&req);
3671}
3672/* }}} */
3673
3674/* {{{ proto bool openssl_pkey_export_to_file(mixed key, string outfilename [, string passphrase, array config_args)
3675   Gets an exportable representation of a key into a file */
3676PHP_FUNCTION(openssl_pkey_export_to_file)
3677{
3678	struct php_x509_request req;
3679	zval * zpkey, * args = NULL;
3680	char * passphrase = NULL;
3681	size_t passphrase_len = 0;
3682	char * filename = NULL;
3683	size_t filename_len = 0;
3684	zend_resource *key_resource = NULL;
3685	int pem_write = 0;
3686	EVP_PKEY * key;
3687	BIO * bio_out = NULL;
3688	const EVP_CIPHER * cipher;
3689
3690	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zp|s!a!", &zpkey, &filename, &filename_len, &passphrase, &passphrase_len, &args) == FAILURE) {
3691		return;
3692	}
3693	RETVAL_FALSE;
3694
3695	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase);
3696
3697	key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource);
3698
3699	if (key == NULL) {
3700		php_error_docref(NULL, E_WARNING, "cannot get key from parameter 1");
3701		RETURN_FALSE;
3702	}
3703
3704	if (php_openssl_open_base_dir_chk(filename)) {
3705		RETURN_FALSE;
3706	}
3707
3708	PHP_SSL_REQ_INIT(&req);
3709
3710	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3711		bio_out = BIO_new_file(filename, "w");
3712
3713		if (passphrase && req.priv_key_encrypt) {
3714			if (req.priv_key_encrypt_cipher) {
3715				cipher = req.priv_key_encrypt_cipher;
3716			} else {
3717				cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
3718			}
3719		} else {
3720			cipher = NULL;
3721		}
3722
3723		switch (EVP_PKEY_type(key->type)) {
3724#ifdef HAVE_EVP_PKEY_EC
3725			case EVP_PKEY_EC:
3726				pem_write = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get1_EC_KEY(key), cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
3727				break;
3728#endif
3729			default:
3730				pem_write = PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
3731				break;
3732		}
3733
3734		if (pem_write) {
3735			/* Success!
3736			 * If returning the output as a string, do so now */
3737			RETVAL_TRUE;
3738		}
3739	}
3740	PHP_SSL_REQ_DISPOSE(&req);
3741
3742	if (key_resource == NULL && key) {
3743		EVP_PKEY_free(key);
3744	}
3745	if (bio_out) {
3746		BIO_free(bio_out);
3747	}
3748}
3749/* }}} */
3750
3751/* {{{ proto bool openssl_pkey_export(mixed key, &mixed out [, string passphrase [, array config_args]])
3752   Gets an exportable representation of a key into a string or file */
3753PHP_FUNCTION(openssl_pkey_export)
3754{
3755	struct php_x509_request req;
3756	zval * zpkey, * args = NULL, *out;
3757	char * passphrase = NULL; size_t passphrase_len = 0;
3758	int pem_write = 0;
3759	zend_resource *key_resource = NULL;
3760	EVP_PKEY * key;
3761	BIO * bio_out = NULL;
3762	const EVP_CIPHER * cipher;
3763
3764	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz/|s!a!", &zpkey, &out, &passphrase, &passphrase_len, &args) == FAILURE) {
3765		return;
3766	}
3767	RETVAL_FALSE;
3768
3769	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(passphrase_len, passphrase);
3770
3771	key = php_openssl_evp_from_zval(zpkey, 0, passphrase, 0, &key_resource);
3772
3773	if (key == NULL) {
3774		php_error_docref(NULL, E_WARNING, "cannot get key from parameter 1");
3775		RETURN_FALSE;
3776	}
3777
3778	PHP_SSL_REQ_INIT(&req);
3779
3780	if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
3781		bio_out = BIO_new(BIO_s_mem());
3782
3783		if (passphrase && req.priv_key_encrypt) {
3784			if (req.priv_key_encrypt_cipher) {
3785				cipher = req.priv_key_encrypt_cipher;
3786			} else {
3787				cipher = (EVP_CIPHER *) EVP_des_ede3_cbc();
3788			}
3789		} else {
3790			cipher = NULL;
3791		}
3792
3793		switch (EVP_PKEY_type(key->type)) {
3794#ifdef HAVE_EVP_PKEY_EC
3795			case EVP_PKEY_EC:
3796				pem_write = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get1_EC_KEY(key), cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
3797				break;
3798#endif
3799			default:
3800				pem_write = PEM_write_bio_PrivateKey(bio_out, key, cipher, (unsigned char *)passphrase, (int)passphrase_len, NULL, NULL);
3801				break;
3802		}
3803
3804		if (pem_write) {
3805			/* Success!
3806			 * If returning the output as a string, do so now */
3807
3808			char * bio_mem_ptr;
3809			long bio_mem_len;
3810			RETVAL_TRUE;
3811
3812			bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
3813			zval_dtor(out);
3814			ZVAL_STRINGL(out, bio_mem_ptr, bio_mem_len);
3815		}
3816	}
3817	PHP_SSL_REQ_DISPOSE(&req);
3818
3819	if (key_resource == NULL && key) {
3820		EVP_PKEY_free(key);
3821	}
3822	if (bio_out) {
3823		BIO_free(bio_out);
3824	}
3825}
3826/* }}} */
3827
3828/* {{{ proto int openssl_pkey_get_public(mixed cert)
3829   Gets public key from X.509 certificate */
3830PHP_FUNCTION(openssl_pkey_get_public)
3831{
3832	zval *cert;
3833	EVP_PKEY *pkey;
3834	zend_resource *res;
3835
3836	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &cert) == FAILURE) {
3837		return;
3838	}
3839	pkey = php_openssl_evp_from_zval(cert, 1, NULL, 1, &res);
3840	if (pkey == NULL) {
3841		RETURN_FALSE;
3842	}
3843	ZVAL_RES(return_value, res);
3844	Z_ADDREF_P(return_value);
3845}
3846/* }}} */
3847
3848/* {{{ proto void openssl_pkey_free(int key)
3849   Frees a key */
3850PHP_FUNCTION(openssl_pkey_free)
3851{
3852	zval *key;
3853	EVP_PKEY *pkey;
3854
3855	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &key) == FAILURE) {
3856		return;
3857	}
3858	if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
3859		RETURN_FALSE;
3860	}
3861	zend_list_close(Z_RES_P(key));
3862}
3863/* }}} */
3864
3865/* {{{ proto int openssl_pkey_get_private(string key [, string passphrase])
3866   Gets private keys */
3867PHP_FUNCTION(openssl_pkey_get_private)
3868{
3869	zval *cert;
3870	EVP_PKEY *pkey;
3871	char * passphrase = "";
3872	size_t passphrase_len = sizeof("")-1;
3873	zend_resource *res;
3874
3875	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s", &cert, &passphrase, &passphrase_len) == FAILURE) {
3876		return;
3877	}
3878	pkey = php_openssl_evp_from_zval(cert, 0, passphrase, 1, &res);
3879
3880	if (pkey == NULL) {
3881		RETURN_FALSE;
3882	}
3883	ZVAL_RES(return_value, res);
3884	Z_ADDREF_P(return_value);
3885}
3886
3887/* }}} */
3888
3889/* {{{ proto resource openssl_pkey_get_details(resource key)
3890	returns an array with the key details (bits, pkey, type)*/
3891PHP_FUNCTION(openssl_pkey_get_details)
3892{
3893	zval *key;
3894	EVP_PKEY *pkey;
3895	BIO *out;
3896	unsigned int pbio_len;
3897	char *pbio;
3898	zend_long ktype;
3899
3900	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &key) == FAILURE) {
3901		return;
3902	}
3903	if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
3904		RETURN_FALSE;
3905	}
3906	out = BIO_new(BIO_s_mem());
3907	PEM_write_bio_PUBKEY(out, pkey);
3908	pbio_len = BIO_get_mem_data(out, &pbio);
3909
3910	array_init(return_value);
3911	add_assoc_long(return_value, "bits", EVP_PKEY_bits(pkey));
3912	add_assoc_stringl(return_value, "key", pbio, pbio_len);
3913	/*TODO: Use the real values once the openssl constants are used
3914	 * See the enum at the top of this file
3915	 */
3916	switch (EVP_PKEY_type(pkey->type)) {
3917		case EVP_PKEY_RSA:
3918		case EVP_PKEY_RSA2:
3919			ktype = OPENSSL_KEYTYPE_RSA;
3920
3921			if (pkey->pkey.rsa != NULL) {
3922				zval rsa;
3923
3924				array_init(&rsa);
3925				OPENSSL_PKEY_GET_BN(rsa, n);
3926				OPENSSL_PKEY_GET_BN(rsa, e);
3927				OPENSSL_PKEY_GET_BN(rsa, d);
3928				OPENSSL_PKEY_GET_BN(rsa, p);
3929				OPENSSL_PKEY_GET_BN(rsa, q);
3930				OPENSSL_PKEY_GET_BN(rsa, dmp1);
3931				OPENSSL_PKEY_GET_BN(rsa, dmq1);
3932				OPENSSL_PKEY_GET_BN(rsa, iqmp);
3933				add_assoc_zval(return_value, "rsa", &rsa);
3934			}
3935
3936			break;
3937		case EVP_PKEY_DSA:
3938		case EVP_PKEY_DSA2:
3939		case EVP_PKEY_DSA3:
3940		case EVP_PKEY_DSA4:
3941			ktype = OPENSSL_KEYTYPE_DSA;
3942
3943			if (pkey->pkey.dsa != NULL) {
3944				zval dsa;
3945
3946				array_init(&dsa);
3947				OPENSSL_PKEY_GET_BN(dsa, p);
3948				OPENSSL_PKEY_GET_BN(dsa, q);
3949				OPENSSL_PKEY_GET_BN(dsa, g);
3950				OPENSSL_PKEY_GET_BN(dsa, priv_key);
3951				OPENSSL_PKEY_GET_BN(dsa, pub_key);
3952				add_assoc_zval(return_value, "dsa", &dsa);
3953			}
3954			break;
3955		case EVP_PKEY_DH:
3956
3957			ktype = OPENSSL_KEYTYPE_DH;
3958
3959			if (pkey->pkey.dh != NULL) {
3960				zval dh;
3961
3962				array_init(&dh);
3963				OPENSSL_PKEY_GET_BN(dh, p);
3964				OPENSSL_PKEY_GET_BN(dh, g);
3965				OPENSSL_PKEY_GET_BN(dh, priv_key);
3966				OPENSSL_PKEY_GET_BN(dh, pub_key);
3967				add_assoc_zval(return_value, "dh", &dh);
3968			}
3969
3970			break;
3971#ifdef HAVE_EVP_PKEY_EC
3972		case EVP_PKEY_EC:
3973			ktype = OPENSSL_KEYTYPE_EC;
3974			if (pkey->pkey.ec != NULL) {
3975				zval ec;
3976				const EC_GROUP *ec_group;
3977				int nid;
3978				char *crv_sn;
3979				ASN1_OBJECT *obj;
3980				// openssl recommends a buffer length of 80
3981				char oir_buf[80];
3982
3983				ec_group = EC_KEY_get0_group(EVP_PKEY_get1_EC_KEY(pkey));
3984
3985				// Curve nid (numerical identifier) used for ASN1 mapping
3986				nid = EC_GROUP_get_curve_name(ec_group);
3987				if (nid == NID_undef) {
3988					break;
3989				}
3990				array_init(&ec);
3991
3992				// Short object name
3993				crv_sn = (char*) OBJ_nid2sn(nid);
3994				if (crv_sn != NULL) {
3995					add_assoc_string(&ec, "curve_name", crv_sn);
3996				}
3997
3998				obj = OBJ_nid2obj(nid);
3999				if (obj != NULL) {
4000					int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
4001					add_assoc_stringl(&ec, "curve_oid", (char*)oir_buf, oir_len);
4002					ASN1_OBJECT_free(obj);
4003				}
4004
4005				add_assoc_zval(return_value, "ec", &ec);
4006			}
4007			break;
4008#endif
4009		default:
4010			ktype = -1;
4011			break;
4012	}
4013	add_assoc_long(return_value, "type", ktype);
4014
4015	BIO_free(out);
4016}
4017/* }}} */
4018
4019/* }}} */
4020
4021#if OPENSSL_VERSION_NUMBER >= 0x10000000L
4022
4023/* {{{ proto string openssl_pbkdf2(string password, string salt, long key_length, long iterations [, string digest_method = "sha1"])
4024   Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
4025PHP_FUNCTION(openssl_pbkdf2)
4026{
4027	zend_long key_length = 0, iterations = 0;
4028	char *password;
4029	size_t password_len;
4030	char *salt;
4031	size_t salt_len;
4032	char *method;
4033	size_t method_len = 0;
4034	zend_string *out_buffer;
4035
4036	const EVP_MD *digest;
4037
4038	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssll|s",
4039				&password, &password_len,
4040				&salt, &salt_len,
4041				&key_length, &iterations,
4042				&method, &method_len) == FAILURE) {
4043		return;
4044	}
4045
4046	if (key_length <= 0) {
4047		RETURN_FALSE;
4048	}
4049
4050	if (method_len) {
4051		digest = EVP_get_digestbyname(method);
4052	} else {
4053		digest = EVP_sha1();
4054	}
4055
4056	if (!digest) {
4057		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm");
4058		RETURN_FALSE;
4059	}
4060
4061	PHP_OPENSSL_CHECK_LONG_TO_INT(key_length, key);
4062	PHP_OPENSSL_CHECK_LONG_TO_INT(iterations, iterations);
4063	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
4064	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(salt_len, salt);
4065
4066	out_buffer = zend_string_alloc(key_length, 0);
4067
4068	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) {
4069		ZSTR_VAL(out_buffer)[key_length] = 0;
4070		RETURN_NEW_STR(out_buffer);
4071	} else {
4072		zend_string_release(out_buffer);
4073		RETURN_FALSE;
4074	}
4075}
4076/* }}} */
4077
4078#endif
4079
4080/* {{{ PKCS7 S/MIME functions */
4081
4082/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
4083   Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */
4084PHP_FUNCTION(openssl_pkcs7_verify)
4085{
4086	X509_STORE * store = NULL;
4087	zval * cainfo = NULL;
4088	STACK_OF(X509) *signers= NULL;
4089	STACK_OF(X509) *others = NULL;
4090	PKCS7 * p7 = NULL;
4091	BIO * in = NULL, * datain = NULL, * dataout = NULL;
4092	zend_long flags = 0;
4093	char * filename;
4094	size_t filename_len;
4095	char * extracerts = NULL;
4096	size_t extracerts_len = 0;
4097	char * signersfilename = NULL;
4098	size_t signersfilename_len = 0;
4099	char * datafilename = NULL;
4100	size_t datafilename_len = 0;
4101
4102	RETVAL_LONG(-1);
4103
4104	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|papp", &filename, &filename_len,
4105				&flags, &signersfilename, &signersfilename_len, &cainfo,
4106				&extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) {
4107		return;
4108	}
4109
4110	if (extracerts) {
4111		others = load_all_certs_from_file(extracerts);
4112		if (others == NULL) {
4113			goto clean_exit;
4114		}
4115	}
4116
4117	flags = flags & ~PKCS7_DETACHED;
4118
4119	store = setup_verify(cainfo);
4120
4121	if (!store) {
4122		goto clean_exit;
4123	}
4124	if (php_openssl_open_base_dir_chk(filename)) {
4125		goto clean_exit;
4126	}
4127
4128	in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r");
4129	if (in == NULL) {
4130		goto clean_exit;
4131	}
4132	p7 = SMIME_read_PKCS7(in, &datain);
4133	if (p7 == NULL) {
4134#if DEBUG_SMIME
4135		zend_printf("SMIME_read_PKCS7 failed\n");
4136#endif
4137		goto clean_exit;
4138	}
4139
4140	if (datafilename) {
4141
4142		if (php_openssl_open_base_dir_chk(datafilename)) {
4143			goto clean_exit;
4144		}
4145
4146		dataout = BIO_new_file(datafilename, "w");
4147		if (dataout == NULL) {
4148			goto clean_exit;
4149		}
4150	}
4151#if DEBUG_SMIME
4152	zend_printf("Calling PKCS7 verify\n");
4153#endif
4154
4155	if (PKCS7_verify(p7, others, store, datain, dataout, (int)flags)) {
4156
4157		RETVAL_TRUE;
4158
4159		if (signersfilename) {
4160			BIO *certout;
4161
4162			if (php_openssl_open_base_dir_chk(signersfilename)) {
4163				goto clean_exit;
4164			}
4165
4166			certout = BIO_new_file(signersfilename, "w");
4167			if (certout) {
4168				int i;
4169				signers = PKCS7_get0_signers(p7, NULL, (int)flags);
4170
4171				for(i = 0; i < sk_X509_num(signers); i++) {
4172					PEM_write_bio_X509(certout, sk_X509_value(signers, i));
4173				}
4174				BIO_free(certout);
4175				sk_X509_free(signers);
4176			} else {
4177				php_error_docref(NULL, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
4178				RETVAL_LONG(-1);
4179			}
4180		}
4181		goto clean_exit;
4182	} else {
4183		RETVAL_FALSE;
4184	}
4185clean_exit:
4186	X509_STORE_free(store);
4187	BIO_free(datain);
4188	BIO_free(in);
4189	BIO_free(dataout);
4190	PKCS7_free(p7);
4191	sk_X509_free(others);
4192}
4193/* }}} */
4194
4195/* {{{ proto bool openssl_pkcs7_encrypt(string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]])
4196   Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile */
4197PHP_FUNCTION(openssl_pkcs7_encrypt)
4198{
4199	zval * zrecipcerts, * zheaders = NULL;
4200	STACK_OF(X509) * recipcerts = NULL;
4201	BIO * infile = NULL, * outfile = NULL;
4202	zend_long flags = 0;
4203	PKCS7 * p7 = NULL;
4204	zval * zcertval;
4205	X509 * cert;
4206	const EVP_CIPHER *cipher = NULL;
4207	zend_long cipherid = PHP_OPENSSL_CIPHER_DEFAULT;
4208	zend_string * strindex;
4209	char * infilename = NULL;
4210	size_t infilename_len;
4211	char * outfilename = NULL;
4212	size_t outfilename_len;
4213
4214	RETVAL_FALSE;
4215
4216	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppza!|ll", &infilename, &infilename_len,
4217				&outfilename, &outfilename_len, &zrecipcerts, &zheaders, &flags, &cipherid) == FAILURE)
4218		return;
4219
4220
4221	if (php_openssl_open_base_dir_chk(infilename) || php_openssl_open_base_dir_chk(outfilename)) {
4222		return;
4223	}
4224
4225	infile = BIO_new_file(infilename, "r");
4226	if (infile == NULL) {
4227		goto clean_exit;
4228	}
4229
4230	outfile = BIO_new_file(outfilename, "w");
4231	if (outfile == NULL) {
4232		goto clean_exit;
4233	}
4234
4235	recipcerts = sk_X509_new_null();
4236
4237	/* get certs */
4238	if (Z_TYPE_P(zrecipcerts) == IS_ARRAY) {
4239		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zrecipcerts), zcertval) {
4240			zend_resource *certresource;
4241
4242			cert = php_openssl_x509_from_zval(zcertval, 0, &certresource);
4243			if (cert == NULL) {
4244				goto clean_exit;
4245			}
4246
4247			if (certresource != NULL) {
4248				/* we shouldn't free this particular cert, as it is a resource.
4249					make a copy and push that on the stack instead */
4250				cert = X509_dup(cert);
4251				if (cert == NULL) {
4252					goto clean_exit;
4253				}
4254			}
4255			sk_X509_push(recipcerts, cert);
4256		} ZEND_HASH_FOREACH_END();
4257	} else {
4258		/* a single certificate */
4259		zend_resource *certresource;
4260
4261		cert = php_openssl_x509_from_zval(zrecipcerts, 0, &certresource);
4262		if (cert == NULL) {
4263			goto clean_exit;
4264		}
4265
4266		if (certresource != NULL) {
4267			/* we shouldn't free this particular cert, as it is a resource.
4268				make a copy and push that on the stack instead */
4269			cert = X509_dup(cert);
4270			if (cert == NULL) {
4271				goto clean_exit;
4272			}
4273		}
4274		sk_X509_push(recipcerts, cert);
4275	}
4276
4277	/* sanity check the cipher */
4278	cipher = php_openssl_get_evp_cipher_from_algo(cipherid);
4279	if (cipher == NULL) {
4280		/* shouldn't happen */
4281		php_error_docref(NULL, E_WARNING, "Failed to get cipher");
4282		goto clean_exit;
4283	}
4284
4285	p7 = PKCS7_encrypt(recipcerts, infile, (EVP_CIPHER*)cipher, (int)flags);
4286
4287	if (p7 == NULL) {
4288		goto clean_exit;
4289	}
4290
4291	/* tack on extra headers */
4292	if (zheaders) {
4293		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, zcertval) {
4294			convert_to_string_ex(zcertval);
4295
4296			if (strindex) {
4297				BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), Z_STRVAL_P(zcertval));
4298			} else {
4299				BIO_printf(outfile, "%s\n", Z_STRVAL_P(zcertval));
4300			}
4301		} ZEND_HASH_FOREACH_END();
4302	}
4303
4304	(void)BIO_reset(infile);
4305
4306	/* write the encrypted data */
4307	SMIME_write_PKCS7(outfile, p7, infile, (int)flags);
4308
4309	RETVAL_TRUE;
4310
4311clean_exit:
4312	PKCS7_free(p7);
4313	BIO_free(infile);
4314	BIO_free(outfile);
4315	if (recipcerts) {
4316		sk_X509_pop_free(recipcerts, X509_free);
4317	}
4318}
4319/* }}} */
4320
4321/* {{{ proto bool openssl_pkcs7_sign(string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]])
4322   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 */
4323
4324PHP_FUNCTION(openssl_pkcs7_sign)
4325{
4326	zval * zcert, * zprivkey, * zheaders;
4327	zval * hval;
4328	X509 * cert = NULL;
4329	EVP_PKEY * privkey = NULL;
4330	zend_long flags = PKCS7_DETACHED;
4331	PKCS7 * p7 = NULL;
4332	BIO * infile = NULL, * outfile = NULL;
4333	STACK_OF(X509) *others = NULL;
4334	zend_resource *certresource = NULL, *keyresource = NULL;
4335	zend_string * strindex;
4336	char * infilename;
4337	size_t infilename_len;
4338	char * outfilename;
4339	size_t outfilename_len;
4340	char * extracertsfilename = NULL;
4341	size_t extracertsfilename_len;
4342
4343	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppzza!|lp!",
4344				&infilename, &infilename_len, &outfilename, &outfilename_len,
4345				&zcert, &zprivkey, &zheaders, &flags, &extracertsfilename,
4346				&extracertsfilename_len) == FAILURE) {
4347		return;
4348	}
4349
4350	RETVAL_FALSE;
4351
4352	if (extracertsfilename) {
4353		others = load_all_certs_from_file(extracertsfilename);
4354		if (others == NULL) {
4355			goto clean_exit;
4356		}
4357	}
4358
4359	privkey = php_openssl_evp_from_zval(zprivkey, 0, "", 0, &keyresource);
4360	if (privkey == NULL) {
4361		php_error_docref(NULL, E_WARNING, "error getting private key");
4362		goto clean_exit;
4363	}
4364
4365	cert = php_openssl_x509_from_zval(zcert, 0, &certresource);
4366	if (cert == NULL) {
4367		php_error_docref(NULL, E_WARNING, "error getting cert");
4368		goto clean_exit;
4369	}
4370
4371	if (php_openssl_open_base_dir_chk(infilename) || php_openssl_open_base_dir_chk(outfilename)) {
4372		goto clean_exit;
4373	}
4374
4375	infile = BIO_new_file(infilename, "r");
4376	if (infile == NULL) {
4377		php_error_docref(NULL, E_WARNING, "error opening input file %s!", infilename);
4378		goto clean_exit;
4379	}
4380
4381	outfile = BIO_new_file(outfilename, "w");
4382	if (outfile == NULL) {
4383		php_error_docref(NULL, E_WARNING, "error opening output file %s!", outfilename);
4384		goto clean_exit;
4385	}
4386
4387	p7 = PKCS7_sign(cert, privkey, others, infile, (int)flags);
4388	if (p7 == NULL) {
4389		php_error_docref(NULL, E_WARNING, "error creating PKCS7 structure!");
4390		goto clean_exit;
4391	}
4392
4393	(void)BIO_reset(infile);
4394
4395	/* tack on extra headers */
4396	if (zheaders) {
4397		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) {
4398			convert_to_string_ex(hval);
4399
4400			if (strindex) {
4401				BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), Z_STRVAL_P(hval));
4402			} else {
4403				BIO_printf(outfile, "%s\n", Z_STRVAL_P(hval));
4404			}
4405		} ZEND_HASH_FOREACH_END();
4406	}
4407	/* write the signed data */
4408	SMIME_write_PKCS7(outfile, p7, infile, (int)flags);
4409
4410	RETVAL_TRUE;
4411
4412clean_exit:
4413	PKCS7_free(p7);
4414	BIO_free(infile);
4415	BIO_free(outfile);
4416	if (others) {
4417		sk_X509_pop_free(others, X509_free);
4418	}
4419	if (privkey && keyresource == NULL) {
4420		EVP_PKEY_free(privkey);
4421	}
4422	if (cert && certresource == NULL) {
4423		X509_free(cert);
4424	}
4425}
4426/* }}} */
4427
4428/* {{{ proto bool openssl_pkcs7_decrypt(string infilename, string outfilename, mixed recipcert [, mixed recipkey])
4429   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 */
4430
4431PHP_FUNCTION(openssl_pkcs7_decrypt)
4432{
4433	zval * recipcert, * recipkey = NULL;
4434	X509 * cert = NULL;
4435	EVP_PKEY * key = NULL;
4436	zend_resource *certresval, *keyresval;
4437	BIO * in = NULL, * out = NULL, * datain = NULL;
4438	PKCS7 * p7 = NULL;
4439	char * infilename;
4440	size_t infilename_len;
4441	char * outfilename;
4442	size_t outfilename_len;
4443
4444	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppz|z", &infilename, &infilename_len,
4445				&outfilename, &outfilename_len, &recipcert, &recipkey) == FAILURE) {
4446		return;
4447	}
4448
4449	RETVAL_FALSE;
4450
4451	cert = php_openssl_x509_from_zval(recipcert, 0, &certresval);
4452	if (cert == NULL) {
4453		php_error_docref(NULL, E_WARNING, "unable to coerce parameter 3 to x509 cert");
4454		goto clean_exit;
4455	}
4456
4457	key = php_openssl_evp_from_zval(recipkey ? recipkey : recipcert, 0, "", 0, &keyresval);
4458	if (key == NULL) {
4459		php_error_docref(NULL, E_WARNING, "unable to get private key");
4460		goto clean_exit;
4461	}
4462
4463	if (php_openssl_open_base_dir_chk(infilename) || php_openssl_open_base_dir_chk(outfilename)) {
4464		goto clean_exit;
4465	}
4466
4467	in = BIO_new_file(infilename, "r");
4468	if (in == NULL) {
4469		goto clean_exit;
4470	}
4471	out = BIO_new_file(outfilename, "w");
4472	if (out == NULL) {
4473		goto clean_exit;
4474	}
4475
4476	p7 = SMIME_read_PKCS7(in, &datain);
4477
4478	if (p7 == NULL) {
4479		goto clean_exit;
4480	}
4481	if (PKCS7_decrypt(p7, key, cert, out, PKCS7_DETACHED)) {
4482		RETVAL_TRUE;
4483	}
4484clean_exit:
4485	PKCS7_free(p7);
4486	BIO_free(datain);
4487	BIO_free(in);
4488	BIO_free(out);
4489	if (cert && certresval == NULL) {
4490		X509_free(cert);
4491	}
4492	if (key && keyresval == NULL) {
4493		EVP_PKEY_free(key);
4494	}
4495}
4496/* }}} */
4497
4498/* }}} */
4499
4500/* {{{ proto bool openssl_private_encrypt(string data, string &crypted, mixed key [, int padding])
4501   Encrypts data with private key */
4502PHP_FUNCTION(openssl_private_encrypt)
4503{
4504	zval *key, *crypted;
4505	EVP_PKEY *pkey;
4506	int cryptedlen;
4507	zend_string *cryptedbuf = NULL;
4508	int successful = 0;
4509	zend_resource *keyresource = NULL;
4510	char * data;
4511	size_t data_len;
4512	zend_long padding = RSA_PKCS1_PADDING;
4513
4514	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
4515		return;
4516	}
4517	RETVAL_FALSE;
4518
4519	pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource);
4520
4521	if (pkey == NULL) {
4522		php_error_docref(NULL, E_WARNING, "key param is not a valid private key");
4523		RETURN_FALSE;
4524	}
4525
4526	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
4527
4528	cryptedlen = EVP_PKEY_size(pkey);
4529	cryptedbuf = zend_string_alloc(cryptedlen, 0);
4530
4531	switch (pkey->type) {
4532		case EVP_PKEY_RSA:
4533		case EVP_PKEY_RSA2:
4534			successful = (RSA_private_encrypt((int)data_len,
4535						(unsigned char *)data,
4536						(unsigned char *)ZSTR_VAL(cryptedbuf),
4537						pkey->pkey.rsa,
4538						(int)padding) == cryptedlen);
4539			break;
4540		default:
4541			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
4542	}
4543
4544	if (successful) {
4545		zval_dtor(crypted);
4546		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
4547		ZVAL_NEW_STR(crypted, cryptedbuf);
4548		cryptedbuf = NULL;
4549		RETVAL_TRUE;
4550	}
4551	if (cryptedbuf) {
4552		zend_string_release(cryptedbuf);
4553	}
4554	if (keyresource == NULL) {
4555		EVP_PKEY_free(pkey);
4556	}
4557}
4558/* }}} */
4559
4560/* {{{ proto bool openssl_private_decrypt(string data, string &decrypted, mixed key [, int padding])
4561   Decrypts data with private key */
4562PHP_FUNCTION(openssl_private_decrypt)
4563{
4564	zval *key, *crypted;
4565	EVP_PKEY *pkey;
4566	int cryptedlen;
4567	zend_string *cryptedbuf = NULL;
4568	unsigned char *crypttemp;
4569	int successful = 0;
4570	zend_long padding = RSA_PKCS1_PADDING;
4571	zend_resource *keyresource = NULL;
4572	char * data;
4573	size_t data_len;
4574
4575	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
4576		return;
4577	}
4578	RETVAL_FALSE;
4579
4580	pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource);
4581	if (pkey == NULL) {
4582		php_error_docref(NULL, E_WARNING, "key parameter is not a valid private key");
4583		RETURN_FALSE;
4584	}
4585
4586	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
4587
4588	cryptedlen = EVP_PKEY_size(pkey);
4589	crypttemp = emalloc(cryptedlen + 1);
4590
4591	switch (pkey->type) {
4592		case EVP_PKEY_RSA:
4593		case EVP_PKEY_RSA2:
4594			cryptedlen = RSA_private_decrypt((int)data_len,
4595					(unsigned char *)data,
4596					crypttemp,
4597					pkey->pkey.rsa,
4598					(int)padding);
4599			if (cryptedlen != -1) {
4600				cryptedbuf = zend_string_alloc(cryptedlen, 0);
4601				memcpy(ZSTR_VAL(cryptedbuf), crypttemp, cryptedlen);
4602				successful = 1;
4603			}
4604			break;
4605		default:
4606			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
4607	}
4608
4609	efree(crypttemp);
4610
4611	if (successful) {
4612		zval_dtor(crypted);
4613		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
4614		ZVAL_NEW_STR(crypted, cryptedbuf);
4615		cryptedbuf = NULL;
4616		RETVAL_TRUE;
4617	}
4618
4619	if (keyresource == NULL) {
4620		EVP_PKEY_free(pkey);
4621	}
4622	if (cryptedbuf) {
4623		zend_string_release(cryptedbuf);
4624	}
4625}
4626/* }}} */
4627
4628/* {{{ proto bool openssl_public_encrypt(string data, string &crypted, mixed key [, int padding])
4629   Encrypts data with public key */
4630PHP_FUNCTION(openssl_public_encrypt)
4631{
4632	zval *key, *crypted;
4633	EVP_PKEY *pkey;
4634	int cryptedlen;
4635	zend_string *cryptedbuf;
4636	int successful = 0;
4637	zend_resource *keyresource = NULL;
4638	zend_long padding = RSA_PKCS1_PADDING;
4639	char * data;
4640	size_t data_len;
4641
4642	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z|l", &data, &data_len, &crypted, &key, &padding) == FAILURE)
4643		return;
4644	RETVAL_FALSE;
4645
4646	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource);
4647	if (pkey == NULL) {
4648		php_error_docref(NULL, E_WARNING, "key parameter is not a valid public key");
4649		RETURN_FALSE;
4650	}
4651
4652	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
4653
4654	cryptedlen = EVP_PKEY_size(pkey);
4655	cryptedbuf = zend_string_alloc(cryptedlen, 0);
4656
4657	switch (pkey->type) {
4658		case EVP_PKEY_RSA:
4659		case EVP_PKEY_RSA2:
4660			successful = (RSA_public_encrypt((int)data_len,
4661						(unsigned char *)data,
4662						(unsigned char *)ZSTR_VAL(cryptedbuf),
4663						pkey->pkey.rsa,
4664						(int)padding) == cryptedlen);
4665			break;
4666		default:
4667			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
4668
4669	}
4670
4671	if (successful) {
4672		zval_dtor(crypted);
4673		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
4674		ZVAL_NEW_STR(crypted, cryptedbuf);
4675		cryptedbuf = NULL;
4676		RETVAL_TRUE;
4677	}
4678	if (keyresource == NULL) {
4679		EVP_PKEY_free(pkey);
4680	}
4681	if (cryptedbuf) {
4682		zend_string_release(cryptedbuf);
4683	}
4684}
4685/* }}} */
4686
4687/* {{{ proto bool openssl_public_decrypt(string data, string &crypted, resource key [, int padding])
4688   Decrypts data with public key */
4689PHP_FUNCTION(openssl_public_decrypt)
4690{
4691	zval *key, *crypted;
4692	EVP_PKEY *pkey;
4693	int cryptedlen;
4694	zend_string *cryptedbuf = NULL;
4695	unsigned char *crypttemp;
4696	int successful = 0;
4697	zend_resource *keyresource = NULL;
4698	zend_long padding = RSA_PKCS1_PADDING;
4699	char * data;
4700	size_t data_len;
4701
4702	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
4703		return;
4704	}
4705	RETVAL_FALSE;
4706
4707	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource);
4708	if (pkey == NULL) {
4709		php_error_docref(NULL, E_WARNING, "key parameter is not a valid public key");
4710		RETURN_FALSE;
4711	}
4712
4713	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
4714
4715	cryptedlen = EVP_PKEY_size(pkey);
4716	crypttemp = emalloc(cryptedlen + 1);
4717
4718	switch (pkey->type) {
4719		case EVP_PKEY_RSA:
4720		case EVP_PKEY_RSA2:
4721			cryptedlen = RSA_public_decrypt((int)data_len,
4722					(unsigned char *)data,
4723					crypttemp,
4724					pkey->pkey.rsa,
4725					(int)padding);
4726			if (cryptedlen != -1) {
4727				cryptedbuf = zend_string_alloc(cryptedlen, 0);
4728				memcpy(ZSTR_VAL(cryptedbuf), crypttemp, cryptedlen);
4729				successful = 1;
4730			}
4731			break;
4732
4733		default:
4734			php_error_docref(NULL, E_WARNING, "key type not supported in this PHP build!");
4735
4736	}
4737
4738	efree(crypttemp);
4739
4740	if (successful) {
4741		zval_dtor(crypted);
4742		ZSTR_VAL(cryptedbuf)[cryptedlen] = '\0';
4743		ZVAL_NEW_STR(crypted, cryptedbuf);
4744		cryptedbuf = NULL;
4745		RETVAL_TRUE;
4746	}
4747
4748	if (cryptedbuf) {
4749		zend_string_release(cryptedbuf);
4750	}
4751	if (keyresource == NULL) {
4752		EVP_PKEY_free(pkey);
4753	}
4754}
4755/* }}} */
4756
4757/* {{{ proto mixed openssl_error_string(void)
4758   Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages */
4759PHP_FUNCTION(openssl_error_string)
4760{
4761	char buf[512];
4762	unsigned long val;
4763
4764	if (zend_parse_parameters_none() == FAILURE) {
4765		return;
4766	}
4767
4768	val = ERR_get_error();
4769	if (val) {
4770		RETURN_STRING(ERR_error_string(val, buf));
4771	} else {
4772		RETURN_FALSE;
4773	}
4774}
4775/* }}} */
4776
4777/* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, mixed method])
4778   Signs data */
4779PHP_FUNCTION(openssl_sign)
4780{
4781	zval *key, *signature;
4782	EVP_PKEY *pkey;
4783	unsigned int siglen;
4784	zend_string *sigbuf;
4785	zend_resource *keyresource = NULL;
4786	char * data;
4787	size_t data_len;
4788	EVP_MD_CTX md_ctx;
4789	zval *method = NULL;
4790	zend_long signature_algo = OPENSSL_ALGO_SHA1;
4791	const EVP_MD *mdtype;
4792
4793	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z|z", &data, &data_len, &signature, &key, &method) == FAILURE) {
4794		return;
4795	}
4796	pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource);
4797	if (pkey == NULL) {
4798		php_error_docref(NULL, E_WARNING, "supplied key param cannot be coerced into a private key");
4799		RETURN_FALSE;
4800	}
4801
4802	if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
4803		if (method != NULL) {
4804			signature_algo = Z_LVAL_P(method);
4805		}
4806		mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
4807	} else if (Z_TYPE_P(method) == IS_STRING) {
4808		mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
4809	} else {
4810		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
4811		RETURN_FALSE;
4812	}
4813	if (!mdtype) {
4814		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
4815		RETURN_FALSE;
4816	}
4817
4818	siglen = EVP_PKEY_size(pkey);
4819	sigbuf = zend_string_alloc(siglen, 0);
4820
4821	EVP_SignInit(&md_ctx, mdtype);
4822	EVP_SignUpdate(&md_ctx, data, data_len);
4823	if (EVP_SignFinal (&md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, pkey)) {
4824		zval_dtor(signature);
4825		ZSTR_VAL(sigbuf)[siglen] = '\0';
4826		ZSTR_LEN(sigbuf) = siglen;
4827		ZVAL_NEW_STR(signature, sigbuf);
4828		RETVAL_TRUE;
4829	} else {
4830		efree(sigbuf);
4831		RETVAL_FALSE;
4832	}
4833	EVP_MD_CTX_cleanup(&md_ctx);
4834	if (keyresource == NULL) {
4835		EVP_PKEY_free(pkey);
4836	}
4837}
4838/* }}} */
4839
4840/* {{{ proto int openssl_verify(string data, string signature, mixed key[, mixed method])
4841   Verifys data */
4842PHP_FUNCTION(openssl_verify)
4843{
4844	zval *key;
4845	EVP_PKEY *pkey;
4846	int err;
4847	EVP_MD_CTX md_ctx;
4848	const EVP_MD *mdtype;
4849	zend_resource *keyresource = NULL;
4850	char * data;
4851	size_t data_len;
4852	char * signature;
4853	size_t signature_len;
4854	zval *method = NULL;
4855	zend_long signature_algo = OPENSSL_ALGO_SHA1;
4856
4857	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz|z", &data, &data_len, &signature, &signature_len, &key, &method) == FAILURE) {
4858		return;
4859	}
4860
4861	PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature);
4862
4863	if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
4864		if (method != NULL) {
4865			signature_algo = Z_LVAL_P(method);
4866		}
4867		mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
4868	} else if (Z_TYPE_P(method) == IS_STRING) {
4869		mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
4870	} else {
4871		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
4872		RETURN_FALSE;
4873	}
4874	if (!mdtype) {
4875		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
4876		RETURN_FALSE;
4877	}
4878
4879	pkey = php_openssl_evp_from_zval(key, 1, NULL, 0, &keyresource);
4880	if (pkey == NULL) {
4881		php_error_docref(NULL, E_WARNING, "supplied key param cannot be coerced into a public key");
4882		RETURN_FALSE;
4883	}
4884
4885	EVP_VerifyInit (&md_ctx, mdtype);
4886	EVP_VerifyUpdate (&md_ctx, data, data_len);
4887	err = EVP_VerifyFinal(&md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey);
4888	EVP_MD_CTX_cleanup(&md_ctx);
4889
4890	if (keyresource == NULL) {
4891		EVP_PKEY_free(pkey);
4892	}
4893	RETURN_LONG(err);
4894}
4895/* }}} */
4896
4897/* {{{ proto int openssl_seal(string data, &string sealdata, &array ekeys, array pubkeys)
4898   Seals data */
4899PHP_FUNCTION(openssl_seal)
4900{
4901	zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL;
4902	HashTable *pubkeysht;
4903	EVP_PKEY **pkeys;
4904	zend_resource ** key_resources;	/* so we know what to cleanup */
4905	int i, len1, len2, *eksl, nkeys, iv_len;
4906	unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks;
4907	char * data;
4908	size_t data_len;
4909	char *method =NULL;
4910	size_t method_len = 0;
4911	const EVP_CIPHER *cipher;
4912	EVP_CIPHER_CTX ctx;
4913
4914	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/z/a/|sz/", &data, &data_len,
4915				&sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) {
4916		return;
4917	}
4918	pubkeysht = Z_ARRVAL_P(pubkeys);
4919	nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
4920	if (!nkeys) {
4921		php_error_docref(NULL, E_WARNING, "Fourth argument to openssl_seal() must be a non-empty array");
4922		RETURN_FALSE;
4923	}
4924
4925	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
4926
4927	if (method) {
4928		cipher = EVP_get_cipherbyname(method);
4929		if (!cipher) {
4930			php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
4931			RETURN_FALSE;
4932		}
4933	} else {
4934		cipher = EVP_rc4();
4935	}
4936
4937	iv_len = EVP_CIPHER_iv_length(cipher);
4938	if (!iv && iv_len > 0) {
4939		php_error_docref(NULL, E_WARNING,
4940				"Cipher algorithm requires an IV to be supplied as a sixth parameter");
4941		RETURN_FALSE;
4942	}
4943
4944	pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
4945	eksl = safe_emalloc(nkeys, sizeof(*eksl), 0);
4946	eks = safe_emalloc(nkeys, sizeof(*eks), 0);
4947	memset(eks, 0, sizeof(*eks) * nkeys);
4948	key_resources = safe_emalloc(nkeys, sizeof(zend_resource*), 0);
4949	memset(key_resources, 0, sizeof(zend_resource*) * nkeys);
4950	memset(pkeys, 0, sizeof(*pkeys) * nkeys);
4951
4952	/* get the public keys we are using to seal this data */
4953	i = 0;
4954	ZEND_HASH_FOREACH_VAL(pubkeysht, pubkey) {
4955		pkeys[i] = php_openssl_evp_from_zval(pubkey, 1, NULL, 0, &key_resources[i]);
4956		if (pkeys[i] == NULL) {
4957			php_error_docref(NULL, E_WARNING, "not a public key (%dth member of pubkeys)", i+1);
4958			RETVAL_FALSE;
4959			goto clean_exit;
4960		}
4961		eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
4962		i++;
4963	} ZEND_HASH_FOREACH_END();
4964
4965	if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) {
4966		RETVAL_FALSE;
4967		EVP_CIPHER_CTX_cleanup(&ctx);
4968		goto clean_exit;
4969	}
4970
4971	/* allocate one byte extra to make room for \0 */
4972	buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx));
4973	EVP_CIPHER_CTX_cleanup(&ctx);
4974
4975	if (!EVP_SealInit(&ctx, cipher, eks, eksl, &iv_buf[0], pkeys, nkeys) ||
4976			!EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
4977			!EVP_SealFinal(&ctx, buf + len1, &len2)) {
4978		RETVAL_FALSE;
4979		efree(buf);
4980		EVP_CIPHER_CTX_cleanup(&ctx);
4981		goto clean_exit;
4982	}
4983
4984	if (len1 + len2 > 0) {
4985		zval_dtor(sealdata);
4986		buf[len1 + len2] = '\0';
4987		ZVAL_NEW_STR(sealdata, zend_string_init((char*)buf, len1 + len2, 0));
4988		efree(buf);
4989
4990		zval_dtor(ekeys);
4991		array_init(ekeys);
4992		for (i=0; i<nkeys; i++) {
4993			eks[i][eksl[i]] = '\0';
4994			add_next_index_stringl(ekeys, (const char*)eks[i], eksl[i]);
4995			efree(eks[i]);
4996			eks[i] = NULL;
4997		}
4998
4999		if (iv) {
5000			zval_dtor(iv);
5001			iv_buf[iv_len] = '\0';
5002			ZVAL_NEW_STR(iv, zend_string_init((char*)iv_buf, iv_len, 0));
5003		}
5004	} else {
5005		efree(buf);
5006	}
5007	RETVAL_LONG(len1 + len2);
5008	EVP_CIPHER_CTX_cleanup(&ctx);
5009
5010clean_exit:
5011	for (i=0; i<nkeys; i++) {
5012		if (key_resources[i] == NULL && pkeys[i] != NULL) {
5013			EVP_PKEY_free(pkeys[i]);
5014		}
5015		if (eks[i]) {
5016			efree(eks[i]);
5017		}
5018	}
5019	efree(eks);
5020	efree(eksl);
5021	efree(pkeys);
5022	efree(key_resources);
5023}
5024/* }}} */
5025
5026/* {{{ proto bool openssl_open(string data, &string opendata, string ekey, mixed privkey)
5027   Opens data */
5028PHP_FUNCTION(openssl_open)
5029{
5030	zval *privkey, *opendata;
5031	EVP_PKEY *pkey;
5032	int len1, len2, cipher_iv_len;
5033	unsigned char *buf, *iv_buf;
5034	zend_resource *keyresource = NULL;
5035	EVP_CIPHER_CTX ctx;
5036	char * data;
5037	size_t data_len;
5038	char * ekey;
5039	size_t ekey_len;
5040	char *method = NULL, *iv = NULL;
5041	size_t method_len = 0, iv_len = 0;
5042	const EVP_CIPHER *cipher;
5043
5044	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/sz|ss", &data, &data_len, &opendata,
5045				&ekey, &ekey_len, &privkey, &method, &method_len, &iv, &iv_len) == FAILURE) {
5046		return;
5047	}
5048
5049	pkey = php_openssl_evp_from_zval(privkey, 0, "", 0, &keyresource);
5050	if (pkey == NULL) {
5051		php_error_docref(NULL, E_WARNING, "unable to coerce parameter 4 into a private key");
5052		RETURN_FALSE;
5053	}
5054
5055	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(ekey_len, ekey);
5056	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
5057
5058	if (method) {
5059		cipher = EVP_get_cipherbyname(method);
5060		if (!cipher) {
5061			php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
5062			RETURN_FALSE;
5063		}
5064	} else {
5065		cipher = EVP_rc4();
5066	}
5067
5068	cipher_iv_len = EVP_CIPHER_iv_length(cipher);
5069	if (cipher_iv_len > 0) {
5070		if (!iv) {
5071			php_error_docref(NULL, E_WARNING,
5072					"Cipher algorithm requires an IV to be supplied as a sixth parameter");
5073			RETURN_FALSE;
5074		}
5075		if (cipher_iv_len != iv_len) {
5076			php_error_docref(NULL, E_WARNING, "IV length is invalid");
5077			RETURN_FALSE;
5078		}
5079		iv_buf = (unsigned char *)iv;
5080	} else {
5081		iv_buf = NULL;
5082	}
5083
5084	buf = emalloc(data_len + 1);
5085
5086	if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) &&
5087			EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, (int)data_len)) {
5088		if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) {
5089			efree(buf);
5090			RETVAL_FALSE;
5091		} else {
5092			zval_dtor(opendata);
5093			buf[len1 + len2] = '\0';
5094			ZVAL_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
5095			efree(buf);
5096			RETVAL_TRUE;
5097		}
5098	} else {
5099		efree(buf);
5100		RETVAL_FALSE;
5101	}
5102	if (keyresource == NULL) {
5103		EVP_PKEY_free(pkey);
5104	}
5105	EVP_CIPHER_CTX_cleanup(&ctx);
5106}
5107/* }}} */
5108
5109static void openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */
5110{
5111	add_next_index_string((zval*)arg, (char*)name->name);
5112}
5113/* }}} */
5114
5115static void openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */
5116{
5117	if (name->alias == 0) {
5118		add_next_index_string((zval*)arg, (char*)name->name);
5119	}
5120}
5121/* }}} */
5122
5123/* {{{ proto array openssl_get_md_methods([bool aliases = false])
5124   Return array of available digest methods */
5125PHP_FUNCTION(openssl_get_md_methods)
5126{
5127	zend_bool aliases = 0;
5128
5129	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
5130		return;
5131	}
5132	array_init(return_value);
5133	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
5134		aliases ? openssl_add_method_or_alias: openssl_add_method,
5135		return_value);
5136}
5137/* }}} */
5138
5139/* {{{ proto array openssl_get_cipher_methods([bool aliases = false])
5140   Return array of available cipher methods */
5141PHP_FUNCTION(openssl_get_cipher_methods)
5142{
5143	zend_bool aliases = 0;
5144
5145	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &aliases) == FAILURE) {
5146		return;
5147	}
5148	array_init(return_value);
5149	OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
5150		aliases ? openssl_add_method_or_alias: openssl_add_method,
5151		return_value);
5152}
5153/* }}} */
5154
5155/* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false])
5156   Computes digest hash value for given data using given method, returns raw or binhex encoded string */
5157PHP_FUNCTION(openssl_digest)
5158{
5159	zend_bool raw_output = 0;
5160	char *data, *method;
5161	size_t data_len, method_len;
5162	const EVP_MD *mdtype;
5163	EVP_MD_CTX md_ctx;
5164	unsigned int siglen;
5165	zend_string *sigbuf;
5166
5167	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) {
5168		return;
5169	}
5170	mdtype = EVP_get_digestbyname(method);
5171	if (!mdtype) {
5172		php_error_docref(NULL, E_WARNING, "Unknown signature algorithm");
5173		RETURN_FALSE;
5174	}
5175
5176	siglen = EVP_MD_size(mdtype);
5177	sigbuf = zend_string_alloc(siglen, 0);
5178
5179	EVP_DigestInit(&md_ctx, mdtype);
5180	EVP_DigestUpdate(&md_ctx, (unsigned char *)data, data_len);
5181	if (EVP_DigestFinal (&md_ctx, (unsigned char *)ZSTR_VAL(sigbuf), &siglen)) {
5182		if (raw_output) {
5183			ZSTR_VAL(sigbuf)[siglen] = '\0';
5184			ZSTR_LEN(sigbuf) = siglen;
5185			RETVAL_STR(sigbuf);
5186		} else {
5187			int digest_str_len = siglen * 2;
5188			zend_string *digest_str = zend_string_alloc(digest_str_len, 0);
5189
5190			make_digest_ex(ZSTR_VAL(digest_str), (unsigned char*)ZSTR_VAL(sigbuf), siglen);
5191			ZSTR_VAL(digest_str)[digest_str_len] = '\0';
5192			zend_string_release(sigbuf);
5193			RETVAL_STR(digest_str);
5194		}
5195	} else {
5196		zend_string_release(sigbuf);
5197		RETVAL_FALSE;
5198	}
5199}
5200/* }}} */
5201
5202static zend_bool php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_required_len)
5203{
5204	char *iv_new;
5205
5206	/* Best case scenario, user behaved */
5207	if (*piv_len == iv_required_len) {
5208		return 0;
5209	}
5210
5211	iv_new = ecalloc(1, iv_required_len + 1);
5212
5213	if (*piv_len == 0) {
5214		/* BC behavior */
5215		*piv_len = iv_required_len;
5216		*piv = iv_new;
5217		return 1;
5218	}
5219
5220	if (*piv_len < iv_required_len) {
5221		php_error_docref(NULL, E_WARNING, "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0", *piv_len, iv_required_len);
5222		memcpy(iv_new, *piv, *piv_len);
5223		*piv_len = iv_required_len;
5224		*piv = iv_new;
5225		return 1;
5226	}
5227
5228	php_error_docref(NULL, E_WARNING, "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating", *piv_len, iv_required_len);
5229	memcpy(iv_new, *piv, iv_required_len);
5230	*piv_len = iv_required_len;
5231	*piv = iv_new;
5232	return 1;
5233
5234}
5235
5236/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv='']])
5237   Encrypts given data with given method and key, returns raw or base64 encoded string */
5238PHP_FUNCTION(openssl_encrypt)
5239{
5240	zend_long options = 0;
5241	char *data, *method, *password, *iv = "";
5242	size_t data_len, method_len, password_len, iv_len = 0, max_iv_len;
5243	const EVP_CIPHER *cipher_type;
5244	EVP_CIPHER_CTX cipher_ctx;
5245	int i=0, outlen, keylen;
5246	zend_string *outbuf;
5247	unsigned char *key;
5248	zend_bool free_iv;
5249
5250	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) {
5251		return;
5252	}
5253	cipher_type = EVP_get_cipherbyname(method);
5254	if (!cipher_type) {
5255		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
5256		RETURN_FALSE;
5257	}
5258
5259	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
5260
5261	keylen = EVP_CIPHER_key_length(cipher_type);
5262	if (keylen > password_len) {
5263		key = emalloc(keylen);
5264		memset(key, 0, keylen);
5265		memcpy(key, password, password_len);
5266	} else {
5267		key = (unsigned char*)password;
5268	}
5269
5270	max_iv_len = EVP_CIPHER_iv_length(cipher_type);
5271	if (iv_len == 0 && max_iv_len > 0) {
5272		php_error_docref(NULL, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
5273	}
5274	free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len);
5275
5276	outlen = (int)data_len + EVP_CIPHER_block_size(cipher_type);
5277	outbuf = zend_string_alloc(outlen, 0);
5278
5279	EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL);
5280	if (password_len > keylen) {
5281		PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
5282		EVP_CIPHER_CTX_set_key_length(&cipher_ctx, (int)password_len);
5283	}
5284	EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
5285	if (options & OPENSSL_ZERO_PADDING) {
5286		EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0);
5287	}
5288	if (data_len > 0) {
5289		EVP_EncryptUpdate(&cipher_ctx, (unsigned char*)ZSTR_VAL(outbuf), &i, (unsigned char *)data, (int)data_len);
5290	}
5291	outlen = i;
5292	if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + i, &i)) {
5293		outlen += i;
5294		if (options & OPENSSL_RAW_DATA) {
5295			ZSTR_VAL(outbuf)[outlen] = '\0';
5296			ZSTR_LEN(outbuf) = outlen;
5297			RETVAL_STR(outbuf);
5298		} else {
5299			zend_string *base64_str;
5300
5301			base64_str = php_base64_encode((unsigned char*)ZSTR_VAL(outbuf), outlen);
5302			zend_string_release(outbuf);
5303			RETVAL_STR(base64_str);
5304		}
5305	} else {
5306		zend_string_release(outbuf);
5307		RETVAL_FALSE;
5308	}
5309	if (key != (unsigned char*)password) {
5310		efree(key);
5311	}
5312	if (free_iv) {
5313		efree(iv);
5314	}
5315	EVP_CIPHER_CTX_cleanup(&cipher_ctx);
5316}
5317/* }}} */
5318
5319/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = '']])
5320   Takes raw or base64 encoded string and decrypts it using given method and key */
5321PHP_FUNCTION(openssl_decrypt)
5322{
5323	zend_long options = 0;
5324	char *data, *method, *password, *iv = "";
5325	size_t data_len, method_len, password_len, iv_len = 0;
5326	const EVP_CIPHER *cipher_type;
5327	EVP_CIPHER_CTX cipher_ctx;
5328	int i, outlen, keylen;
5329	zend_string *outbuf;
5330	unsigned char *key;
5331	zend_string *base64_str = NULL;
5332	zend_bool free_iv;
5333
5334	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) {
5335		return;
5336	}
5337
5338	if (!method_len) {
5339		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
5340		RETURN_FALSE;
5341	}
5342
5343	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data);
5344
5345	cipher_type = EVP_get_cipherbyname(method);
5346	if (!cipher_type) {
5347		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
5348		RETURN_FALSE;
5349	}
5350
5351	if (!(options & OPENSSL_RAW_DATA)) {
5352		base64_str = php_base64_decode((unsigned char*)data, (int)data_len);
5353		if (!base64_str) {
5354			php_error_docref(NULL, E_WARNING, "Failed to base64 decode the input");
5355			RETURN_FALSE;
5356		}
5357		data_len = ZSTR_LEN(base64_str);
5358		data = ZSTR_VAL(base64_str);
5359	}
5360
5361	keylen = EVP_CIPHER_key_length(cipher_type);
5362	if (keylen > password_len) {
5363		key = emalloc(keylen);
5364		memset(key, 0, keylen);
5365		memcpy(key, password, password_len);
5366	} else {
5367		key = (unsigned char*)password;
5368	}
5369
5370	free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type));
5371
5372	outlen = (int)data_len + EVP_CIPHER_block_size(cipher_type);
5373	outbuf = zend_string_alloc(outlen, 0);
5374
5375	EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL);
5376	if (password_len > keylen) {
5377		PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password);
5378		EVP_CIPHER_CTX_set_key_length(&cipher_ctx, (int)password_len);
5379	}
5380	EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
5381	if (options & OPENSSL_ZERO_PADDING) {
5382		EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0);
5383	}
5384	EVP_DecryptUpdate(&cipher_ctx, (unsigned char*)ZSTR_VAL(outbuf), &i, (unsigned char *)data, (int)data_len);
5385	outlen = i;
5386	if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + i, &i)) {
5387		outlen += i;
5388		ZSTR_VAL(outbuf)[outlen] = '\0';
5389		ZSTR_LEN(outbuf) = outlen;
5390		RETVAL_STR(outbuf);
5391	} else {
5392		zend_string_release(outbuf);
5393		RETVAL_FALSE;
5394	}
5395	if (key != (unsigned char*)password) {
5396		efree(key);
5397	}
5398	if (free_iv) {
5399		efree(iv);
5400	}
5401	if (base64_str) {
5402		zend_string_release(base64_str);
5403	}
5404 	EVP_CIPHER_CTX_cleanup(&cipher_ctx);
5405}
5406/* }}} */
5407
5408/* {{{ proto int openssl_cipher_iv_length(string $method) */
5409PHP_FUNCTION(openssl_cipher_iv_length)
5410{
5411	char *method;
5412	size_t method_len;
5413	const EVP_CIPHER *cipher_type;
5414
5415	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &method, &method_len) == FAILURE) {
5416		return;
5417	}
5418
5419	if (!method_len) {
5420		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
5421		RETURN_FALSE;
5422	}
5423
5424	cipher_type = EVP_get_cipherbyname(method);
5425	if (!cipher_type) {
5426		php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
5427		RETURN_FALSE;
5428	}
5429
5430	RETURN_LONG(EVP_CIPHER_iv_length(cipher_type));
5431}
5432/* }}} */
5433
5434
5435/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key)
5436   Computes shared secret for public value of remote DH key and local DH key */
5437PHP_FUNCTION(openssl_dh_compute_key)
5438{
5439	zval *key;
5440	char *pub_str;
5441	size_t pub_len;
5442	EVP_PKEY *pkey;
5443	BIGNUM *pub;
5444	zend_string *data;
5445	int len;
5446
5447	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sr", &pub_str, &pub_len, &key) == FAILURE) {
5448		return;
5449	}
5450	if ((pkey = (EVP_PKEY *)zend_fetch_resource(Z_RES_P(key), "OpenSSL key", le_key)) == NULL) {
5451		RETURN_FALSE;
5452	}
5453	if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DH || !pkey->pkey.dh) {
5454		RETURN_FALSE;
5455	}
5456
5457	PHP_OPENSSL_CHECK_SIZE_T_TO_INT(pub_len, pub_key);
5458	pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
5459
5460	data = zend_string_alloc(DH_size(pkey->pkey.dh), 0);
5461	len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, pkey->pkey.dh);
5462
5463	if (len >= 0) {
5464		ZSTR_LEN(data) = len;
5465		ZSTR_VAL(data)[len] = 0;
5466		RETVAL_STR(data);
5467	} else {
5468		zend_string_release(data);
5469		RETVAL_FALSE;
5470	}
5471
5472	BN_free(pub);
5473}
5474/* }}} */
5475
5476/* {{{ proto string openssl_random_pseudo_bytes(integer length [, &bool returned_strong_result])
5477   Returns a string of the length specified filled with random pseudo bytes */
5478PHP_FUNCTION(openssl_random_pseudo_bytes)
5479{
5480	zend_long buffer_length;
5481	zend_string *buffer = NULL;
5482	zval *zstrong_result_returned = NULL;
5483
5484	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z/", &buffer_length, &zstrong_result_returned) == FAILURE) {
5485		return;
5486	}
5487
5488	if (buffer_length <= 0) {
5489		RETURN_FALSE;
5490	}
5491
5492	if (zstrong_result_returned) {
5493		zval_dtor(zstrong_result_returned);
5494		ZVAL_FALSE(zstrong_result_returned);
5495	}
5496
5497	buffer = zend_string_alloc(buffer_length, 0);
5498
5499#ifdef PHP_WIN32
5500	/* random/urandom equivalent on Windows */
5501	if (php_win32_get_random_bytes((unsigned char*)buffer->val, (size_t) buffer_length) == FAILURE){
5502		zend_string_release(buffer);
5503		if (zstrong_result_returned) {
5504			ZVAL_FALSE(zstrong_result_returned);
5505		}
5506		RETURN_FALSE;
5507	}
5508#else
5509
5510	PHP_OPENSSL_CHECK_LONG_TO_INT(buffer_length, length);
5511
5512	if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), (int)buffer_length) <= 0) {
5513		zend_string_release(buffer);
5514		if (zstrong_result_returned) {
5515			ZVAL_FALSE(zstrong_result_returned);
5516		}
5517		RETURN_FALSE;
5518	}
5519#endif
5520
5521	ZSTR_VAL(buffer)[buffer_length] = 0;
5522	RETVAL_STR(buffer);
5523
5524	if (zstrong_result_returned) {
5525		ZVAL_BOOL(zstrong_result_returned, 1);
5526	}
5527}
5528/* }}} */
5529
5530/*
5531 * Local variables:
5532 * tab-width: 8
5533 * c-basic-offset: 8
5534 * End:
5535 * vim600: sw=4 ts=4 fdm=marker
5536 * vim<600: sw=4 ts=4
5537 */
5538
5539