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