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