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