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