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